From fec95dd41443917090418ce0081d3bb55d50920d Mon Sep 17 00:00:00 2001 From: CXUtk <2237367067@qq.com> Date: Thu, 29 Jun 2023 22:08:32 -0700 Subject: [PATCH 01/13] initial upload --- Localization/en-US_Mods.Everglow.hjson | 3058 +++++++++++++++++ Sources/Everglow.Core/Everglow.Core.csproj | 11 + .../Physics/PBEngine/Collision/AABB.cs | 20 + .../Collision/BroadPhase/BruteForceDetect.cs | 115 + .../Collision/BroadPhase/CollisionGraph.cs | 65 + .../Collision/BroadPhase/IBroadPhase.cs | 24 + .../Collision/BroadPhase/SweepAndPrune.cs | 143 + .../Collision/Colliders/BoxCollider.cs | 489 +++ .../Collision/Colliders/Collider2D.cs | 37 + .../Collision/Colliders/SphereCollider.cs | 201 ++ .../PBEngine/Collision/CollisionEvent2D.cs | 65 + .../PBEngine/Collision/CollisionInfo.cs | 20 + .../Physics/PBEngine/Collision/Edge2D.cs | 66 + .../PBEngine/Collision/ImpluseEntry.cs | 18 + .../PBEngine/Collision/MovementType.cs | 31 + .../Physics/PBEngine/Constrains/Constrain.cs | 15 + .../PBEngine/Constrains/JointConstrain.cs | 63 + .../PBEngine/Constrains/SpringConstrain.cs | 96 + .../PBEngine/GameInteraction/TileCollider.cs | 55 + .../GameInteraction/TileCollisionUtils.cs | 300 ++ .../Physics/PBEngine/GeometryUtils.cs | 401 +++ .../Physics/PBEngine/PhysicsObject.cs | 162 + .../Physics/PBEngine/PhysicsSimulation.cs | 229 ++ .../PlayGround/Constrain/JointPlayground.cs | 229 ++ .../PlayGround/Contact/ContactPlayGround1.cs | 308 ++ .../PlayGround/Contact/ContactPlayGround2.cs | 110 + .../Physics/PBEngine/RigidBody2D.cs | 584 ++++ .../Physics/PBEngine/Shaders/Default.fx | 43 + Sources/Everglow.Core/Utilities/Matrix2x2.cs | 210 ++ Sources/Everglow.Core/Utilities/Matrix3x3.cs | 217 ++ Sources/Everglow.Core/Utilities/Utils.md | 8 +- Sources/Modules/Example/RigidBodyDisplay.cs | 97 + 32 files changed, 7489 insertions(+), 1 deletion(-) create mode 100644 Localization/en-US_Mods.Everglow.hjson create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionInfo.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Edge2D.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/ImpluseEntry.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/MovementType.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Shaders/Default.fx create mode 100644 Sources/Everglow.Core/Utilities/Matrix2x2.cs create mode 100644 Sources/Everglow.Core/Utilities/Matrix3x3.cs create mode 100644 Sources/Modules/Example/RigidBodyDisplay.cs diff --git a/Localization/en-US_Mods.Everglow.hjson b/Localization/en-US_Mods.Everglow.hjson new file mode 100644 index 000000000..efc8c0216 --- /dev/null +++ b/Localization/en-US_Mods.Everglow.hjson @@ -0,0 +1,3058 @@ +Tiles: { + BottleOfNutShellFlowers.MapEntry: Bottle Of Nut Shell Flowers + PlumBlossomInABowl.MapEntry: Plum Blossom In A Bowl + WhiteLotusBonsai.MapEntry: White Lotus Bonsai + BloodifyPlatform.MapEntry: Bloodify Platform + BloodyMossWheel.MapEntry: Bloody Moss Wheel + BloodyMossWheelFinished.MapEntry: Bloody Moss Wheel Finished + StoneTusk.MapEntry: Stone Tusk + StoneTuskSmall.MapEntry: Stone Tusk Small + StrangeTuskStone.MapEntry: Strange Tusk Stone + StrangeTuskStone1.MapEntry: Strange Tusk Stone1 + StrangeTuskStone2.MapEntry: Strange Tusk Stone2 + StrangeTuskStone3.MapEntry: Strange Tusk Stone3 + StrangeTuskStone4.MapEntry: Strange Tusk Stone4 + TuskRelic.MapEntry: Tusk Relic + BlackFren.MapEntry: Black Fren + BlackFrenLarge.MapEntry: Black Fren Large + BlackStarShrub.MapEntry: Black Star Shrub + BlackStarShrubSmall.MapEntry: Black Star Shrub Small + BlackVine.MapEntry: Black Vine + BlueBlossom.MapEntry: Blue Blossom + CocoonRock.MapEntry: Cocoon Rock + GlowingReed.MapEntry: Glowing Reed + LampLotus.MapEntry: Lamp Lotus + MothWorldDoor.MapEntry: Moth World Door + CyanVineBar.MapEntry: Cyan Vine Bar + CyanVineOreTile.MapEntry: Cyan Vine Ore Tile + CyanVineStone.MapEntry: Cyan Vine Stone + LiftLamp.MapEntry: Lift Lamp +} + +Biomes: { + TuskBiome: { + DisplayName: Tusk Biome + TownNPCDialogueName: the Tusk Biome + } + + TuskSurfaceBiome: { + DisplayName: Tusk Surface Biome + TownNPCDialogueName: the Tusk Surface Biome + } + + FireflyBiome: { + DisplayName: Firefly Biome + TownNPCDialogueName: the Firefly Biome + } + + YggdrasilTownBiome: { + DisplayName: Yggdrasil Town Biome + TownNPCDialogueName: the Yggdrasil Town Biome + } + + KelpCurtainBiome: { + DisplayName: Kelp Curtain Biome + TownNPCDialogueName: the Kelp Curtain Biome + } +} + +MapEntry.FluorescentTree: Mods.Everglow.MapEntry.FluorescentTree + +Buffs: { + BloodSwordStrikeBuff: { + DisplayName: Blood Sword Strike Buff + Description: Mods.Everglow.Buffs.BloodSwordStrikeBuff.Description + } + + LifeRotten: { + DisplayName: Life Rotten + Description: Mods.Everglow.Buffs.LifeRotten.Description + } + + OnElectric: { + DisplayName: On Electric + Description: Mods.Everglow.Buffs.OnElectric.Description + } + + StarCrack: { + DisplayName: Star Crack + Description: Mods.Everglow.Buffs.StarCrack.Description + } + + AppleBuff: { + DisplayName: Apple Buff + Description: Mods.Everglow.Buffs.AppleBuff.Description + } + + ApplePieBuff: { + DisplayName: Apple Pie Buff + Description: Mods.Everglow.Buffs.ApplePieBuff.Description + } + + ApricotBuff: { + DisplayName: Apricot Buff + Description: Mods.Everglow.Buffs.ApricotBuff.Description + } + + BaconBuff: { + DisplayName: Bacon Buff + Description: Mods.Everglow.Buffs.BaconBuff.Description + } + + BananaBuff: { + DisplayName: Banana Buff + Description: Mods.Everglow.Buffs.BananaBuff.Description + } + + BananaSplitBuff: { + DisplayName: Banana Split Buff + Description: Mods.Everglow.Buffs.BananaSplitBuff.Description + } + + BBQRibsBuff: { + DisplayName: B B Q Ribs Buff + Description: Mods.Everglow.Buffs.BBQRibsBuff.Description + } + + BlackCurrantBuff: { + DisplayName: Black Currant Buff + Description: Mods.Everglow.Buffs.BlackCurrantBuff.Description + } + + BloodOrangeBuff: { + DisplayName: Blood Orange Buff + Description: Mods.Everglow.Buffs.BloodOrangeBuff.Description + } + + BowlofSoupBuff: { + DisplayName: Bowlof Soup Buff + Description: Mods.Everglow.Buffs.BowlofSoupBuff.Description + } + + BunnyStewBuff: { + DisplayName: Bunny Stew Buff + Description: Mods.Everglow.Buffs.BunnyStewBuff.Description + } + + BurgerBuff: { + DisplayName: Burger Buff + Description: Mods.Everglow.Buffs.BurgerBuff.Description + } + + CherryBuff: { + DisplayName: Cherry Buff + Description: Mods.Everglow.Buffs.CherryBuff.Description + } + + ChickenNuggetBuff: { + DisplayName: Chicken Nugget Buff + Description: Mods.Everglow.Buffs.ChickenNuggetBuff.Description + } + + ChocolateChipCookieBuff: { + DisplayName: Chocolate Chip Cookie Buff + Description: Mods.Everglow.Buffs.ChocolateChipCookieBuff.Description + } + + ChristmasPuddingBuff: { + DisplayName: Christmas Pudding Buff + Description: Mods.Everglow.Buffs.ChristmasPuddingBuff.Description + } + + CoconutBuff: { + DisplayName: Coconut Buff + Description: Mods.Everglow.Buffs.CoconutBuff.Description + } + + CookedFishBuff: { + DisplayName: Cooked Fish Buff + Description: Mods.Everglow.Buffs.CookedFishBuff.Description + } + + CookedMarshmallowBuff: { + DisplayName: Cooked Marshmallow Buff + Description: Mods.Everglow.Buffs.CookedMarshmallowBuff.Description + } + + CookedShrimpBuff: { + DisplayName: Cooked Shrimp Buff + Description: Mods.Everglow.Buffs.CookedShrimpBuff.Description + } + + DragonfruitBuff: { + DisplayName: Dragonfruit Buff + Description: Mods.Everglow.Buffs.DragonfruitBuff.Description + } + + ElderberryBuff: { + DisplayName: Elderberry Buff + Description: Mods.Everglow.Buffs.ElderberryBuff.Description + } + + EscargotBuff: { + DisplayName: Escargot Buff + Description: Mods.Everglow.Buffs.EscargotBuff.Description + } + + FriedEggBuff: { + DisplayName: Fried Egg Buff + Description: Mods.Everglow.Buffs.FriedEggBuff.Description + } + + FriesBuff: { + DisplayName: Fries Buff + Description: Mods.Everglow.Buffs.FriesBuff.Description + } + + FroggleBunwichBuff: { + DisplayName: Froggle Bunwich Buff + Description: Mods.Everglow.Buffs.FroggleBunwichBuff.Description + } + + FruitSaladBuff: { + DisplayName: Fruit Salad Buff + Description: Mods.Everglow.Buffs.FruitSaladBuff.Description + } + + GingerbreadCookieBuff: { + DisplayName: Gingerbread Cookie Buff + Description: Mods.Everglow.Buffs.GingerbreadCookieBuff.Description + } + + GoldenDelightBuff: { + DisplayName: Golden Delight Buff + Description: Mods.Everglow.Buffs.GoldenDelightBuff.Description + } + + GrapefruitBuff: { + DisplayName: Grapefruit Buff + Description: Mods.Everglow.Buffs.GrapefruitBuff.Description + } + + GrapesBuff: { + DisplayName: Grapes Buff + Description: Mods.Everglow.Buffs.GrapesBuff.Description + } + + GrilledSquirrelBuff: { + DisplayName: Grilled Squirrel Buff + Description: Mods.Everglow.Buffs.GrilledSquirrelBuff.Description + } + + GrubSoupBuff: { + DisplayName: Grub Soup Buff + Description: Mods.Everglow.Buffs.GrubSoupBuff.Description + } + + HotdogBuff: { + DisplayName: Hotdog Buff + Description: Mods.Everglow.Buffs.HotdogBuff.Description + } + + IceCreamBuff: { + DisplayName: Ice Cream Buff + Description: Mods.Everglow.Buffs.IceCreamBuff.Description + } + + LemonBuff: { + DisplayName: Lemon Buff + Description: Mods.Everglow.Buffs.LemonBuff.Description + } + + LobsterTailBuff: { + DisplayName: Lobster Tail Buff + Description: Mods.Everglow.Buffs.LobsterTailBuff.Description + } + + MangoBuff: { + DisplayName: Mango Buff + Description: Mods.Everglow.Buffs.MangoBuff.Description + } + + MarshmallowBuff: { + DisplayName: Marshmallow Buff + Description: Mods.Everglow.Buffs.MarshmallowBuff.Description + } + + MonsterLasagnaBuff: { + DisplayName: Monster Lasagna Buff + Description: Mods.Everglow.Buffs.MonsterLasagnaBuff.Description + } + + NachosBuff: { + DisplayName: Nachos Buff + Description: Mods.Everglow.Buffs.NachosBuff.Description + } + + PadThaiBuff: { + DisplayName: Pad Thai Buff + Description: Mods.Everglow.Buffs.PadThaiBuff.Description + } + + PeachBuff: { + DisplayName: Peach Buff + Description: Mods.Everglow.Buffs.PeachBuff.Description + } + + PhoBuff: { + DisplayName: Pho Buff + Description: Mods.Everglow.Buffs.PhoBuff.Description + } + + PineappleBuff: { + DisplayName: Pineapple Buff + Description: Mods.Everglow.Buffs.PineappleBuff.Description + } + + PizzaBuff: { + DisplayName: Pizza Buff + Description: Mods.Everglow.Buffs.PizzaBuff.Description + } + + PlumBuff: { + DisplayName: Plum Buff + Description: Mods.Everglow.Buffs.PlumBuff.Description + } + + PotatoChipsBuff: { + DisplayName: Potato Chips Buff + Description: Mods.Everglow.Buffs.PotatoChipsBuff.Description + } + + PumpkinPieBuff: { + DisplayName: Pumpkin Pie Buff + Description: Mods.Everglow.Buffs.PumpkinPieBuff.Description + } + + RambutanBuff: { + DisplayName: Rambutan Buff + Description: Mods.Everglow.Buffs.RambutanBuff.Description + } + + RoastedBirdBuff: { + DisplayName: Roasted Bird Buff + Description: Mods.Everglow.Buffs.RoastedBirdBuff.Description + } + + RoastedDuckBuff: { + DisplayName: Roasted Duck Buff + Description: Mods.Everglow.Buffs.RoastedDuckBuff.Description + } + + SashimiBuff: { + DisplayName: Sashimi Buff + Description: Mods.Everglow.Buffs.SashimiBuff.Description + } + + SauteedFrogLegsBuff: { + DisplayName: Sauteed Frog Legs Buff + Description: Mods.Everglow.Buffs.SauteedFrogLegsBuff.Description + } + + SeafoodDinnerBuff: { + DisplayName: Seafood Dinner Buff + Description: Mods.Everglow.Buffs.SeafoodDinnerBuff.Description + } + + ShrimpPoBoyBuff: { + DisplayName: Shrimp Po Boy Buff + Description: Mods.Everglow.Buffs.ShrimpPoBoyBuff.Description + } + + ShuckedOysterBuff: { + DisplayName: Shucked Oyster Buff + Description: Mods.Everglow.Buffs.ShuckedOysterBuff.Description + } + + SpaghettiBuff: { + DisplayName: Spaghetti Buff + Description: Mods.Everglow.Buffs.SpaghettiBuff.Description + } + + StarfruitBuff: { + DisplayName: Starfruit Buff + Description: Mods.Everglow.Buffs.StarfruitBuff.Description + } + + SteakBuff: { + DisplayName: Steak Buff + Description: Mods.Everglow.Buffs.SteakBuff.Description + } + + SugarCookieBuff: { + DisplayName: Sugar Cookie Buff + Description: Mods.Everglow.Buffs.SugarCookieBuff.Description + } + + AleBuff: { + DisplayName: Ale Buff + Description: Mods.Everglow.Buffs.AleBuff.Description + } + + AppleJuiceBuff: { + DisplayName: Apple Juice Buff + Description: Mods.Everglow.Buffs.AppleJuiceBuff.Description + } + + BananaDaiquiriBuff: { + DisplayName: Banana Daiquiri Buff + Description: Mods.Everglow.Buffs.BananaDaiquiriBuff.Description + } + + BloodyMoscatoBuff: { + DisplayName: Bloody Moscato Buff + Description: Mods.Everglow.Buffs.BloodyMoscatoBuff.Description + } + + CoffeeCupBuff: { + DisplayName: Coffee Cup Buff + Description: Mods.Everglow.Buffs.CoffeeCupBuff.Description + } + + CreamSodaBuff: { + DisplayName: Cream Soda Buff + Description: Mods.Everglow.Buffs.CreamSodaBuff.Description + } + + FruitJuiceBuff: { + DisplayName: Fruit Juice Buff + Description: Mods.Everglow.Buffs.FruitJuiceBuff.Description + } + + GrapeJuiceBuff: { + DisplayName: Grape Juice Buff + Description: Mods.Everglow.Buffs.GrapeJuiceBuff.Description + } + + LemonadeBuff: { + DisplayName: Lemonade Buff + Description: Mods.Everglow.Buffs.LemonadeBuff.Description + } + + MilkCartonBuff: { + DisplayName: Milk Carton Buff + Description: Mods.Everglow.Buffs.MilkCartonBuff.Description + } + + MilkshakeBuff: { + DisplayName: Milkshake Buff + Description: Mods.Everglow.Buffs.MilkshakeBuff.Description + } + + PeachSangriaBuff: { + DisplayName: Peach Sangria Buff + Description: Mods.Everglow.Buffs.PeachSangriaBuff.Description + } + + PinaColadaBuff: { + DisplayName: Pina Colada Buff + Description: Mods.Everglow.Buffs.PinaColadaBuff.Description + } + + PrismaticPunchBuff: { + DisplayName: Prismatic Punch Buff + Description: Mods.Everglow.Buffs.PrismaticPunchBuff.Description + } + + SakeBuff: { + DisplayName: Sake Buff + Description: Mods.Everglow.Buffs.SakeBuff.Description + } + + SmoothieofDarknessBuff: { + DisplayName: Smoothieof Darkness Buff + Description: Mods.Everglow.Buffs.SmoothieofDarknessBuff.Description + } + + TeacupBuff: { + DisplayName: Teacup Buff + Description: Mods.Everglow.Buffs.TeacupBuff.Description + } + + TropicalSmoothieBuff: { + DisplayName: Tropical Smoothie Buff + Description: Mods.Everglow.Buffs.TropicalSmoothieBuff.Description + } + + ArcticshellfishWarshipBuff: { + DisplayName: Arcticshellfish Warship Buff + Description: Mods.Everglow.Buffs.ArcticshellfishWarshipBuff.Description + } + + BakedMusselBuff: { + DisplayName: Baked Mussel Buff + Description: Mods.Everglow.Buffs.BakedMusselBuff.Description + } + + BakedOysterBuff: { + DisplayName: Baked Oyster Buff + Description: Mods.Everglow.Buffs.BakedOysterBuff.Description + } + + CantaloupeJellyBuff: { + DisplayName: Cantaloupe Jelly Buff + Description: Mods.Everglow.Buffs.CantaloupeJellyBuff.Description + } + + CaramelPuddingBuff: { + DisplayName: Caramel Pudding Buff + Description: Mods.Everglow.Buffs.CaramelPuddingBuff.Description + } + + CrabseedWarshipBuff: { + DisplayName: Crabseed Warship Buff + Description: Mods.Everglow.Buffs.CrabseedWarshipBuff.Description + } + + DurianBuff: { + DisplayName: Durian Buff + Description: Mods.Everglow.Buffs.DurianBuff.Description + } + + EggTartBuff: { + DisplayName: Egg Tart Buff + Description: Mods.Everglow.Buffs.EggTartBuff.Description + } + + KiwiFruitBuff: { + DisplayName: Kiwi Fruit Buff + Description: Mods.Everglow.Buffs.KiwiFruitBuff.Description + } + + KiwiIceCreamBuff: { + DisplayName: Kiwi Ice Cream Buff + Description: Mods.Everglow.Buffs.KiwiIceCreamBuff.Description + } + + MangosteenBuff: { + DisplayName: Mangosteen Buff + Description: Mods.Everglow.Buffs.MangosteenBuff.Description + } + + OrangeIcecreamBuff: { + DisplayName: Orange Icecream Buff + Description: Mods.Everglow.Buffs.OrangeIcecreamBuff.Description + } + + SalmonInPepperBuff: { + DisplayName: Salmon In Pepper Buff + Description: Mods.Everglow.Buffs.SalmonInPepperBuff.Description + } + + SalmonWarshipBuff: { + DisplayName: Salmon Warship Buff + Description: Mods.Everglow.Buffs.SalmonWarshipBuff.Description + } + + SeafoodPizzaBuff: { + DisplayName: Seafood Pizza Buff + Description: Mods.Everglow.Buffs.SeafoodPizzaBuff.Description + } + + StinkyTofuBuff: { + DisplayName: Stinky Tofu Buff + Description: Mods.Everglow.Buffs.StinkyTofuBuff.Description + } + + StrawberryBuff: { + DisplayName: Strawberry Buff + Description: Mods.Everglow.Buffs.StrawberryBuff.Description + } + + StrawberryIcecreamBuff: { + DisplayName: Strawberry Icecream Buff + Description: Mods.Everglow.Buffs.StrawberryIcecreamBuff.Description + } + + SweetshrimpSushiBuff: { + DisplayName: Sweetshrimp Sushi Buff + Description: Mods.Everglow.Buffs.SweetshrimpSushiBuff.Description + } + + TamakoSushiBuff: { + DisplayName: Tamako Sushi Buff + Description: Mods.Everglow.Buffs.TamakoSushiBuff.Description + } + + WakameWarshipBuff: { + DisplayName: Wakame Warship Buff + Description: Mods.Everglow.Buffs.WakameWarshipBuff.Description + } + + WatermelonBuff: { + DisplayName: Watermelon Buff + Description: Mods.Everglow.Buffs.WatermelonBuff.Description + } + + WatermelonPlateBuff: { + DisplayName: Watermelon Plate Buff + Description: Mods.Everglow.Buffs.WatermelonPlateBuff.Description + } + + BlueHawaiiBuff: { + DisplayName: Blue Hawaii Buff + Description: Mods.Everglow.Buffs.BlueHawaiiBuff.Description + } + + B_52Buff: { + DisplayName: B_52 Buff + Description: Mods.Everglow.Buffs.B_52Buff.Description + } + + DreamYearningBuff: { + DisplayName: Dream Yearning Buff + Description: Mods.Everglow.Buffs.DreamYearningBuff.Description + } + + DryMartiniBuff: { + DisplayName: Dry Martini Buff + Description: Mods.Everglow.Buffs.DryMartiniBuff.Description + } + + GreenStormBuff: { + DisplayName: Green Storm Buff + Description: Mods.Everglow.Buffs.GreenStormBuff.Description + } + + KiwiJuiceBuff: { + DisplayName: Kiwi Juice Buff + Description: Mods.Everglow.Buffs.KiwiJuiceBuff.Description + } + + LonelyJellyfishBuff: { + DisplayName: Lonely Jellyfish Buff + Description: Mods.Everglow.Buffs.LonelyJellyfishBuff.Description + } + + OrangeJuiceBuff: { + DisplayName: Orange Juice Buff + Description: Mods.Everglow.Buffs.OrangeJuiceBuff.Description + } + + PiercoldWindBuff: { + DisplayName: Piercold Wind Buff + Description: Mods.Everglow.Buffs.PiercoldWindBuff.Description + } + + PurpleHooterBuff: { + DisplayName: Purple Hooter Buff + Description: Mods.Everglow.Buffs.PurpleHooterBuff.Description + } + + QuinceMarryBuff: { + DisplayName: Quince Marry Buff + Description: Mods.Everglow.Buffs.QuinceMarryBuff.Description + } + + RedWineBuff: { + DisplayName: Red Wine Buff + Description: Mods.Everglow.Buffs.RedWineBuff.Description + } + + SunriseBuff: { + DisplayName: Sunrise Buff + Description: Mods.Everglow.Buffs.SunriseBuff.Description + } + + TricolourBuff: { + DisplayName: Tricolour Buff + Description: Mods.Everglow.Buffs.TricolourBuff.Description + } + + WaterMelonJuiceBuff: { + DisplayName: Water Melon Juice Buff + Description: Mods.Everglow.Buffs.WaterMelonJuiceBuff.Description + } + + TuskStaff: { + DisplayName: Tusk Staff + Description: Mods.Everglow.Buffs.TuskStaff.Description + } + + GlowMothBuff: { + DisplayName: Glow Moth Buff + Description: Mods.Everglow.Buffs.GlowMothBuff.Description + } + + OnMoth: { + DisplayName: On Moth + Description: Mods.Everglow.Buffs.OnMoth.Description + } + + Freeze: { + DisplayName: Freeze + Description: Mods.Everglow.Buffs.Freeze.Description + } + + LaserWormBuff: { + DisplayName: Laser Worm Buff + Description: Mods.Everglow.Buffs.LaserWormBuff.Description + } + + ShadowSupervisor: { + DisplayName: Shadow Supervisor + Description: Mods.Everglow.Buffs.ShadowSupervisor.Description + } + + MoonAndFragrans: { + DisplayName: Moon And Fragrans + Description: Mods.Everglow.Buffs.MoonAndFragrans.Description + } + + MoonAndFragransII: { + DisplayName: Moon And Fragrans I I + Description: Mods.Everglow.Buffs.MoonAndFragransII.Description + } + + MoonAndFragransIII: { + DisplayName: Moon And Fragrans I I I + Description: Mods.Everglow.Buffs.MoonAndFragransIII.Description + } + + WaterBoltII: { + DisplayName: Water Bolt I I + Description: Mods.Everglow.Buffs.WaterBoltII.Description + } + + BlueImmune: { + DisplayName: Blue Immune + Description: Mods.Everglow.Buffs.BlueImmune.Description + } + + BrownImmune: { + DisplayName: Brown Immune + Description: Mods.Everglow.Buffs.BrownImmune.Description + } + + GreenImmune: { + DisplayName: Green Immune + Description: Mods.Everglow.Buffs.GreenImmune.Description + } + + PinkImmune: { + DisplayName: Pink Immune + Description: Mods.Everglow.Buffs.PinkImmune.Description + } + + PurpleImmune: { + DisplayName: Purple Immune + Description: Mods.Everglow.Buffs.PurpleImmune.Description + } + + RedImmune: { + DisplayName: Red Immune + Description: Mods.Everglow.Buffs.RedImmune.Description + } + + WhiteImmune: { + DisplayName: White Immune + Description: Mods.Everglow.Buffs.WhiteImmune.Description + } + + YellowImmune: { + DisplayName: Yellow Immune + Description: Mods.Everglow.Buffs.YellowImmune.Description + } + + CactusBallBuff: { + DisplayName: Cactus Ball Buff + Description: Mods.Everglow.Buffs.CactusBallBuff.Description + } + + DeathFlame: { + DisplayName: Death Flame + Description: Mods.Everglow.Buffs.DeathFlame.Description + } +} + +Items: { + BottleOfNutShellFlowers: { + DisplayName: Bottle Of Nut Shell Flowers + Tooltip: "" + } + + PlumBlossomInABowl: { + DisplayName: Plum Blossom In A Bowl + Tooltip: "" + } + + WhiteLotusBonsai: { + DisplayName: White Lotus Bonsai + Tooltip: "" + } + + BloodGoldBayonet: { + DisplayName: Blood Gold Bayonet + Tooltip: "" + } + + BlueRibbonBayonet: { + DisplayName: Blue Ribbon Bayonet + Tooltip: "" + } + + CopperBayonet: { + DisplayName: Copper Bayonet + Tooltip: "" + } + + DreamStar: { + DisplayName: Dream Star + Tooltip: "" + } + + EnchantedBayonet: { + DisplayName: Enchanted Bayonet + Tooltip: "" + } + + EternalNight: { + DisplayName: Eternal Night + Tooltip: "" + } + + GoldenStabbingSword: { + DisplayName: Golden Stabbing Sword + Tooltip: "" + } + + HolyBayonet: { + DisplayName: Holy Bayonet + Tooltip: "" + } + + IronStabbingSword: { + DisplayName: Iron Stabbing Sword + Tooltip: "" + } + + LeadStabbingSword: { + DisplayName: Lead Stabbing Sword + Tooltip: "" + } + + PlatinumStabbingSword: { + DisplayName: Platinum Stabbing Sword + Tooltip: "" + } + + PrisonFireBayonet: { + DisplayName: Prison Fire Bayonet + Tooltip: "" + } + + RedRibbonBayonet: { + DisplayName: Red Ribbon Bayonet + Tooltip: "" + } + + RottenGoldBayonet: { + DisplayName: Rotten Gold Bayonet + Tooltip: "" + } + + SilverStabbingSword: { + DisplayName: Silver Stabbing Sword + Tooltip: "" + } + + TinStabbingSword: { + DisplayName: Tin Stabbing Sword + Tooltip: "" + } + + TungstenStabbingSword: { + DisplayName: Tungsten Stabbing Sword + Tooltip: "" + } + + VegetationBayonet: { + DisplayName: Vegetation Bayonet + Tooltip: "" + } + + YoenLeZed: { + DisplayName: Yoen Le Zed + Tooltip: "" + } + + StarSeed: { + DisplayName: Star Seed + Tooltip: "" + } + + FryingPanItem: { + DisplayName: Frying Pan Item + Tooltip: "" + } + + BloodGlucoseMonitor: { + DisplayName: Blood Glucose Monitor + Tooltip: "" + } + + OsmoticPressureMonitor: { + DisplayName: Osmotic Pressure Monitor + Tooltip: "" + } + + ArcticshellfishWarship: { + DisplayName: Arcticshellfish Warship + Tooltip: "" + } + + BakedMussel: { + DisplayName: Baked Mussel + Tooltip: "" + } + + BakedOyster: { + DisplayName: Baked Oyster + Tooltip: "" + } + + CantaloupeJelly: { + DisplayName: Cantaloupe Jelly + Tooltip: "" + } + + CaramelPudding: { + DisplayName: Caramel Pudding + Tooltip: "" + } + + CrabseedWarship: { + DisplayName: Crabseed Warship + Tooltip: "" + } + + Durian: { + DisplayName: Durian + Tooltip: "" + } + + EggTart: { + DisplayName: Egg Tart + Tooltip: "" + } + + KiwiFruit: { + DisplayName: Kiwi Fruit + Tooltip: "" + } + + KiwiIceCream: { + DisplayName: Kiwi Ice Cream + Tooltip: "" + } + + Mangosteen: { + DisplayName: Mangosteen + Tooltip: "" + } + + OrangeIcecream: { + DisplayName: Orange Icecream + Tooltip: "" + } + + SalmonInPepper: { + DisplayName: Salmon In Pepper + Tooltip: "" + } + + SalmonWarship: { + DisplayName: Salmon Warship + Tooltip: "" + } + + SeafoodPizza: { + DisplayName: Seafood Pizza + Tooltip: "" + } + + StinkyTofu: { + DisplayName: Stinky Tofu + Tooltip: "" + } + + Strawberry: { + DisplayName: Strawberry + Tooltip: "" + } + + StrawberryIcecream: { + DisplayName: Strawberry Icecream + Tooltip: "" + } + + SweetshrimpSushi: { + DisplayName: Sweetshrimp Sushi + Tooltip: "" + } + + TamakoSushi: { + DisplayName: Tamako Sushi + Tooltip: "" + } + + WakameWarship: { + DisplayName: Wakame Warship + Tooltip: "" + } + + Watermelon: { + DisplayName: Watermelon + Tooltip: "" + } + + WatermelonPlate: { + DisplayName: Watermelon Plate + Tooltip: "" + } + + BlueHawaii: { + DisplayName: Blue Hawaii + Tooltip: "" + } + + B_52: { + DisplayName: B_52 + Tooltip: "" + } + + DreamYearning: { + DisplayName: Dream Yearning + Tooltip: "" + } + + DryMartini: { + DisplayName: Dry Martini + Tooltip: "" + } + + GreenStorm: { + DisplayName: Green Storm + Tooltip: "" + } + + KiwiJuice: { + DisplayName: Kiwi Juice + Tooltip: "" + } + + LonelyJellyfish: { + DisplayName: Lonely Jellyfish + Tooltip: "" + } + + OrangeJuice: { + DisplayName: Orange Juice + Tooltip: "" + } + + PiercoldWind: { + DisplayName: Piercold Wind + Tooltip: "" + } + + PurpleHooter: { + DisplayName: Purple Hooter + Tooltip: "" + } + + QuinceMarry: { + DisplayName: Quince Marry + Tooltip: "" + } + + RedWine: { + DisplayName: Red Wine + Tooltip: "" + } + + Sunrise: { + DisplayName: Sunrise + Tooltip: "" + } + + Tricolour: { + DisplayName: Tricolour + Tooltip: "" + } + + WaterMelonJuice: { + DisplayName: Water Melon Juice + Tooltip: "" + } + + VFXGenerator: { + DisplayName: V F X Generator + Tooltip: "" + } + + AirTileItem: { + DisplayName: Air Tile Item + Tooltip: "" + } + + AirWallItem: { + DisplayName: Air Wall Item + Tooltip: "" + } + + VortexVanquisherItem: { + DisplayName: Vortex Vanquisher Item + Tooltip: "" + } + + Tester: { + DisplayName: Tester + Tooltip: "" + } + + BloodMossStone: { + DisplayName: Blood Moss Stone + Tooltip: "" + } + + BloodyStoneWall: { + DisplayName: Bloody Stone Wall + Tooltip: "" + } + + StoneTusk: { + DisplayName: Stone Tusk + Tooltip: "" + } + + TuskFlesh: { + DisplayName: Tusk Flesh + Tooltip: "" + } + + TuskStatusI: { + DisplayName: Tusk Status I + Tooltip: "" + } + + TuskStatusII: { + DisplayName: Tusk Status I I + Tooltip: "" + } + + TuskStatusIII: { + DisplayName: Tusk Status I I I + Tooltip: "" + } + + TuskStatusIV: { + DisplayName: Tusk Status I V + Tooltip: "" + } + + BloodyBoneYoyo: { + DisplayName: Bloody Bone Yoyo + Tooltip: "" + } + + SpineGun: { + DisplayName: Spine Gun + Tooltip: "" + } + + ToothBow: { + DisplayName: Tooth Bow + Tooltip: "" + } + + ToothKnife: { + DisplayName: Tooth Knife + Tooltip: "" + } + + ToothMagicBall: { + DisplayName: Tooth Magic Ball + Tooltip: "" + } + + ToothSpear: { + DisplayName: Tooth Spear + Tooltip: "" + } + + ToothStaff: { + DisplayName: Tooth Staff + Tooltip: "" + } + + BloodyTuskTrophy: { + DisplayName: Bloody Tusk Trophy + Tooltip: "" + } + + TuskRelic: { + DisplayName: Tusk Relic + Tooltip: "" + } + + TuskTreasureBag: { + DisplayName: Tusk Treasure Bag + Tooltip: "" + } + + TuskLace: { + DisplayName: Tusk Lace + Tooltip: "" + } + + FireflyPylonItem: { + DisplayName: Firefly Pylon Item + Tooltip: "" + } + + ShabbyPylonItem: { + DisplayName: Shabby Pylon Item + Tooltip: "" + } + + BlackStarShrub: { + DisplayName: Black Star Shrub + Tooltip: "" + } + + DarkCocoon: { + DisplayName: Dark Cocoon + Tooltip: "" + } + + DarkCocoonWall: { + DisplayName: Dark Cocoon Wall + Tooltip: "" + } + + EvilCocoon: { + DisplayName: Evil Cocoon + Tooltip: "" + } + + FireflyCapital: { + DisplayName: Firefly Capital + Tooltip: "" + } + + FireflyMoss: { + DisplayName: Firefly Moss + Tooltip: "" + } + + FireflyPigment: { + DisplayName: Firefly Pigment + Tooltip: "" + } + + GlowCrystal: { + DisplayName: Glow Crystal + Tooltip: "" + } + + GlowingFirefly: { + DisplayName: Glowing Firefly + Tooltip: "" + } + + GlowingPedal: { + DisplayName: Glowing Pedal + Tooltip: "" + } + + GlowMetal: { + DisplayName: Glow Metal + Tooltip: "" + } + + GlowStone: { + DisplayName: Glow Stone + Tooltip: "" + } + + GlowWood: { + DisplayName: Glow Wood + Tooltip: "" + } + + LiveFluorescentLeafWall: { + DisplayName: Live Fluorescent Leaf Wall + Tooltip: "" + } + + LiveFluorescentWoodWall: { + DisplayName: Live Fluorescent Wood Wall + Tooltip: "" + } + + MothScaleDust: { + DisplayName: Moth Scale Dust + Tooltip: "" + } + + DarknessFan: { + DisplayName: Darkness Fan + Tooltip: "" + } + + DreamWeaver: { + DisplayName: Dream Weaver + Tooltip: "" + } + + DustOfCorrupt: { + DisplayName: Dust Of Corrupt + Tooltip: "" + } + + EvilChrysalis: { + DisplayName: Evil Chrysalis + Tooltip: "" + } + + FlowLightMissile: { + DisplayName: Flow Light Missile + Tooltip: "" + } + + GlowBeadGun: { + DisplayName: Glow Bead Gun + Tooltip: "" + } + + GlowWoodSword: { + DisplayName: Glow Wood Sword + Tooltip: "" + } + + MothYoyo: { + DisplayName: Moth Yoyo + Tooltip: "" + } + + NavyThunder: { + DisplayName: Navy Thunder + Tooltip: "" + } + + PhosphorescenceGun: { + DisplayName: Phosphorescence Gun + Tooltip: "" + } + + ScaleWingBlade: { + DisplayName: Scale Wing Blade + Tooltip: "" + } + + ShadowWingBow: { + DisplayName: Shadow Wing Bow + Tooltip: "" + } + + FluorescentLeafStaff: { + DisplayName: Fluorescent Leaf Staff + Tooltip: "" + } + + FluorescentWoodStaff: { + DisplayName: Fluorescent Wood Staff + Tooltip: "" + } + + GlowingDrop: { + DisplayName: Glowing Drop + Tooltip: "" + } + + GlowWoodBed: { + DisplayName: Glow Wood Bed + Tooltip: "" + } + + GlowWoodBookcase: { + DisplayName: Glow Wood Bookcase + Tooltip: "" + } + + GlowWoodBookcaseType2: { + DisplayName: Glow Wood Bookcase Type2 + Tooltip: "" + } + + GlowWoodCampfire: { + DisplayName: Glow Wood Campfire + Tooltip: "" + } + + GlowWoodCandelabra: { + DisplayName: Glow Wood Candelabra + Tooltip: "" + } + + GlowWoodCandle: { + DisplayName: Glow Wood Candle + Tooltip: "" + } + + GlowWoodChair: { + DisplayName: Glow Wood Chair + Tooltip: "" + } + + GlowWoodChandelier: { + DisplayName: Glow Wood Chandelier + Tooltip: "" + } + + GlowWoodChandelierType2: { + DisplayName: Glow Wood Chandelier Type2 + Tooltip: "" + } + + GlowWoodChandelierType3: { + DisplayName: Glow Wood Chandelier Type3 + Tooltip: "" + } + + GlowWoodChandelierType4: { + DisplayName: Glow Wood Chandelier Type4 + Tooltip: "" + } + + GlowWoodChest: { + DisplayName: Glow Wood Chest + Tooltip: "" + } + + GlowWoodClock: { + DisplayName: Glow Wood Clock + Tooltip: "" + } + + GlowWoodDoor: { + DisplayName: Glow Wood Door + Tooltip: "" + } + + GlowWoodDoorType2: { + DisplayName: Glow Wood Door Type2 + Tooltip: "" + } + + GlowWoodDresser: { + DisplayName: Glow Wood Dresser + Tooltip: "" + } + + GlowWoodDresserType2: { + DisplayName: Glow Wood Dresser Type2 + Tooltip: "" + } + + GlowWoodLamp: { + DisplayName: Glow Wood Lamp + Tooltip: "" + } + + GlowWoodLantern: { + DisplayName: Glow Wood Lantern + Tooltip: "" + } + + GlowWoodLanternType2: { + DisplayName: Glow Wood Lantern Type2 + Tooltip: "" + } + + GlowWoodMusicBox: { + DisplayName: Glow Wood Music Box + Tooltip: "" + } + + GlowWoodPiano: { + DisplayName: Glow Wood Piano + Tooltip: "" + } + + GlowWoodPlatform: { + DisplayName: Glow Wood Platform + Tooltip: "" + } + + GlowWoodSink: { + DisplayName: Glow Wood Sink + Tooltip: "" + } + + GlowWoodSinkType2: { + DisplayName: Glow Wood Sink Type2 + Tooltip: "" + } + + GlowWoodSofa: { + DisplayName: Glow Wood Sofa + Tooltip: "" + } + + GlowWoodSofaType2: { + DisplayName: Glow Wood Sofa Type2 + Tooltip: "" + } + + GlowWoodTable: { + DisplayName: Glow Wood Table + Tooltip: "" + } + + GlowWoodTorch: { + DisplayName: Glow Wood Torch + Tooltip: "" + } + + GlowWoodWorkBench: { + DisplayName: Glow Wood Work Bench + Tooltip: "" + } + + CorruptMothTreasureBag: { + DisplayName: Corrupt Moth Treasure Bag + Tooltip: "" + } + + MothRelic: { + DisplayName: Moth Relic + Tooltip: "" + } + + FireflyBorageBadge: { + DisplayName: Firefly Borage Badge + Tooltip: "" + } + + MothEye: { + DisplayName: Moth Eye + Tooltip: "" + } + + LilyHarp: { + DisplayName: Lily Harp + Tooltip: "" + } + + BlueflowerBra: { + DisplayName: Blueflower Bra + Tooltip: "" + } + + BlueflowerCap: { + DisplayName: Blueflower Cap + Tooltip: "" + } + + BlueflowerDress: { + DisplayName: Blueflower Dress + Tooltip: "" + } + + AshBone: { + DisplayName: Ash Bone + Tooltip: "" + } + + BoneFeatherMagic: { + DisplayName: Bone Feather Magic + Tooltip: "" + } + + CloudGun: { + DisplayName: Cloud Gun + Tooltip: "" + } + + ComingGhost: { + DisplayName: Coming Ghost + Tooltip: "" + } + + CyanFrost: { + DisplayName: Cyan Frost + Tooltip: "" + } + + FeatherMagic: { + DisplayName: Feather Magic + Tooltip: "" + } + + FireFeatherMagic: { + DisplayName: Fire Feather Magic + Tooltip: "" + } + + FreezeFeatherMagic: { + DisplayName: Freeze Feather Magic + Tooltip: "" + } + + GoldRoundYoyo: { + DisplayName: Gold Round Yoyo + Tooltip: "" + } + + GoldRound: { + DisplayName: Gold Round + Tooltip: "" + } + + Hepuyuan: { + DisplayName: Hepuyuan + Tooltip: "" + } + + SilveralGun: { + DisplayName: Silveral Gun + Tooltip: "" + } + + SilveralRifle: { + DisplayName: Silveral Rifle + Tooltip: "" + } + + Sunflower: { + DisplayName: Sunflower + Tooltip: "" + } + + ThunderFlower: { + DisplayName: Thunder Flower + Tooltip: "" + } + + World: { + DisplayName: World + Tooltip: "" + } + + XmasWhip: { + DisplayName: Xmas Whip + Tooltip: "" + } + + AmbiguousNight: { + DisplayName: Ambiguous Night + Tooltip: "" + } + + AmethystSlingshot: { + DisplayName: Amethyst Slingshot + Tooltip: "" + } + + BorealWoodSlingshot: { + DisplayName: Boreal Wood Slingshot + Tooltip: "" + } + + CactusSlingShot: { + DisplayName: Cactus Sling Shot + Tooltip: "" + } + + DiamondSlingshot: { + DisplayName: Diamond Slingshot + Tooltip: "" + } + + EbonwoodSlingshot: { + DisplayName: Ebonwood Slingshot + Tooltip: "" + } + + EmeraldSlingshot: { + DisplayName: Emerald Slingshot + Tooltip: "" + } + + GelSlingshot: { + DisplayName: Gel Slingshot + Tooltip: "" + } + + JungleSlingshot: { + DisplayName: Jungle Slingshot + Tooltip: "" + } + + PalmWoodSlingshot: { + DisplayName: Palm Wood Slingshot + Tooltip: "" + } + + PearlwoodSlingshot: { + DisplayName: Pearlwood Slingshot + Tooltip: "" + } + + RichMahoganySlingshot: { + DisplayName: Rich Mahogany Slingshot + Tooltip: "" + } + + RubySlingshot: { + DisplayName: Ruby Slingshot + Tooltip: "" + } + + SapphireSlingshot: { + DisplayName: Sapphire Slingshot + Tooltip: "" + } + + ShadewoodSlingShot: { + DisplayName: Shadewood Sling Shot + Tooltip: "" + } + + StarSlingshot: { + DisplayName: Star Slingshot + Tooltip: "" + } + + TopazSlingshot: { + DisplayName: Topaz Slingshot + Tooltip: "" + } + + WoodSlingshot: { + DisplayName: Wood Slingshot + Tooltip: "" + } + + AdamantiteClub: { + DisplayName: Adamantite Club + Tooltip: "" + } + + BorealWoodClub: { + DisplayName: Boreal Wood Club + Tooltip: "" + } + + CactusClub: { + DisplayName: Cactus Club + Tooltip: "" + } + + ChlorophyteClub: { + DisplayName: Chlorophyte Club + Tooltip: "" + } + + CobaltClub: { + DisplayName: Cobalt Club + Tooltip: "" + } + + CopperClub: { + DisplayName: Copper Club + Tooltip: "" + } + + CorruptClub: { + DisplayName: Corrupt Club + Tooltip: "" + } + + CrimsonClub: { + DisplayName: Crimson Club + Tooltip: "" + } + + CrystalClub: { + DisplayName: Crystal Club + Tooltip: "" + } + + CurseClub: { + DisplayName: Curse Club + Tooltip: "" + } + + EbonwoodClub: { + DisplayName: Ebonwood Club + Tooltip: "" + } + + GoldClub: { + DisplayName: Gold Club + Tooltip: "" + } + + IchorClub: { + DisplayName: Ichor Club + Tooltip: "" + } + + IronClub: { + DisplayName: Iron Club + Tooltip: "" + } + + LeadClub: { + DisplayName: Lead Club + Tooltip: "" + } + + MeteorClub: { + DisplayName: Meteor Club + Tooltip: "" + } + + MithrilClub: { + DisplayName: Mithril Club + Tooltip: "" + } + + OrichalcumClub: { + DisplayName: Orichalcum Club + Tooltip: "" + } + + PalladiumClub: { + DisplayName: Palladium Club + Tooltip: "" + } + + PalmWoodClub: { + DisplayName: Palm Wood Club + Tooltip: "" + } + + PearlwoodClub: { + DisplayName: Pearlwood Club + Tooltip: "" + } + + PlatinumClub: { + DisplayName: Platinum Club + Tooltip: "" + } + + RichMahoganyClub: { + DisplayName: Rich Mahogany Club + Tooltip: "" + } + + ShadewoodClub: { + DisplayName: Shadewood Club + Tooltip: "" + } + + SilverClub: { + DisplayName: Silver Club + Tooltip: "" + } + + SpikeClub: { + DisplayName: Spike Club + Tooltip: "" + } + + StarDancer: { + DisplayName: Star Dancer + Tooltip: "" + } + + TinClub: { + DisplayName: Tin Club + Tooltip: "" + } + + TitaniumClub: { + DisplayName: Titanium Club + Tooltip: "" + } + + TungstenClub: { + DisplayName: Tungsten Club + Tooltip: "" + } + + WoodenClub: { + DisplayName: Wooden Club + Tooltip: "" + } + + CloudBall: { + DisplayName: Cloud Ball + Tooltip: "" + } + + BlueGel: { + DisplayName: Blue Gel + Tooltip: "" + } + + CorruptEye: { + DisplayName: Corrupt Eye + Tooltip: "" + } + + DukeTooth: { + DisplayName: Duke Tooth + Tooltip: "" + } + + GoldLiquidPupil: { + DisplayName: Gold Liquid Pupil + Tooltip: "" + } + + GreenGel: { + DisplayName: Green Gel + Tooltip: "" + } + + JungleVitamin: { + DisplayName: Jungle Vitamin + Tooltip: "" + } + + Odd8Ring: { + DisplayName: Odd8 Ring + Tooltip: "" + } + + RedGel: { + DisplayName: Red Gel + Tooltip: "" + } + + SilverWing: { + DisplayName: Silver Wing + Tooltip: "" + } + + WalnutClip: { + DisplayName: Walnut Clip + Tooltip: "" + } + + FixCoinCrit1: { + DisplayName: Fix Coin Crit1 + Tooltip: "" + } + + FixCoinCrit2: { + DisplayName: Fix Coin Crit2 + Tooltip: "" + } + + FixCoinCrit3: { + DisplayName: Fix Coin Crit3 + Tooltip: "" + } + + FixCoinCrit4: { + DisplayName: Fix Coin Crit4 + Tooltip: "" + } + + FixCoinCrit5: { + DisplayName: Fix Coin Crit5 + Tooltip: "" + } + + FixCoinDamage1: { + DisplayName: Fix Coin Damage1 + Tooltip: "" + } + + FixCoinDamage2: { + DisplayName: Fix Coin Damage2 + Tooltip: "" + } + + FixCoinDamage3: { + DisplayName: Fix Coin Damage3 + Tooltip: "" + } + + FixCoinDamage4: { + DisplayName: Fix Coin Damage4 + Tooltip: "" + } + + FixCoinDamage5: { + DisplayName: Fix Coin Damage5 + Tooltip: "" + } + + FixCoinDefense1: { + DisplayName: Fix Coin Defense1 + Tooltip: "" + } + + FixCoinDefense2: { + DisplayName: Fix Coin Defense2 + Tooltip: "" + } + + FixCoinDefense3: { + DisplayName: Fix Coin Defense3 + Tooltip: "" + } + + FixCoinDefense4: { + DisplayName: Fix Coin Defense4 + Tooltip: "" + } + + FixCoinDefense5: { + DisplayName: Fix Coin Defense5 + Tooltip: "" + } + + FixCoinMelee1: { + DisplayName: Fix Coin Melee1 + Tooltip: "" + } + + FixCoinMelee2: { + DisplayName: Fix Coin Melee2 + Tooltip: "" + } + + FixCoinMelee3: { + DisplayName: Fix Coin Melee3 + Tooltip: "" + } + + FixCoinSpeed1: { + DisplayName: Fix Coin Speed1 + Tooltip: "" + } + + FixCoinSpeed2: { + DisplayName: Fix Coin Speed2 + Tooltip: "" + } + + FixCoinSpeed3: { + DisplayName: Fix Coin Speed3 + Tooltip: "" + } + + CrystalSkull: { + DisplayName: Crystal Skull + Tooltip: "" + } + + BloodLamp: { + DisplayName: Blood Lamp + Tooltip: "" + } + + LanternBombRemoteControl: { + DisplayName: Lantern Bomb Remote Control + Tooltip: "" + } + + CactusBall: { + DisplayName: Cactus Ball + Tooltip: "" + } + + BlueFreeze: { + DisplayName: Blue Freeze + Tooltip: "" + } + + BluePedal: { + DisplayName: Blue Pedal + Tooltip: "" + } + + CyanHyacinth: { + DisplayName: Cyan Hyacinth + Tooltip: "" + } + + DarkPoppy: { + DisplayName: Dark Poppy + Tooltip: "" + } + + FlowerBrochure: { + DisplayName: Flower Brochure + Tooltip: "" + } + + GoldCup: { + DisplayName: Gold Cup + Tooltip: "" + } + + GoldWhip: { + DisplayName: Gold Whip + Tooltip: "" + } + + HotPinkTulip: { + DisplayName: Hot Pink Tulip + Tooltip: "" + } + + Lavender: { + DisplayName: Lavender + Tooltip: "" + } + + LightChrysanthemum: { + DisplayName: Light Chrysanthemum + Tooltip: "" + } + + LightPurpleBalls: { + DisplayName: Light Purple Balls + Tooltip: "" + } + + OrangeSausage: { + DisplayName: Orange Sausage + Tooltip: "" + } + + OrangeTulip: { + DisplayName: Orange Tulip + Tooltip: "" + } + + PinkSun: { + DisplayName: Pink Sun + Tooltip: "" + } + + PurplePhantom: { + DisplayName: Purple Phantom + Tooltip: "" + } + + PurpleTail: { + DisplayName: Purple Tail + Tooltip: "" + } + + RedFlame: { + DisplayName: Red Flame + Tooltip: "" + } + + ShallowNight: { + DisplayName: Shallow Night + Tooltip: "" + } + + SilverClock: { + DisplayName: Silver Clock + Tooltip: "" + } + + WhiteStar: { + DisplayName: White Star + Tooltip: "" + } + + WhiteTulip: { + DisplayName: White Tulip + Tooltip: "" + } + + WindMoveSeed: { + DisplayName: Wind Move Seed + Tooltip: "" + } + + BlueTorchFlower: { + DisplayName: Blue Torch Flower + Tooltip: "" + } + + CyanBranch: { + DisplayName: Cyan Branch + Tooltip: "" + } + + CyanPedal: { + DisplayName: Cyan Pedal + Tooltip: "" + } + + DarkHat: { + DisplayName: Dark Hat + Tooltip: "" + } + + HotPinkCrown: { + DisplayName: Hot Pink Crown + Tooltip: "" + } + + LightPurplePen: { + DisplayName: Light Purple Pen + Tooltip: "" + } + + ManyWhiteFlower: { + DisplayName: Many White Flower + Tooltip: "" + } + + OrangeCrown: { + DisplayName: Orange Crown + Tooltip: "" + } + + OrangeStick: { + DisplayName: Orange Stick + Tooltip: "" + } + + PinkFourPedal: { + DisplayName: Pink Four Pedal + Tooltip: "" + } + + PurpleBall: { + DisplayName: Purple Ball + Tooltip: "" + } + + PurpleReedHalo: { + DisplayName: Purple Reed Halo + Tooltip: "" + } + + RedTriangle: { + DisplayName: Red Triangle + Tooltip: "" + } + + SilverCupFlower: { + DisplayName: Silver Cup Flower + Tooltip: "" + } + + ThreeColorCrown: { + DisplayName: Three Color Crown + Tooltip: "" + } + + WhiteCrown: { + DisplayName: White Crown + Tooltip: "" + } + + WhitePedal: { + DisplayName: White Pedal + Tooltip: "" + } + + WhiteWoodFlower: { + DisplayName: White Wood Flower + Tooltip: "" + } + + YellowBellFlower: { + DisplayName: Yellow Bell Flower + Tooltip: "" + } + + TreePlacer: { + DisplayName: Tree Placer + Tooltip: "" + } + + TwilightWood: { + DisplayName: Twilight Wood + Tooltip: "" + } + + YggdrasilImpression: { + DisplayName: Yggdrasil Impression + Tooltip: "" + } + + ChinesePartitionLamp: { + DisplayName: Chinese Partition Lamp + Tooltip: "" + } + + ChineseStyleFloorLamp: { + DisplayName: Chinese Style Floor Lamp + Tooltip: "" + } + + CyanVineBar: { + DisplayName: Cyan Vine Bar + Tooltip: "" + } + + CyanVineOre: { + DisplayName: Cyan Vine Ore + Tooltip: "" + } + + DoubleArmsChineseStreetLamp: { + DisplayName: Double Arms Chinese Street Lamp + Tooltip: "" + } + + HangingSkyLantern: { + DisplayName: Hanging Sky Lantern + Tooltip: "" + } + + HexagonalCeilingLamp: { + DisplayName: Hexagonal Ceiling Lamp + Tooltip: "" + } + + ShoreMud: { + DisplayName: Shore Mud + Tooltip: "" + } + + SideHangingLantern: { + DisplayName: Side Hanging Lantern + Tooltip: "" + } + + SideHangingLantern_White: { + DisplayName: Side Hanging Lantern_ White + Tooltip: "" + } + + StoneDragonScaleWood: { + DisplayName: Stone Dragon Scale Wood + Tooltip: "" + } + + StoneDragonScaleWoodWall: { + DisplayName: Stone Dragon Scale Wood Wall + Tooltip: "" + } + + StreetLantern: { + DisplayName: Street Lantern + Tooltip: "" + } + + YellowDynastyShingles: { + DisplayName: Yellow Dynasty Shingles + Tooltip: "" + } + + YggdrasilAmber: { + DisplayName: Yggdrasil Amber + Tooltip: "" + } + + CyanVineAxe: { + DisplayName: Cyan Vine Axe + Tooltip: "" + } + + CyanVineHammer: { + DisplayName: Cyan Vine Hammer + Tooltip: "" + } + + CyanVinePickaxe: { + DisplayName: Cyan Vine Pickaxe + Tooltip: "" + } + + CyanVineSword: { + DisplayName: Cyan Vine Sword + Tooltip: "" + } + + CyanVineThrowingSpear: { + DisplayName: Cyan Vine Throwing Spear + Tooltip: "" + } + + DragonScaleWood: { + DisplayName: Dragon Scale Wood + Tooltip: "" + } + + DragonScaleWoodWall: { + DisplayName: Dragon Scale Wood Wall + Tooltip: "" + } + + OldMoss: { + DisplayName: Old Moss + Tooltip: "" + } + + YggdrasilDirt: { + DisplayName: Yggdrasil Dirt + Tooltip: "" + } + + AcroporaSpear: { + DisplayName: Acropora Spear + Tooltip: "" + } + + CyanWindGranite: { + DisplayName: Cyan Wind Granite + Tooltip: "" + } + + BloodLightCrystal: { + DisplayName: Blood Light Crystal + Tooltip: "" + } + + TrueDeathSickle: { + DisplayName: True Death Sickle + Tooltip: "" + } + + LiftLamp: { + DisplayName: Lift Lamp + Tooltip: "" + } + + LiftWinch: { + DisplayName: Lift Winch + Tooltip: "" + } + + HardmodeItem: { + DisplayName: Hardmode Item + Tooltip: "" + } + + SightOfTile: { + DisplayName: Sight Of Tile + Tooltip: "" + } + + TestItem: { + DisplayName: Test Item + Tooltip: "" + } +} + +NPCs: { + TuskKillEffect.DisplayName: Tusk Kill Effect + TuskWall.DisplayName: Tusk Wall + BloodTusk.DisplayName: Blood Tusk + BloodyMouth1.DisplayName: Bloody Mouth1 + BloodyMouth2.DisplayName: Bloody Mouth2 + CrimsonTusk.DisplayName: Crimson Tusk + CrimsonTusk1.DisplayName: Crimson Tusk1 + CrimsonTuskControlable.DisplayName: Crimson Tusk Controlable + CrimsonTuskFireWork.DisplayName: Crimson Tusk Fire Work + HugeMouth.DisplayName: Huge Mouth + LargeTusk.DisplayName: Large Tusk + LargeTusk2.DisplayName: Large Tusk2 + LittleMouth.DisplayName: Little Mouth + LittleTusk.DisplayName: Little Tusk + TuskCooling.DisplayName: Tusk Cooling + TuskPoolWave.DisplayName: Tusk Pool Wave + TuskRedLight.DisplayName: Tusk Red Light + TuskWallLeft.DisplayName: Tusk Wall Left + TuskWallRight.DisplayName: Tusk Wall Right + TuskWave.DisplayName: Tusk Wave + TuskWaveHuge.DisplayName: Tusk Wave Huge + GlowingHeal.DisplayName: Glowing Heal + BlackStarFruit.DisplayName: Black Star Fruit + CentipedeHead.DisplayName: Centipede Head + CentipedeBody.DisplayName: Centipede Body + CentipedeTail.DisplayName: Centipede Tail + Dendroid_normal.DisplayName: Dendroid_normal + FireflyPiranha.DisplayName: Firefly Piranha + FireflyPiranhaInfected.DisplayName: Firefly Piranha Infected + FireflyPiranhaInfected_small.DisplayName: Firefly Piranha Infected_small + FireflyPiranha_small.DisplayName: Firefly Piranha_small + GlowingFirefly.DisplayName: Glowing Firefly + LargeFireBulb.DisplayName: Large Fire Bulb + LittleFireBulb.DisplayName: Little Fire Bulb + Butterfly.DisplayName: Butterfly + CorruptMoth.DisplayName: Corrupt Moth + EvilPack.DisplayName: Evil Pack + MothSummonEffect.DisplayName: Moth Summon Effect + FloatLantern.DisplayName: Float Lantern + RedPackBomber.DisplayName: Red Pack Bomber + LanternGhostKing.DisplayName: Lantern Ghost King + BlueCore.DisplayName: Blue Core + FlamingDashCore.DisplayName: Flaming Dash Core + GreenCore.DisplayName: Green Core + RedCore.DisplayName: Red Core + YellowCore.DisplayName: Yellow Core + + Acytaea: { + DisplayName: Acytaea + + TownNPCMood: { + Content: I am content. + NoHome: I hate not having a home. + FarFromHome: I am too far from home. + LoveSpace: I love having so much space. + DislikeCrowded: I dislike how crowded I am. + HateCrowded: I hate how crowded I am. + LikeNPC_Princess: I like {NPCName}. + Princess_LovesNPC: I like {NPCName}. + } + } + + AcytaeaShadow.DisplayName: Acytaea Shadow + AcytaeaShadow2.DisplayName: Acytaea Shadow2 + AcytaeaShadow3.DisplayName: Acytaea Shadow3 + FakeAcytaea.DisplayName: Fake Acytaea + CyanOreBeetle.DisplayName: Cyan Ore Beetle + SquamousShell.DisplayName: Squamous Shell + TestNPC.DisplayName: Test N P C +} + +Prefixes: { + Accurate.DisplayName: Accurate + Aimed.DisplayName: Aimed +} + +Projectiles: { + BloodGoldBayonet_Pro.DisplayName: Blood Gold Bayonet_ Pro + BlueRibbonBayonet_Pro.DisplayName: Blue Ribbon Bayonet_ Pro + CopperStabbingSword_Pro.DisplayName: Copper Stabbing Sword_ Pro + DreamStar_Explosion.DisplayName: Dream Star_ Explosion + DreamStar_Pro.DisplayName: Dream Star_ Pro + EnchantedBayonet_Pro.DisplayName: Enchanted Bayonet_ Pro + EternalNight_Pro.DisplayName: Eternal Night_ Pro + GoldenStabbingSword_Pro.DisplayName: Golden Stabbing Sword_ Pro + HolyBayonet_Pro.DisplayName: Holy Bayonet_ Pro + IronStabbingSword_Pro.DisplayName: Iron Stabbing Sword_ Pro + LeadStabbingSword_Pro.DisplayName: Lead Stabbing Sword_ Pro + PlatinumStabbingSword_Pro.DisplayName: Platinum Stabbing Sword_ Pro + PrisonFireBayonet_Pro.DisplayName: Prison Fire Bayonet_ Pro + RedRibbonBayonet_Pro.DisplayName: Red Ribbon Bayonet_ Pro + RottenGoldBayonet_Mark.DisplayName: Rotten Gold Bayonet_ Mark + RottenGoldBayonet_Pro.DisplayName: Rotten Gold Bayonet_ Pro + RottenGoldBayonet_Slash.DisplayName: Rotten Gold Bayonet_ Slash + SilverStabbingSword_Pro.DisplayName: Silver Stabbing Sword_ Pro + TinStabbingSword_Pro.DisplayName: Tin Stabbing Sword_ Pro + TungstenStabbingSword_Pro.DisplayName: Tungsten Stabbing Sword_ Pro + VegetationBayonet_Pro.DisplayName: Vegetation Bayonet_ Pro + YoenLeZed_Pro.DisplayName: Yoen Le Zed_ Pro + FryingPan.DisplayName: Frying Pan + ButterflyDreamFriendly.DisplayName: Butterfly Dream Friendly + ButterflyDream.DisplayName: Butterfly Dream + DashingLightEff.DisplayName: Dashing Light Eff + VortexVanquisher.DisplayName: Vortex Vanquisher + VortexVanquisher2.DisplayName: Vortex Vanquisher2 + VortexVanquisher3.DisplayName: Vortex Vanquisher3 + VortexVanquisherThump.DisplayName: Vortex Vanquisher Thump + GoldShield.DisplayName: Gold Shield + StonePost.DisplayName: Stone Post + CrimsonTuskProj.DisplayName: Crimson Tusk Proj + CrimsonTuskProjGra.DisplayName: Crimson Tusk Proj Gra + EarthTusk.DisplayName: Earth Tusk + EarthTuskHostile.DisplayName: Earth Tusk Hostile + TuskCrack.DisplayName: Tusk Crack + TuskCurse.DisplayName: Tusk Curse + TuskSpice.DisplayName: Tusk Spice + TuskSpiceBlack.DisplayName: Tusk Spice Black + TuskSpicePro.DisplayName: Tusk Spice Pro + Tusk.DisplayName: Tusk + BloodyBoneYoyo.DisplayName: Bloody Bone Yoyo + LittleTusk.DisplayName: Little Tusk + LittleTusk0.DisplayName: Little Tusk0 + SpineGun.DisplayName: Spine Gun + SplieSpineBullet.DisplayName: Splie Spine Bullet + ToothMagic.DisplayName: Tooth Magic + ToothMagicBall.DisplayName: Tooth Magic Ball + ToothMagicHit.DisplayName: Tooth Magic Hit + ToothMagicSplit.DisplayName: Tooth Magic Split + ToothMagicSplit2.DisplayName: Tooth Magic Split2 + ToothSpear.DisplayName: Tooth Spear + Tusk0.DisplayName: Tusk0 + TuskArrow.DisplayName: Tusk Arrow + TuskSpear3.DisplayName: Tusk Spear3 + TuskSpear4.DisplayName: Tusk Spear4 + TuskSummon.DisplayName: Tusk Summon + BeadShakeWave.DisplayName: Bead Shake Wave + BlackCorruptRain.DisplayName: Black Corrupt Rain + BlackCorruptRain3.DisplayName: Black Corrupt Rain3 + BlackCorruptRain3Friendly.DisplayName: Black Corrupt Rain3 Friendly + BlackCorruptRainFriendly.DisplayName: Black Corrupt Rain Friendly + BlueMissil.DisplayName: Blue Missil + BlueMissilFriendly.DisplayName: Blue Missil Friendly + BombShakeWave.DisplayName: Bomb Shake Wave + CorMoth4DProj.DisplayName: Cor Moth4 D Proj + CorruptDust.DisplayName: Corrupt Dust + DarkEffect.DisplayName: Dark Effect + DarkFan.DisplayName: Dark Fan + DarkFanFly.DisplayName: Dark Fan Fly + DreamWeaver.DisplayName: Dream Weaver + EvilChrysalis.DisplayName: Evil Chrysalis + EvilChrysalisRightClick.DisplayName: Evil Chrysalis Right Click + FanHit.DisplayName: Fan Hit + FlowLightMissile.DisplayName: Flow Light Missile + FruitBomb.DisplayName: Fruit Bomb + GlowBeadGun.DisplayName: Glow Bead Gun + GlowBeadGunShootFlame.DisplayName: Glow Bead Gun Shoot Flame + GlowingButterfly.DisplayName: Glowing Butterfly + GlowMoth.DisplayName: Glow Moth + GlowStar.DisplayName: Glow Star + GlowStarExplosion.DisplayName: Glow Star Explosion + GlowWoodSword.DisplayName: Glow Wood Sword + MissileProj.DisplayName: Missile Proj + MissileProjHostile.DisplayName: Missile Proj Hostile + MothArrow.DisplayName: Moth Arrow + MothBall.DisplayName: Moth Ball + MothGrey.DisplayName: Moth Grey + MothMagicArray.DisplayName: Moth Magic Array + MothYoyoProjectile.DisplayName: Moth Yoyo Projectile + MothYoyoSub.DisplayName: Moth Yoyo Sub + NavyThunder.DisplayName: Navy Thunder + NavyThunderBomb.DisplayName: Navy Thunder Bomb + PhantomMoth.DisplayName: Phantom Moth + PhosphorescenceBullet.DisplayName: Phosphorescence Bullet + PhosphorescenceGun.DisplayName: Phosphorescence Gun + ScaleWingBladeProj.DisplayName: Scale Wing Blade Proj + ShadowWingBow.DisplayName: Shadow Wing Bow + StaffOfCorruptDust.DisplayName: Staff Of Corrupt Dust + LilyHarpNote.DisplayName: Lily Harp Note + LilyHarpNoteKill.DisplayName: Lily Harp Note Kill + LilyHarpProj.DisplayName: Lily Harp Proj + LilyHarpWave.DisplayName: Lily Harp Wave + VineProj.DisplayName: Vine Proj + VineProj2.DisplayName: Vine Proj2 + CloudBall.DisplayName: Cloud Ball + MagicHit.DisplayName: Magic Hit + AshBone.DisplayName: Ash Bone + XmasWhip.DisplayName: Xmas Whip + AmbiguousLine.DisplayName: Ambiguous Line + AmbiguousNight.DisplayName: Ambiguous Night + AmbiguousNightAmmo.DisplayName: Ambiguous Night Ammo + AmbiguousNightHit.DisplayName: Ambiguous Night Hit + AmethystBead.DisplayName: Amethyst Bead + AmethystSlingshot.DisplayName: Amethyst Slingshot + BorealWoodSlingshot.DisplayName: Boreal Wood Slingshot + CactusSlingShot.DisplayName: Cactus Sling Shot + DiamondBead.DisplayName: Diamond Bead + DiamondSlingshot.DisplayName: Diamond Slingshot + EbonwoodSlingshot.DisplayName: Ebonwood Slingshot + EmeraldBead.DisplayName: Emerald Bead + EmeraldSlingshot.DisplayName: Emerald Slingshot + GelBall.DisplayName: Gel Ball + GelSlingshot.DisplayName: Gel Slingshot + GlowSporeBead.DisplayName: Glow Spore Bead + JungleSlingshot.DisplayName: Jungle Slingshot + JungleSlingshotHit.DisplayName: Jungle Slingshot Hit + KSSlingshotHit.DisplayName: K S Slingshot Hit + KSSlingshotHitPink.DisplayName: K S Slingshot Hit Pink + NormalAmmo.DisplayName: Normal Ammo + NormalHit.DisplayName: Normal Hit + PalmWoodSlingshot.DisplayName: Palm Wood Slingshot + PearlwoodSlingshot.DisplayName: Pearlwood Slingshot + RichMahoganySlingshot.DisplayName: Rich Mahogany Slingshot + RubyBead.DisplayName: Ruby Bead + RubySlingshot.DisplayName: Ruby Slingshot + SapphireBead.DisplayName: Sapphire Bead + SapphireSlingshot.DisplayName: Sapphire Slingshot + ShadewoodSlingShot.DisplayName: Shadewood Sling Shot + StarAmmo.DisplayName: Star Ammo + StarSlingshot.DisplayName: Star Slingshot + TopazBead.DisplayName: Topaz Bead + TopazSlingshot.DisplayName: Topaz Slingshot + WoodSlingshot.DisplayName: Wood Slingshot + ComingGhost.DisplayName: Coming Ghost + ComingGhost2.DisplayName: Coming Ghost2 + GhostHit.DisplayName: Ghost Hit + GoldRound.DisplayName: Gold Round + GoldRoundYoyo.DisplayName: Gold Round Yoyo + MeleeHit.DisplayName: Melee Hit + MeleeHit2.DisplayName: Melee Hit2 + Sunflower.DisplayName: Sunflower + SunFlowerpetal.DisplayName: Sun Flowerpetal + World.DisplayName: World + WorldHit.DisplayName: World Hit + Hepuyuan.DisplayName: Hepuyuan + HepuyuanDown.DisplayName: Hepuyuan Down + HepuyuanShake.DisplayName: Hepuyuan Shake + HepuyuanSpice.DisplayName: Hepuyuan Spice + XiaoBlackWave.DisplayName: Xiao Black Wave + XiaoHit.DisplayName: Xiao Hit + AdamantiteClub.DisplayName: Adamantite Club + BorealWoodClub.DisplayName: Boreal Wood Club + CactusClub.DisplayName: Cactus Club + ChlorophyteClub.DisplayName: Chlorophyte Club + ChlorophyteClub_fly.DisplayName: Chlorophyte Club_fly + ChlorophyteClub_VFX.DisplayName: Chlorophyte Club_ V F X + CobaltClub.DisplayName: Cobalt Club + CopperClub.DisplayName: Copper Club + CorruptClub.DisplayName: Corrupt Club + CrimsonClub.DisplayName: Crimson Club + CrystalClub.DisplayName: Crystal Club + CrystalClub_fly.DisplayName: Crystal Club_fly + CurseClub.DisplayName: Curse Club + CurseClub_fly.DisplayName: Curse Club_fly + EbonwoodClub.DisplayName: Ebonwood Club + GoldClub.DisplayName: Gold Club + IchorClub.DisplayName: Ichor Club + IchorClub_fly.DisplayName: Ichor Club_fly + IronClub.DisplayName: Iron Club + LeadClub.DisplayName: Lead Club + MeteorClub.DisplayName: Meteor Club + MithrilClub.DisplayName: Mithril Club + OrichalcumClub.DisplayName: Orichalcum Club + PalladiumClub.DisplayName: Palladium Club + PalmWoodClub.DisplayName: Palm Wood Club + PearlwoodClub.DisplayName: Pearlwood Club + PlatinumClub.DisplayName: Platinum Club + RichMahoganyClub.DisplayName: Rich Mahogany Club + ShadewoodClub.DisplayName: Shadewood Club + SilverClub.DisplayName: Silver Club + SpikeClub.DisplayName: Spike Club + StarDancer.DisplayName: Star Dancer + TinClub.DisplayName: Tin Club + TitaniumClub.DisplayName: Titanium Club + TungstenClub.DisplayName: Tungsten Club + WoodenClub.DisplayName: Wooden Club + BoneFeather.DisplayName: Bone Feather + EndlessCurseFlame.DisplayName: Endless Curse Flame + FireFeather.DisplayName: Fire Feather + FreezeFeather.DisplayName: Freeze Feather + ThunderBall.DisplayName: Thunder Ball + ThunderBall2.DisplayName: Thunder Ball2 + ThunderBallToNPC.DisplayName: Thunder Ball To N P C + FreezeFeatherMagicArray.DisplayName: Freeze Feather Magic Array + FreezeFeatherMagicBook.DisplayName: Freeze Feather Magic Book + FreezeShoot.DisplayName: Freeze Shoot + GiantFreezeFeather.DisplayName: Giant Freeze Feather + GiantFreezeFeatherExplosion.DisplayName: Giant Freeze Feather Explosion + FireFeatherMagicArray.DisplayName: Fire Feather Magic Array + FireFeatherMagicBook.DisplayName: Fire Feather Magic Book + FlameShoot.DisplayName: Flame Shoot + GiantFireFeather.DisplayName: Giant Fire Feather + GiantFireFeatherExplosion.DisplayName: Giant Fire Feather Explosion + FlowerPetalPurple.DisplayName: Flower Petal Purple + PurpleBallEffect.DisplayName: Purple Ball Effect + FixCoinCrit1.DisplayName: Fix Coin Crit1 + FixCoinCrit2.DisplayName: Fix Coin Crit2 + FixCoinCrit3.DisplayName: Fix Coin Crit3 + FixCoinCrit4.DisplayName: Fix Coin Crit4 + FixCoinCrit5.DisplayName: Fix Coin Crit5 + FixCoinDamage1.DisplayName: Fix Coin Damage1 + FixCoinDamage2.DisplayName: Fix Coin Damage2 + FixCoinDamage3.DisplayName: Fix Coin Damage3 + FixCoinDamage4.DisplayName: Fix Coin Damage4 + FixCoinDamage5.DisplayName: Fix Coin Damage5 + FixCoinDefense1.DisplayName: Fix Coin Defense1 + FixCoinDefense2.DisplayName: Fix Coin Defense2 + FixCoinDefense3.DisplayName: Fix Coin Defense3 + FixCoinDefense4.DisplayName: Fix Coin Defense4 + FixCoinDefense5.DisplayName: Fix Coin Defense5 + FixCoinMelee1.DisplayName: Fix Coin Melee1 + FixCoinMelee2.DisplayName: Fix Coin Melee2 + FixCoinMelee3.DisplayName: Fix Coin Melee3 + FixCoinSpeed1.DisplayName: Fix Coin Speed1 + FixCoinSpeed2.DisplayName: Fix Coin Speed2 + FixCoinSpeed3.DisplayName: Fix Coin Speed3 + IchorRing.DisplayName: Ichor Ring + NewWaterBolt.DisplayName: New Water Bolt + WaterBoltArray.DisplayName: Water Bolt Array + WaterBoltBook.DisplayName: Water Bolt Book + WaterTeleport.DisplayName: Water Teleport + HurricaneMask.DisplayName: Hurricane Mask + RazorbladeTyphoonArray.DisplayName: Razorblade Typhoon Array + RazorbladeTyphoonBook.DisplayName: Razorblade Typhoon Book + TyphoonII.DisplayName: Typhoon I I + MagnetSphereArray.DisplayName: Magnet Sphere Array + MagnetSphereBook.DisplayName: Magnet Sphere Book + MagnetSphereHit.DisplayName: Magnet Sphere Hit + MagnetSphereII.DisplayName: Magnet Sphere I I + MagnetSphereLighting.DisplayName: Magnet Sphere Lighting + LunarFlareArray.DisplayName: Lunar Flare Array + LunarFlareBook.DisplayName: Lunar Flare Book + LunarFlareHit.DisplayName: Lunar Flare Hit + LunarFlareII.DisplayName: Lunar Flare I I + StarProj.DisplayName: Star Proj + GoldenShowerArray.DisplayName: Golden Shower Array + GoldenShowerBomb.DisplayName: Golden Shower Bomb + GoldenShowerBook.DisplayName: Golden Shower Book + GoldenShowerII.DisplayName: Golden Shower I I + DreamWeaverBook.DisplayName: Dream Weaver Book + DreamWeaverII.DisplayName: Dream Weaver I I + DemoHit.DisplayName: Demo Hit + DemonScytheArray.DisplayName: Demon Scythe Array + DemonScytheBook.DisplayName: Demon Scythe Book + DemonScythePlus.DisplayName: Demon Scythe Plus + DemonScythePlusCrack.DisplayName: Demon Scythe Plus Crack + DemoSpark.DisplayName: Demo Spark + CursedFlameHit.DisplayName: Cursed Flame Hit + CursedFlamesArray.DisplayName: Cursed Flames Array + CursedFlamesBook.DisplayName: Cursed Flames Book + CursedFlamesII.DisplayName: Cursed Flames I I + CrystalStormArray.DisplayName: Crystal Storm Array + CrystalStormBook.DisplayName: Crystal Storm Book + CrystalStormII.DisplayName: Crystal Storm I I + CrystalWind.DisplayName: Crystal Wind + LargeCrystal.DisplayName: Large Crystal + Storm.DisplayName: Storm + BoneSpike.DisplayName: Bone Spike + BookofSkullsArray.DisplayName: Bookof Skulls Array + BookofSkullsBook.DisplayName: Bookof Skulls Book + SkullHand.DisplayName: Skull Hand + SkullII.DisplayName: Skull I I + BlackHole.DisplayName: Black Hole + BloodLampProj.DisplayName: Blood Lamp Proj + LBloodEffect.DisplayName: L Blood Effect + LBloodEffectGra.DisplayName: L Blood Effect Gra + LMeteor.DisplayName: L Meteor + RainbowWave.DisplayName: Rainbow Wave + DarkLantern.DisplayName: Dark Lantern + DarkLantern3.DisplayName: Dark Lantern3 + DarkLanternBomb2.DisplayName: Dark Lantern Bomb2 + DarkLanternBombExplosion.DisplayName: Dark Lantern Bomb Explosion + DarkLanternBombExplosion_II.DisplayName: Dark Lantern Bomb Explosion_ I I + ExplodeLantern.DisplayName: Explode Lantern + floatLantern.DisplayName: float Lantern + floatLantern2.DisplayName: float Lantern2 + floatLantern3.DisplayName: float Lantern3 + floatLantern4.DisplayName: float Lantern4 + floatLantern5.DisplayName: float Lantern5 + GoldLantern.DisplayName: Gold Lantern + GoldLanternLine.DisplayName: Gold Lantern Line + GoldLanternLine2.DisplayName: Gold Lantern Line2 + GoldLanternLine3.DisplayName: Gold Lantern Line3 + GoldLanternLine4.DisplayName: Gold Lantern Line4 + GoldLanternLine5.DisplayName: Gold Lantern Line5 + GoldLanternLine6.DisplayName: Gold Lantern Line6 + GoldLanternLine7.DisplayName: Gold Lantern Line7 + GoldLanternLine8.DisplayName: Gold Lantern Line8 + GoldLanternRay.DisplayName: Gold Lantern Ray + GoldLanternStar.DisplayName: Gold Lantern Star + Redlight.DisplayName: Redlight + StrikePlayer.DisplayName: Strike Player + BlueFlame0.DisplayName: Blue Flame0 + BlueFlame0Little.DisplayName: Blue Flame0 Little + BlueFlame0LittleSoon.DisplayName: Blue Flame0 Little Soon + BlueFlame1Boom.DisplayName: Blue Flame1 Boom + BlueStarTrail.DisplayName: Blue Star Trail + BrownFlame0.DisplayName: Brown Flame0 + BrownFlame0Little.DisplayName: Brown Flame0 Little + GoldDashLine.DisplayName: Gold Dash Line + GreenFlame0.DisplayName: Green Flame0 + GreenFlame0Little.DisplayName: Green Flame0 Little + GreenFlame0Split.DisplayName: Green Flame0 Split + GreenFlame1Split.DisplayName: Green Flame1 Split + ImmuneCircle.DisplayName: Immune Circle + PinkFlame0.DisplayName: Pink Flame0 + PinkFlame0Little.DisplayName: Pink Flame0 Little + PurpleFlame0.DisplayName: Purple Flame0 + PurpleFlame0Little.DisplayName: Purple Flame0 Little + PurpleGreenFlame0.DisplayName: Purple Green Flame0 + PurpleGreenFlame0Little.DisplayName: Purple Green Flame0 Little + RedFlame0.DisplayName: Red Flame0 + RedFlame0Little.DisplayName: Red Flame0 Little + RedFlame0Split.DisplayName: Red Flame0 Split + RedFlame1Split.DisplayName: Red Flame1 Split + RedFlame2Split.DisplayName: Red Flame2 Split + YellowFlame0.DisplayName: Yellow Flame0 + YellowFlame0Little.DisplayName: Yellow Flame0 Little + YellowFlame0LittleShine.DisplayName: Yellow Flame0 Little Shine + YellowFlame0Shine.DisplayName: Yellow Flame0 Shine + YellowToRedFlame0Little.DisplayName: Yellow To Red Flame0 Little + AcytaeaArrow.DisplayName: Acytaea Arrow + AcytaeaArrow2.DisplayName: Acytaea Arrow2 + AcytaeaArrow3.DisplayName: Acytaea Arrow3 + AcytaeaArrowGra.DisplayName: Acytaea Arrow Gra + AcytaeaBow.DisplayName: Acytaea Bow + AcytaeaBow2.DisplayName: Acytaea Bow2 + AcytaeaCenterBow.DisplayName: Acytaea Center Bow + AcytaeaEffect.DisplayName: Acytaea Effect + AcytaeaEffectUp.DisplayName: Acytaea Effect Up + AcytaeaLaser.DisplayName: Acytaea Laser + AcytaeaLaserBall.DisplayName: Acytaea Laser Ball + AcytaeaLaserLight.DisplayName: Acytaea Laser Light + AcytaeaLight.DisplayName: Acytaea Light + AcytaeaMagicBook.DisplayName: Acytaea Magic Book + AcytaeaTornado.DisplayName: Acytaea Tornado + AcytaeaTornado2.DisplayName: Acytaea Tornado2 + AcytaeaTornado3.DisplayName: Acytaea Tornado3 + AcytaeaTornado4.DisplayName: Acytaea Tornado4 + AcytaeaTornado5.DisplayName: Acytaea Tornado5 + AcytaeaTornadoHit.DisplayName: Acytaea Tornado Hit + AcytaeaWarningArrow.DisplayName: Acytaea Warning Arrow + BloodBlade.DisplayName: Blood Blade + BloodBlade2.DisplayName: Blood Blade2 + BloodBladeShadow.DisplayName: Blood Blade Shadow + BrokenAcytaea.DisplayName: Broken Acytaea + BrokenAcytaea2.DisplayName: Broken Acytaea2 + Metero.DisplayName: Metero + Metero2.DisplayName: Metero2 + Metero3.DisplayName: Metero3 + Metero4.DisplayName: Metero4 + MeteroFri.DisplayName: Metero Fri + playerHit.DisplayName: player Hit + CactusBallProj.DisplayName: Cactus Ball Proj + CactusBallSpike.DisplayName: Cactus Ball Spike + CyanVineThrowingSpear.DisplayName: Cyan Vine Throwing Spear + AcroporaSpear.DisplayName: Acropora Spear + AcroporaThumpEff.DisplayName: Acropora Thump Eff + TrueDeathSickle.DisplayName: True Death Sickle + TrueDeathSickleHit.DisplayName: True Death Sickle Hit + SightOfTileProj.DisplayName: Sight Of Tile Proj + SightOfTileProjRead.DisplayName: Sight Of Tile Proj Read +} + +Configs: { + EverglowConfig: { + DisplayName: Everglow Config + + debugMode: { + Label: Enable Debug Mode + Tooltip: "[For developers] Enable debug mode to allow debug functions to run" + } + } + + EverglowClientConfig: { + DisplayName: Everglow Client Config + + TextureReplace: { + Label: "{$Mods.Everglow.Config.TextureReplace.Label}" + Tooltip: "{$Mods.Everglow.Config.TextureReplace.Tooltip}" + } + + ItemPickSoundReplace: { + Label: "{$Mods.Everglow.Config.ItemPickSoundReplace.Label}" + Tooltip: "" + } + + MothAudioReplace: { + Label: "{$Mods.Everglow.Config.MothAudioReplace.Label}" + Tooltip: "{$Mods.Everglow.Config.MothAudioReplace.Tooltip}" + } + + TuskAudioReplace: { + Label: "{$Mods.Everglow.Config.TuskAudioReplace.Label}" + Tooltip: "{$Mods.Everglow.Config.TuskAudioReplace.Tooltip}" + } + } + + TextureReplaceMode: { + Tooltip: "" + Terraria.Label: Terraria + EternalResolve.Label: Eternal Resolve + Myth.Label: Myth + } + + MothAudioReplaceMode: { + Tooltip: "" + MothFighting.Label: Original + AltMothFighting.Label: Alternate + OldMothFighting.Label: Old + } + + TuskAudioReplaceMode: { + Tooltip: "" + TuskFighting.Label: Original + OldTuskFighting.Label: Old + } + + FogConfigs: { + DisplayName: Fog Configs + + EnableScattering: { + Label: 开启雾效 + Tooltip: 是否开启参与介质散射 + } + + EnableLightUpload: { + Label: 【性能测试】开启光源数据 + Tooltip: 是否启用发光体数据上传(CPU到GPU) + } + + MaxBloomRadius: { + Label: 模糊半径 + Tooltip: 光效的最大溢出半径,最终半径是以2为底的指数,越大越影响性能 + } + + BloomIntensity: { + Label: 光效强度 + Tooltip: 光效的强度,用于放大亮度 + } + + OffscreenTiles: { + Label: 离屏物块渲染数量 + Tooltip: 光效需要计算超出屏幕的物块数量 + } + + LightLuminanceThreashold: { + Label: 光源阈值 + Tooltip: 可以调整原版点亮物块的光源阈值 + } + + GaussianKernel: { + Label: 是否开启高斯卷积核 + Tooltip: 更柔和的效果,但是性能较差 + } + + FogAbsorptionR: { + Label: 雾吸收率(R) + Tooltip: 雾吸收红色通道直接光的比率,越大则越暗淡 + } + + FogAbsorptionG: { + Label: 雾吸收率(G) + Tooltip: 雾吸收绿色通道直接光的比率,越大则越暗淡 + } + + FogAbsorptionB: { + Label: 雾吸收率(B) + Tooltip: 雾吸收蓝色通道直接光的比率,越大则越暗淡 + } + + FogScatterWithDistance: { + Label: 扩散率随距离增大 + Tooltip: 是否让散射效果随着距离增大而增大 + } + + FogBloomRate: { + Label: 雾散射扩散率 + Tooltip: 散射光的扩散比率 + } + + FogBloomAbsorptionFactor: { + Label: 雾散射能见度影响系数 + Tooltip: 散射光受到能见度影响的比率,0为不受能见度影响,1完全受能见度影响 + } + + EnableTemporalInterp: { + Label: 开启Temporal插值 + Tooltip: 是否允许光照结果进行帧间插值来平滑 + } + } +} + +Config: { + Header: { + TextureReplace: Texture Replace Header + AudioReplace: Item Pick Sound Replace Header + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Everglow.Core.csproj b/Sources/Everglow.Core/Everglow.Core.csproj index 9a8d2552b..b29ccf7d7 100644 --- a/Sources/Everglow.Core/Everglow.Core.csproj +++ b/Sources/Everglow.Core/Everglow.Core.csproj @@ -6,9 +6,20 @@ + + + + + + + + + + + diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs new file mode 100644 index 000000000..6ced81e82 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision +{ + public struct AABB + { + public Vector2 MinPoint; + public Vector2 MaxPoint; + + public bool Intersects(in AABB other) + { + return Math.Max(MinPoint.X, other.MinPoint.X) <= Math.Min(MaxPoint.X, other.MaxPoint.X) + && Math.Max(MinPoint.Y, other.MinPoint.Y) <= Math.Min(MaxPoint.Y, other.MaxPoint.Y); + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs new file mode 100644 index 000000000..c18773689 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs @@ -0,0 +1,115 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +{ + /// + /// 暴力遍历AABB的粗粒度碰撞检测算法 + /// + public class BruteForceDetect : BroadPhase + { + private struct BruteForceEntry + { + public int ColliderId; + public AABB BoundingBox; + public bool IsDynamic; + } + private List _colliders; + private Dictionary> _groups; + public BruteForceDetect(CollisionGraph graph) : base(graph) + { + _colliders = new List(); + _groups = new Dictionary>(); + } + + private void GroupInnerDetection(List entries, + List> collisionResults) + { + for (int i = 0; i < entries.Count; i++) + { + for (int j = i + 1; j < entries.Count; j++) + { + if ((entries[i].IsDynamic || entries[j].IsDynamic) + && entries[i].BoundingBox.Intersects(entries[j].BoundingBox)) + { + collisionResults.Add(new KeyValuePair(_colliders[entries[i].ColliderId], + _colliders[entries[j].ColliderId])); + } + } + } + } + + private void GroupOuterDetection(List groupA, + List groupB, + List> collisionResults) + { + for (int i = 0; i < groupA.Count; i++) + { + for (int j = 0; j < groupB.Count; j++) + { + if ((groupA[i].IsDynamic || groupB[j].IsDynamic) + && groupA[i].BoundingBox.Intersects(groupB[j].BoundingBox)) + { + collisionResults.Add(new KeyValuePair(_colliders[groupA[i].ColliderId], + _colliders[groupB[j].ColliderId])); + } + } + } + } + + public override List> GetCollisionPairs(float deltaTime) + { + List> finalPairs = new List>(); + foreach(var group in _groups) + { + var thisgroup = _groups[group.Key]; + GroupInnerDetection(thisgroup, finalPairs); + if (_collisionGraph.Graph.ContainsKey(group.Key)) + { + foreach (var dual in _collisionGraph.Graph[group.Key]) + { + if (!_groups.ContainsKey(dual)) + continue; + GroupOuterDetection(thisgroup, _groups[dual], finalPairs); + } + } + } + return finalPairs; + } + + public override void Prepare(List objects, float deltaTime) + { + _colliders.Clear(); + _colliders.EnsureCapacity(objects.Count); + _groups.Clear(); + for(int i = 0; i < objects.Count; i++) + { + var obj = objects[i]; + _colliders.Add(obj.Collider); + var entry = new BruteForceEntry() + { + ColliderId = i, + BoundingBox = obj.Collider.GetAABB(deltaTime), + IsDynamic = obj.RigidBody.MovementType == MovementType.Dynamic || obj.RigidBody.MovementType == MovementType.Player + }; + + if (_groups.ContainsKey(obj.Tag)) + { + _groups[obj.Tag].Add(entry); + } + else + { + _groups.Add(obj.Tag, new List() + { + entry + }); + } + } + } + + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs new file mode 100644 index 000000000..5a3183227 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +{ + /// + /// 存储碰撞组的碰撞关系的图结构 + /// + public class CollisionGraph + { + public static CollisionGraph DefualtGraph + = new CollisionGraph(new Dictionary>() { { "Default", new List() } }); + public Dictionary> Graph + { + get; + set; + } + + public CollisionGraph() + { + Graph = new Dictionary>(); + } + + public CollisionGraph(Dictionary> graph) + { + Graph = graph; + } + + public void AddDoubleEdge(string groupA, string groupB) + { + if (groupA == groupB) + return; + // Ensure only compare once + if (String.CompareOrdinal(groupA, groupB) < 0) + { + AddSingleEdge(groupA, groupB); + } + else + { + AddSingleEdge(groupB, groupA); + } + } + + public void AddSingleEdge(string groupA, string groupB) + { + if (groupA == groupB) + return; + if (Graph.ContainsKey(groupA)) + { + Graph[groupA].Add(groupB); + } + else + { + Graph.Add(groupA, new List() + { + groupB + }); + } + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs new file mode 100644 index 000000000..0ec4116e7 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs @@ -0,0 +1,24 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +{ + /// + /// 粗粒度碰撞检测的基类 + /// + public abstract class BroadPhase + { + protected CollisionGraph _collisionGraph; + public BroadPhase(CollisionGraph graph) + { + _collisionGraph = graph; + } + public abstract void Prepare(List dynamicObjects, float deltaTime); + + public abstract List> GetCollisionPairs(float deltaTime); + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs new file mode 100644 index 000000000..7a888255e --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs @@ -0,0 +1,143 @@ +//using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using System.Threading.Tasks; + +//namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +//{ +// internal class SweepAndPrune : IBroadPhase +// { +// private struct IntervalPoint +// { +// public float V; +// public bool IsEnd; +// public int ColliderId; +// } + +// private List _pointsX; +// private List _pointsY; +// private List _colliders; + +// public SweepAndPrune() +// { +// _pointsX = new List(); +// _pointsY = new List(); +// _colliders = new List(); +// } +// public List> GetCollisionPairs(float deltaTime) +// { +// List> finalPairs = new List>(); +// HashSet> spottedPairs = new HashSet>(); +// HashSet current = new HashSet(); +// for (int i = 0; i < _pointsX.Count; i++) +// { +// var pt = _pointsX[i]; +// if (pt.IsEnd) +// { +// foreach (var c in current) +// { +// if (c == pt.ColliderId) +// continue; +// var p = new KeyValuePair(pt.ColliderId, c); + +// if (_colliders[p.Key].ParentObject.RigidBody.MovementType == MovementType.Static +// && _colliders[p.Value].ParentObject.RigidBody.MovementType == MovementType.Static) +// { +// continue; +// } +// spottedPairs.Add(p); +// } +// current.Remove(pt.ColliderId); +// } +// else +// { +// current.Add(pt.ColliderId); +// } +// } + +// current.Clear(); + +// for (int i = 0; i < _pointsY.Count; i++) +// { +// var pt = _pointsY[i]; +// if (pt.IsEnd) +// { +// foreach (var c in current) +// { +// if (c == pt.ColliderId) +// continue; +// var p = new KeyValuePair(pt.ColliderId, c); +// if (spottedPairs.Contains(p) +// || spottedPairs.Contains(new KeyValuePair(c, pt.ColliderId))) +// { +// if (_colliders[p.Key].ParentObject.RigidBody.MovementType == MovementType.Static +// && _colliders[p.Value].ParentObject.RigidBody.MovementType == MovementType.Static) +// { +// continue; +// } +// finalPairs.Add(new KeyValuePair(_colliders[p.Key], _colliders[p.Value])); +// } +// } +// current.Remove(pt.ColliderId); +// } +// else +// { +// current.Add(pt.ColliderId); +// } +// } +// return finalPairs; +// } + +// public void Prepare(List dynamicObjects, List staticObjects, float deltaTime) +// { +// _pointsX.Clear(); +// _pointsY.Clear(); +// _colliders.Clear(); +// _colliders.EnsureCapacity(dynamicObjects.Count + staticObjects.Count); +// for (int i = 0; i < dynamicObjects.Count; i++) +// { +// var obj = dynamicObjects[i]; +// var aabb = obj.Collider.GetAABB(deltaTime); +// _pointsX.Add(new IntervalPoint() { V = aabb.MinPoint.X, IsEnd = false, ColliderId = i }); +// _pointsX.Add(new IntervalPoint() { V = aabb.MaxPoint.X, IsEnd = true, ColliderId = i }); +// _pointsY.Add(new IntervalPoint() { V = aabb.MinPoint.Y, IsEnd = false, ColliderId = i }); +// _pointsY.Add(new IntervalPoint() { V = aabb.MaxPoint.Y, IsEnd = true, ColliderId = i }); +// _colliders.Add(obj.Collider); +// } + +// for (int i = 0; i < staticObjects.Count; i++) +// { +// var obj = staticObjects[i]; +// var aabb = obj.Collider.GetAABB(deltaTime); +// int id = staticObjects.Count + i; +// _pointsX.Add(new IntervalPoint() { V = aabb.MinPoint.X, IsEnd = false, ColliderId = id }); +// _pointsX.Add(new IntervalPoint() { V = aabb.MaxPoint.X, IsEnd = true, ColliderId = id }); +// _pointsY.Add(new IntervalPoint() { V = aabb.MinPoint.Y, IsEnd = false, ColliderId = id }); +// _pointsY.Add(new IntervalPoint() { V = aabb.MaxPoint.Y, IsEnd = true, ColliderId = id }); +// _colliders.Add(obj.Collider); +// } + +// Comparison cmp = (a, b) => +// { +// int c = a.V.CompareTo(b.V); +// if (c == 0) +// { +// if (a.IsEnd && !b.IsEnd) +// { +// return -1; +// } +// if (!a.IsEnd && b.IsEnd) +// { +// return 1; +// } +// return 0; +// } +// return c; +// }; +// _pointsX.Sort(cmp); +// _pointsY.Sort(cmp); +// } +// } +//} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs new file mode 100644 index 000000000..9ce31289c --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs @@ -0,0 +1,489 @@ +using Everglow.Commons.Physics.PBEngine.GameInteraction; +using Everglow.Commons.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization.Formatters; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using static Terraria.ModLoader.PlayerDrawLayer; + +namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders +{ + /// + /// 四边形、盒子碰撞体 + /// + public class BoxCollider : Collider2D + { + public Vector2 Size + { + get => new Vector2(_width, _height); + } + private float _width; + private float _height; + public BoxCollider(float width, float height) + { + _width = width; + _height = height; + } + + public override double InertiaTensor(float mass) + { + return mass / 12.0 * (_width * _width + _height * _height); + } + + public override List GetWireFrameWires() + { + List wires = new List + { + new Vector2(-_width / 2, -_height / 2), + new Vector2(_width / 2, -_height / 2), + new Vector2(_width / 2, -_height / 2), + new Vector2(_width / 2, _height / 2), + new Vector2(_width / 2, _height / 2), + new Vector2(-_width / 2, _height / 2), + new Vector2(-_width / 2, _height / 2), + new Vector2(-_width / 2, -_height / 2) + }; + return wires; + } + + public List GetEdges(float dt) + { + List cornerWorldSpace = GetCornerPoints(dt); + List edges = new List(); + for (int i = 0; i < 4; i++) + { + edges.Add(new Edge2D(cornerWorldSpace[i], cornerWorldSpace[(i + 1) % cornerWorldSpace.Count])); + } + return edges; + + } + + //public override List GetCollisionEvents(Collider2D other, float deltaTime) + //{ + // List events = new List(); + + // Vector2[] localPoints = new Vector2[4] + // { + // new Vector2(-_width / 2, -_height / 2), + // new Vector2(_width / 2, -_height / 2), + // new Vector2(_width / 2, _height / 2), + // new Vector2(-_width / 2, _height / 2) + // }; + // if (other is AABBCollider) + // { + // AABBCollider aabb_other = (AABBCollider)other; + // for (int i = 0; i < 4; i++) + // { + // var curPos = _bindObject.Position + Matrix2x2.CreateRotationMatrix(_bindObject.Rotation).Multiply(localPoints[i]); + // var oldPos = _bindObject.OldPosition + Matrix2x2.CreateRotationMatrix(_bindObject.OldRotation).Multiply(localPoints[i]); + + // foreach (var edge in aabb_other.GetEdges()) + // { + // CollisionEvent2D collision; + // if (edge.Segment_Segment_Collision(oldPos, curPos, out collision)) + // { + // collision.Source = this.ParentObject; + // collision.Target = other.ParentObject; + // collision.LocalOffsetSrc = Matrix2x2.CreateRotationMatrix(_bindObject.Rotation).Multiply(localPoints[i]); + + // events.Add(collision); + // } + // } + // } + // } + // if (events.Count > 1) + // { + // Vector2 averagedLocalPoint = new Vector2(0, 0); + // Vector2 averagedNormal = new Vector2(0, 0); + // float minTime = 1.0f; + // for (int i = 0; i < events.Count; i++) + // { + // averagedLocalPoint += events[i].LocalOffsetSrc; + // averagedNormal += events[i].Normal; + // minTime = Math.Min(minTime, events[i].Time); + // } + // averagedLocalPoint /= events.Count; + // averagedNormal /= events.Count; + // CollisionEvent2D collision = new CollisionEvent2D(); + // collision.Source = this.ParentObject; + // collision.Target = other.ParentObject; + // collision.LocalOffsetSrc = averagedLocalPoint; + // collision.Normal = Vector2.Normalize(averagedNormal); + // collision.Time = minTime; + + // events.Clear(); + // events.Add(collision); + // } + // return events; + //} + + public List GetCornerPoints(float dt) + { + List cornerPoints = new List(); + Vector2[] localPoints = new Vector2[4] + { + new Vector2(-_width / 2, -_height / 2), + new Vector2(_width / 2, -_height / 2), + new Vector2(_width / 2, _height / 2), + new Vector2(-_width / 2, _height / 2) + }; + if (_bindObject.RigidBody != null) + { + var M = Matrix2x2.CreateRotationMatrix(_bindObject.OldRotation + _bindObject.RigidBody.AngularVelocity * dt); + for (int i = 0; i < 4; i++) + { + + var curPos = _bindObject.OldPosition + _bindObject.RigidBody.LinearVelocity * dt + + M.Multiply(localPoints[i]); + cornerPoints.Add(curPos); + } + } + else + { + var M = Matrix2x2.CreateRotationMatrix(_bindObject.Rotation); + for (int i = 0; i < 4; i++) + { + + var curPos = _bindObject.Position + + M.Multiply(localPoints[i]); + cornerPoints.Add(curPos); + } + + } + + return cornerPoints; + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + info = new CollisionInfo(); + if (other is BoxCollider) + { + BoxCollider b = (BoxCollider)other; + List edgesA = GetEdges(deltaTime); + List edgesB = b.GetEdges(deltaTime); + List cornersA = GetCornerPoints(deltaTime); + List cornersB = b.GetCornerPoints(deltaTime); + float depth; + Vector2 normal; + if (GeometryUtils.PolygonPolygonCollisionInfo(edgesA, cornersA, edgesB, cornersB, out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is SphereCollider) + { + SphereCollider b = (SphereCollider)other; + float depth; + Vector2 normal; + bool collided = GeometryUtils.CirclePolygonCollisionInfo(b.ParentObject.Position, b.Radius, GetEdges(deltaTime), + GetCornerPoints(deltaTime), + out depth, out normal); + if (collided) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + } + return collided; + } + else if (other is TileCollider) + { + float depth; + Vector2 normal; + if (TileCollisionUtils.GetPolygonTileCollisionInfo(GetAABB(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, + GetEdges(deltaTime), GetCornerPoints(deltaTime), out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = other.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - other.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + return false; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, + out List collisionEvents) + { + collisionEvents = new List(); + if (info.Target.Collider is BoxCollider) + { + BoxCollider b = (BoxCollider)info.Target.Collider; + List edgesA = GetEdges(deltaTime); + List edgesB = b.GetEdges(deltaTime); + List cornersA = GetCornerPoints(deltaTime); + List cornersB = b.GetCornerPoints(deltaTime); + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = info.Source, + Target = info.Target, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + List> contacts = new List>(); + GeometryUtils.PolygonPolygonContactInfo(edgesA, cornersA, edgesB, cornersB, + ParentObject.RigidBody.CentroidWorldSpace, b.ParentObject.RigidBody.CentroidWorldSpace, + contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is SphereCollider) + { + SphereCollider b = (SphereCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = 0, + Source = info.Source, + Target = info.Target, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + + List> contacts = new List>(); + GeometryUtils.CirclePolygonContactInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, GetEdges(deltaTime), + GetCornerPoints(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Value, + LocalOffsetTarget = c.Key, + }); + } + + } + else if (info.Target.Collider is TileCollider) + { + TileCollisionUtils.GetPolygonTileContactInfo(GetAABB(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, + GetEdges(deltaTime), GetCornerPoints(deltaTime), ParentObject, info.Target, deltaTime, collisionEvents); + } + } + + public override AABB GetAABB(float deltaTime) + { + var points = GetCornerPoints(deltaTime); + Vector2 minPoint = points[0]; + Vector2 maxPoint = points[0]; + for (int i = 1; i < points.Count; i++) + { + minPoint.X = Math.Min(points[i].X, minPoint.X); + minPoint.Y = Math.Min(points[i].Y, minPoint.Y); + maxPoint.X = Math.Max(points[i].X, maxPoint.X); + maxPoint.Y = Math.Max(points[i].Y, maxPoint.Y); + } + return new AABB() { MaxPoint = maxPoint, MinPoint = minPoint }; + } + } +} + + +//float maxDepth = -1; +//if (edgeBelongsToA) +//{ +// for (int i = 0; i < cornersB.Count; i++) +// { +// if (IsInsideEdge(cornersB[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersB[i], keyEdge._pA, keyEdge._pB); +// if (d > maxDepth) +// { +// maxDepth = d; +// } +// } +// } +// Debug.Assert(maxDepth >= 0); +// for (int i = 0; i < cornersB.Count; i++) +// { +// if (IsInsideEdge(cornersB[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersB[i], keyEdge._pA, keyEdge._pB); +// if (d == maxDepth) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = 0, +// Source = this.ParentObject, +// Target = b.ParentObject, +// LocalOffsetSrc = Utils.ProjectPointOnSegment(cornersB[i], keyEdge._pA, keyEdge._pB) +// - ParentObject.RigidBody.CentroidWorldSpace, +// LocalOffsetTarget = cornersB[i] - b.ParentObject.RigidBody.CentroidWorldSpace, +// Normal = normal, +// Position = Vector2.Zero, +// Depth = depth +// }); +// } +// } +// } + +//} +//else +//{ +// for (int i = 0; i < cornersA.Count; i++) +// { +// if (IsInsideEdge(cornersA[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersA[i], keyEdge._pA, keyEdge._pB); +// if (d > maxDepth) +// { +// maxDepth = d; +// } +// } +// } +// Debug.Assert(maxDepth >= 0); +// for (int i = 0; i < cornersA.Count; i++) +// { +// if (IsInsideEdge(cornersA[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersA[i], keyEdge._pA, keyEdge._pB); +// if (d == maxDepth) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = 0, +// Source = b.ParentObject, +// Target = this.ParentObject, +// LocalOffsetSrc = Utils.ProjectPointOnSegment(cornersA[i], keyEdge._pA, keyEdge._pB) +// - b.ParentObject.RigidBody.CentroidWorldSpace, +// LocalOffsetTarget = cornersA[i] - ParentObject.RigidBody.CentroidWorldSpace, +// Normal = -normal, +// Position = Vector2.Zero, +// Depth = depth +// }); +// } +// } +// } +//} +//collisionEvents.Add(new CollisionEvent2D() +//{ +// Time = 0, +// Source = this.ParentObject, +// Target = b.ParentObject, +// LocalOffsetSrc = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), +// LocalOffsetTarget = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), +// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), +// Position = curPoint, +//}); + +//AABBCollider b = (AABBCollider)other; +//double l = 0, r = deltaTime; +//double ans = r; +//while (r - l > deltaTime * 1e-2) +//{ +// double midTime = (r + l) / 2.0; +// if (IsCollideWith(b, (float)midTime)) +// { +// ans = Math.Min(ans, midTime); +// r = midTime; +// } +// else +// { +// l = midTime; +// } +//} + +//double t = Math.Max(0, ans); +//var curPointsA = GetCornerPoints((float)ans); +//var curEdgesA = GetEdges((float)ans); +//var curPointsB = b.GetCornerPoints((float)ans); +//var curEdgesB = b.GetEdges((float)ans); + +//// Vertex/Edge collision, A to B +//foreach (var curPoint in curPointsA) +//{ +// bool isInside = true; +// Edge2D closestEdge = null; +// float closestDistance = float.PositiveInfinity; +// foreach(var curEdge in curEdgesB) +// { +// if (!IsInsideEdge(curPoint, curEdge)) +// { +// isInside = false; +// break; +// } +// float dist = Utils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); +// if (dist < closestDistance) +// { +// closestDistance = dist; +// closestEdge = curEdge; +// } +// } + +// if(isInside) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = (float)(t), +// Source = this.ParentObject, +// Target = b.ParentObject, +// LocalOffsetSrc = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), +// LocalOffsetTarget = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), +// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), +// Position = curPoint, + +// }); +// } + +//} + +//// Vertex/Edge collision, B to A +//foreach (var curPoint in curPointsB) +//{ +// bool isInside = true; +// Edge2D closestEdge = null; +// float closestDistance = float.PositiveInfinity; +// foreach (var curEdge in curEdgesA) +// { +// if (!IsInsideEdge(curPoint, curEdge)) +// { +// isInside = false; +// break; +// } +// float dist = Utils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); +// if (dist < closestDistance) +// { +// closestDistance = dist; +// closestEdge = curEdge; +// } +// } + +// if (isInside) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = (float)(t), +// Source = b.ParentObject, +// Target = this.ParentObject, +// LocalOffsetSrc = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), +// LocalOffsetTarget = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), +// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), +// Position = curPoint, +// }); +// } + +//} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs new file mode 100644 index 000000000..c3c3381de --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders +{ + /// + /// 碰撞组件的基类 + /// + public abstract class Collider2D + { + public PhysicsObject ParentObject + { + get => _bindObject; + set => _bindObject = value; + } + + protected PhysicsObject _bindObject; + public abstract double InertiaTensor(float mass); + public abstract List GetWireFrameWires(); + // public abstract List GetCollisionEvents(Collider2D other, float deltaTime); + + public abstract bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info); + + public abstract void GetContactInfo(in CollisionInfo info, float deltaTime, + out List collisionEvents); + + //public abstract List GetContactInfo(CollisionEvent2D e, float deltaTime); + + public abstract AABB GetAABB(float deltaTime); + public Collider2D() + { + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs new file mode 100644 index 000000000..d60629b3a --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs @@ -0,0 +1,201 @@ +using Everglow.Commons.Physics.PBEngine.GameInteraction; +using Everglow.Commons.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders +{ + /// + /// 圆形碰撞体 + /// + public class SphereCollider : Collider2D + { + public float Radius + { + get => _radius; set => _radius = value; + } + private float _radius; + public SphereCollider(float radius) + { + _radius = radius; + } + public override AABB GetAABB(float deltaTime) + { + var center = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime; + return new AABB() { + MinPoint = center - new Vector2(_radius, _radius), + MaxPoint = center + new Vector2(_radius, _radius), + }; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) + { + collisionEvents = new List(); + if (info.Target.Collider is SphereCollider) + { + SphereCollider b = (SphereCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + + var unit = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.Zero); + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = -unit * _radius, + LocalOffsetTarget = unit * b.Radius, + }); + } + else if (info.Target.Collider is BoxCollider) + { + BoxCollider b = (BoxCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = info.Source, + Target = info.Target, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(info.Normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -info.Normal : info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + + List> contacts = new List>(); + GeometryUtils.CirclePolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, b.GetEdges(deltaTime), + b.GetCornerPoints(deltaTime), b.ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is TileCollider) + { + TileCollisionUtils.GetSphereTileContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, + ParentObject, info.Target, deltaTime, collisionEvents); + } + } + + //public override List GetContactInfo(CollisionEvent2D e, float deltaTime) + //{ + // List events = new List(); + // if (e.Target.Collider is SphereCollider) + // { + // SphereCollider b = (SphereCollider)e.Target.Collider; + // var unit = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.Zero); + // events.Add(new CollisionEvent2D(e) + // { + // LocalOffsetSrc = -unit * _radius, + // LocalOffsetTarget = unit * b.Radius, + // }); + // } + // else if (e.Target.Collider is BoxCollider) + // { + // BoxCollider b = (BoxCollider)e.Target.Collider; + // List edgesB = b.GetEdges(deltaTime); + // List cornersB = b.GetCornerPoints(deltaTime); + // List> contacts = new List>(); + // Utils.CirclePolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, edgesB, + // cornersB, b.ParentObject.RigidBody.CentroidWorldSpace, contacts); + // foreach (var c in contacts) + // { + // events.Add(new CollisionEvent2D(e) + // { + // LocalOffsetSrc = c.Key, + // LocalOffsetTarget = c.Value, + // }); + // } + // } + // else if (e.Target.Collider is TileCollider) + // { + // TileCollisionUtils.GetSphereTileContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, e, events); + // } + // return events; + //} + + public override List GetWireFrameWires() + { + List lines = new List(); + float da = MathHelper.TwoPi / 64; + for (int i = 0; i < 64; i++) + { + lines.Add((i * da).ToRotationVector2() * _radius); + lines.Add(((i + 1) * da).ToRotationVector2() * _radius); + } + lines.Add(Vector2.Zero); + lines.Add(Matrix2x2.CreateRotationMatrix(ParentObject.Rotation).Multiply(new Vector2(_radius, 0))); + return lines; + } + + public override double InertiaTensor(float mass) + { + return 0.5 * mass * _radius * _radius; + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + info = new CollisionInfo(); + if (other is SphereCollider) + { + SphereCollider b = (SphereCollider)other; + float d = Vector2.DistanceSquared(ParentObject.RigidBody.CentroidWorldSpace, b.ParentObject.RigidBody.CentroidWorldSpace); + if (d < (_radius + b._radius) * (_radius + b._radius)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = _radius + b._radius - (float)Math.Sqrt(d); + info.Normal = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.One); + return true; + } + } + else if (other is BoxCollider) + { + BoxCollider b = (BoxCollider)other; + float depth; + Vector2 normal; + if (GeometryUtils.CirclePolygonCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, + b.GetEdges(deltaTime), b.GetCornerPoints(deltaTime), out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is TileCollider) + { + float depth; + Vector2 normal; + if (TileCollisionUtils.GetSphereTileCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, + ParentObject, info.Target, out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = other.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = normal; + return true; + } + } + return false; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs new file mode 100644 index 000000000..1bf3abbaf --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs @@ -0,0 +1,65 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision +{ + /// + /// 储存碰撞事件的对象,包括接触点信息 + /// + public class CollisionEvent2D + { + public PhysicsObject Source; + public PhysicsObject Target; + public float Time; + public float Depth; + public Vector2 Position; + public Vector2 Normal; + public Vector2 Offset; + + /// + /// From centroid of source object to hit point + /// + public Vector2 LocalOffsetSrc; + + /// + /// From centroid of target object to hit point + /// + public Vector2 LocalOffsetTarget; + public float NormalVelOld; + + public CollisionEvent2D(CollisionEvent2D e) + { + this.Source = e.Source; + this.Target = e.Target; + this.Time = e.Time; + this.Depth = e.Depth; + this.Position = e.Position; + this.Normal = e.Normal; + this.Offset = e.Offset; + this.NormalVelOld = e.NormalVelOld; + } + + public CollisionEvent2D() + { + } + + public CollisionEvent2D CreateMirrorEvent() + { + return new CollisionEvent2D() + { + Source = this.Target, + Target = this.Source, + Time = this.Time, + Position = this.Position, + Normal = -this.Normal, + Offset = -this.Offset, + LocalOffsetSrc = this.LocalOffsetTarget, + LocalOffsetTarget = this.LocalOffsetSrc + }; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionInfo.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionInfo.cs new file mode 100644 index 000000000..2cbe19e95 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionInfo.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision +{ + /// + /// 储存碰撞事件的对象,包括穿透深度和挤出方向,不包括接触点 + /// + public struct CollisionInfo + { + public PhysicsObject Source; + public PhysicsObject Target; + public float Time; + public float Depth; + public Vector2 Normal; + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Edge2D.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Edge2D.cs new file mode 100644 index 000000000..bf0824a82 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/Edge2D.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Everglow.Commons.Utilities; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Collision +{ + public class Edge2D + { + public Vector2 _pA, _pB; + public Edge2D() + { + + } + + public Edge2D(Vector2 A, Vector2 B) + { + _pA = A; + _pB = B; + } + + public Vector2 GetNormal() + { + return -GeometryUtils.Rotate90((_pB - _pA).SafeNormalize(Vector2.Zero)); + } + + public bool Segment_Segment_Collision(Vector2 a, Vector2 b, out CollisionEvent2D collisionEvent) + { + Matrix2x2 M = new Matrix2x2 + { + [0, 0] = _pB.X - _pA.X, + [0, 1] = -(b.X - a.X), + [1, 0] = _pB.Y - _pA.Y, + [1, 1] = -(b.Y - a.Y), + }; + + double det = M.Determinant(); + if (Math.Abs(det) < 1e-7) + { + collisionEvent = new CollisionEvent2D(); + return false; // parallel lines + } + else + { + + Vector2 st = M.Adjoint().Multiply(a - _pA) / (float)det; + if (st.X >= 0 && st.X <= 1.0f && st.Y >= 0 && st.Y <= 1.0f) + { + collisionEvent = new CollisionEvent2D + { + Time = st.Y, + Position = _pA + (_pB - _pA) * st.X, + Normal = -GeometryUtils.Rotate90((_pB - _pA).SafeNormalize(Vector2.Zero)), + Offset = Vector2.Zero + }; + return true; + } + collisionEvent = new CollisionEvent2D(); + return false; // intersection is outside of the line segments + } + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/ImpluseEntry.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/ImpluseEntry.cs new file mode 100644 index 000000000..8c9449e0c --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/ImpluseEntry.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision +{ + public struct ImpluseEntry + { + public RigidBody2D Source; + public RigidBody2D Target; + public Vector2 ImpluseSource; + public Vector2 RelativePositionSource; + public Vector2 ImpluseTarget; + public Vector2 RelativePositionTarget; + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/MovementType.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/MovementType.cs new file mode 100644 index 000000000..fcb1f8c5e --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/MovementType.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision +{ + /// + /// 刚体的移动模式,用于区分模拟方法以及提升性能 + /// + public enum MovementType + { + /// + /// 静态物体,无法移动、转向 + /// + Static, + /// + /// 动态物体,但是拥有无限质量,动作无法被其他动态物体影响 + /// + Kinematic, + /// + /// 玩家物体,动态,具有质量但是不可旋转 + /// + Player, + /// + /// 动态物体,可以任意移动和旋转,可以被任何物体影响 + /// + Dynamic + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs b/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs new file mode 100644 index 000000000..3ec8fbbc5 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Constrains +{ + public abstract class Constrain + { + public abstract void Apply(float deltaTime); + + public abstract List GetDrawMesh(); + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs b/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs new file mode 100644 index 000000000..7c1a8283d --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Constrains +{ + /// + /// TODO: 待定约束 + /// + public class JointConstrain : Constrain + { + private PhysicsObject _objA; + private PhysicsObject _objB; + private Vector2 _localPosA; + private Vector2 _localPosB; + public JointConstrain(PhysicsObject A, PhysicsObject B, Vector2 localPos1, Vector2 localPos2) + { + _objA = A; + _objB = B; + _localPosA = localPos1; + _localPosB = localPos2; + } + + public override void Apply(float deltaTime) + { + var pA = _objA.LocalToWorldPos(_localPosA); + // var pB = _objB.LocalToWorldPos(_localPosB); + float w = _objA.RigidBody.InvMass + _objB.RigidBody.InvMass; + float weightA = _objA.RigidBody.InvMass / w; + var newCenter = pA; + var dirA = (newCenter - _objA.Position).SafeNormalize(Vector2.One); + var dirB = (newCenter - _objB.Position).SafeNormalize(Vector2.One); + + float oldRotA = MathHelper.WrapAngle(_objA.Rotation); + float oldRotB = MathHelper.WrapAngle(_objB.Rotation); + //_objA.Rotation = (float)(Math.Atan2(dirA.Y, dirA.X) - Math.Atan2(_localPosA.Y, _localPosA.X)); + //_objB.Rotation = (float)(Math.Atan2(dirB.Y, dirB.X) - Math.Atan2(_localPosB.Y, _localPosB.X)); + + Vector2 addA = newCenter - _objA.LocalToWorldPos(_localPosA); + Vector2 addB = newCenter - _objB.LocalToWorldPos(_localPosB); + Vector2 oldPosB = _objB.Position; + _objA.Position += addA; + _objB.Position = (newCenter - dirB * 200); + + _objA.RigidBody.LinearVelocity = (_objA.Position - _objA.OldPosition) / deltaTime; + _objB.RigidBody.LinearVelocity = (_objB.Position - _objB.OldPosition) / deltaTime; + //_objA.RigidBody.AngularVelocity = MathHelper.WrapAngle(_objA.Rotation - _objA.OldRotation) / deltaTime; + //_objB.RigidBody.AngularVelocity = MathHelper.WrapAngle(_objB.Rotation - _objB.OldRotation) / deltaTime; + } + + public override List GetDrawMesh() + { + List drawMesh = new List(); + drawMesh.Add(_objA.LocalToWorldPos(_localPosA)); + drawMesh.Add(_objB.RigidBody.CentroidWorldSpace); + + return drawMesh; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs b/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs new file mode 100644 index 000000000..ed2447b90 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Everglow.Commons.Utilities; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Constrains +{ + /// + /// 弹簧约束 + /// + public class SpringConstrain : Constrain + { + private PhysicsObject _objA; + private PhysicsObject _objB; + private float _compliance; + private float _restLength; + private float _lambda; + private Vector2 _localPosA; + private Vector2 _localPosB; + + public SpringConstrain(PhysicsObject A, PhysicsObject B, float elasticity, float restLength) + { + _objA = A; + _objB = B; + _compliance = 1 / elasticity; + _restLength = restLength; + _lambda = 0; + _localPosA = Vector2.Zero; + _localPosB = Vector2.Zero; + } + + public SpringConstrain(PhysicsObject A, PhysicsObject B, float elasticity, float restLength, + Vector2 localPosA, + Vector2 localPosB) : this(A, B, elasticity, restLength) + { + _localPosA = localPosA; + _localPosB = localPosB; + } + + public override void Apply(float deltaTime) + { + var pA = _objA.LocalToWorldPos(_localPosA); + var pB = _objB.LocalToWorldPos(_localPosB); + var L = (pB - pA).Length(); + var unit = (pB - pA).SafeNormalize(Vector2.Zero); + float C = L - _restLength; + float alpha = _compliance / deltaTime / deltaTime; + float d_lambda = (-C - alpha * _lambda) / (_objA.RigidBody.InvMass + _objB.RigidBody.InvMass + alpha); + + _lambda += d_lambda; + var curPosA = pA + _lambda * _objA.RigidBody.InvMass * -unit; + var curPosB = pB + _lambda * _objB.RigidBody.InvMass * unit; + + var ra = Matrix2x2.CreateRotationMatrix(_objA.Rotation).Multiply(_localPosA); + var rb = Matrix2x2.CreateRotationMatrix(_objB.Rotation).Multiply(_localPosB); + var da = (_lambda * _objA.RigidBody.InvMass * -unit).SafeNormalize(Vector2.Zero); + var db = (_lambda * _objB.RigidBody.InvMass * unit).SafeNormalize(Vector2.Zero); + var oldVelA = Vector2.Dot(_objA.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ra, _objA.RigidBody.AngularVelocity), da); + var oldVelB = Vector2.Dot(_objB.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, _objB.RigidBody.AngularVelocity), db); + + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ra), da); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), db); + double R1 = rAdotN * rAdotN * _objA.RigidBody.GlobalInverseInertiaTensor; + double R2 = rBdotN * rBdotN * _objB.RigidBody.GlobalInverseInertiaTensor; + var impluseA = ((curPosA - pA).Length() / deltaTime - oldVelA) / (_objA.RigidBody.InvMass + (float)R1); + var impluseB = ((curPosB - pB).Length() / deltaTime - oldVelB) / (_objB.RigidBody.InvMass + (float)R2); + + if (_objA.RigidBody.InvMass != 0) + { + _objA.RigidBody.AddImpluseImmediate(impluseA * da, ra); + _objA.Position += _objA.RigidBody.LinearVelocity * deltaTime; + _objA.Rotation += _objA.RigidBody.AngularVelocity * deltaTime; + } + if (_objB.RigidBody.InvMass != 0) + { + _objB.RigidBody.AddImpluseImmediate(impluseB * db, rb); + _objB.Position += _objB.RigidBody.LinearVelocity * deltaTime; + _objB.Rotation += _objB.RigidBody.AngularVelocity * deltaTime; + } + //_objA.RigidBody.AngularVelocity = Utils.Cross(_localPosA, _objA.RigidBody.LinearVelocity); + //_objB.RigidBody.AngularVelocity = Utils.Cross(_localPosB, _objB.RigidBody.LinearVelocity); + } + + public override List GetDrawMesh() + { + List drawMesh = new List(); + drawMesh.Add(_objA.LocalToWorldPos(_localPosA)); + drawMesh.Add(_objB.LocalToWorldPos(_localPosB)); + + return drawMesh; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs new file mode 100644 index 000000000..2cf5b8b38 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs @@ -0,0 +1,55 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.GameInteraction +{ + /// + /// 与TR世界地形碰撞的碰撞体 + /// + public class TileCollider : Collider2D + { + public override AABB GetAABB(float deltaTime) + { + return new AABB() + { + MinPoint = new Vector2(float.NegativeInfinity, float.NegativeInfinity), + MaxPoint = new Vector2(float.PositiveInfinity, 0) + }; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) + { + // Leave empty since static object will not serve as collision source + + collisionEvents = new List(); + } + + //public override List GetContactInfo(CollisionEvent2D e, float deltaTime) + //{ + // return new List(); + // // Leave empty since static object will not serve as collision source + //} + + public override List GetWireFrameWires() + { + return new List(); + } + + public override double InertiaTensor(float mass) + { + return 1; + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + // Leave empty since static object will not serve as collision source + info = new CollisionInfo(); + return false; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs new file mode 100644 index 000000000..c521d1859 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs @@ -0,0 +1,300 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using static Humanizer.In; + +namespace Everglow.Commons.Physics.PBEngine.GameInteraction +{ + /// + /// 与TR地形进行碰撞检测的函数 + /// + public class TileCollisionUtils + { + public static List GenerateHalfBrick(int x, int y) + { + var slope = Main.tile[x, y].Slope; + if (slope == Terraria.ID.SlopeType.Solid) + { + if (Main.tile[x, y].IsHalfBlock) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 8 ), + new Vector2(x * 16, -y * 16 - 8) + }; + } + else + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16) + }; + } + } + else if (slope == Terraria.ID.SlopeType.SlopeDownLeft) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16, -y * 16) + }; + } + else if (slope == Terraria.ID.SlopeType.SlopeDownRight) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ) + }; + } + else if (slope == Terraria.ID.SlopeType.SlopeUpLeft) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16) + }; + } + else if (slope == Terraria.ID.SlopeType.SlopeUpRight) + { + return new List() + { + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16) + }; + } + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16) + }; + } + + public static bool GetPolygonTileCollisionInfo(AABB boundingBox, Vector2 polyCenter, List edges, List corners, + out float depth1, out Vector2 normal1) + { + bool collided = false; + float maxDepth = 0; + Vector2 maxNormal = Vector2.Zero; + int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); + int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edgesTile = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edgesTile.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.PolygonPolygonCollisionInfo(edges, corners, edgesTile, localPoints, + out depth, out normal)) + { + collided = true; + + if (depth > maxDepth) + { + maxDepth = depth; + maxNormal = Vector2.Dot(maxNormal, polyCenter - centerTile) < 0 ? -normal : normal; + } + } + } + } + } + depth1 = 0; + normal1 = maxNormal; + return collided; + } + public static void GetPolygonTileContactInfo(AABB boundingBox, Vector2 polyCenter, List edges, + List corners, + PhysicsObject A, PhysicsObject B, + float deltaTime, + List outputList) + { + + Vector2 maxNormal = Vector2.Zero; + int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); + int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edgesTile = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edgesTile.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.PolygonPolygonCollisionInfo(edges, corners, edgesTile, localPoints, + out depth, out normal)) + { + var e = new CollisionEvent2D() + { + Time = 0, + Source = A, + Target = B, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(normal, polyCenter - centerTile) < 0 ? -normal : normal, + Position = Vector2.Zero, + Depth = depth, + }; + float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + float weightB = e.Target.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); + e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); + + List> contacts = new List>(); + GeometryUtils.PolygonPolygonContactInfo(edges, corners, edgesTile, localPoints.ToList(), polyCenter, + centerTile, contacts); + foreach (var c in contacts) + { + outputList.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + } + } + } + } + + public static bool GetSphereTileCollisionInfo(Vector2 center, float radius, PhysicsObject A, PhysicsObject B, + out float depth1, out Vector2 normal1) + { + bool collided = false; + float maxDepth = 0; + Vector2 maxNormal = Vector2.Zero; + int minX = (int)Math.Floor((center.X - radius) / 16); + int maxX = (int)Math.Ceiling((center.X + radius) / 16); + int minY = (int)Math.Floor((-center.Y - radius) / 16); + int maxY = (int)Math.Ceiling((-center.Y + radius) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.CirclePolygonIsCollide(center, radius, edges, + Vector2.Zero)) + { + collided = true; + GeometryUtils.CirclePolygonCollisionInfo(center, radius, edges, localPoints.ToList(), out depth, out normal); + + if (depth > maxDepth) + { + maxDepth = depth; + maxNormal = Vector2.Dot(maxNormal, center - centerTile) < 0 ? -normal : normal; + } + } + } + } + } + depth1 = 0; + normal1 = maxNormal; + return collided; + } + public static void GetSphereTileContactInfo(Vector2 center, float radius, PhysicsObject A, PhysicsObject B, + float deltaTime, + List outputList) + { + int minX = (int)Math.Floor((center.X - radius) / 16); + int maxX = (int)Math.Ceiling((center.X + radius) / 16); + int minY = (int)Math.Floor((-center.Y - radius) / 16); + int maxY = (int)Math.Ceiling((-center.Y + radius) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.CirclePolygonCollisionInfo(center, radius, edges, localPoints.ToList(), out depth, out normal)) + { + var e = new CollisionEvent2D() + { + Time = 0, + Source = A, + Target = B, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(normal, center - centerTile) < 0 ? -normal : normal, + Position = Vector2.Zero, + Depth = depth, + }; + float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + float weightB = e.Target.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); + e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); + + List> contacts = new List>(); + GeometryUtils.CirclePolygonContactInfo(e.Source.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), centerTile, contacts); + foreach (var c in contacts) + { + outputList.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + } + } + } + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs b/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs new file mode 100644 index 000000000..e2a4747c4 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs @@ -0,0 +1,401 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine +{ + public static class GeometryUtils + { + public static bool ApproxEqual(float a, float b) + { + return Math.Abs(a - b) < 1e-6; + } + /// + /// Compute ab^T + /// + /// + /// + /// + public static Matrix3x3 OuterProduct(Vector3 a, Vector3 b) + { + return new Matrix3x3 + { + [0, 0] = a.X * b.X, + [0, 1] = a.X * b.Y, + [0, 2] = a.X * b.Z, + [1, 0] = a.Y * b.X, + [1, 1] = a.Y * b.Y, + [1, 2] = a.Y * b.Z, + [2, 0] = a.Z * b.X, + [2, 1] = a.Z * b.Y, + [2, 2] = a.Z * b.Z + }; + } + + /// + /// Compute ab^T + /// + /// + /// + /// + public static Matrix2x2 OuterProduct(Vector2 a, Vector2 b) + { + return new Matrix2x2 + { + [0, 0] = a.X * b.X, + [0, 1] = a.X * b.Y, + [1, 0] = a.Y * b.X, + [1, 1] = a.Y * b.Y, + }; + } + + + public static float Cross(this Vector2 a, Vector2 b) + { + return a.X * b.Y - a.Y * b.X; + } + + + public static void Blit(Texture2D source, RenderTarget2D dest, Effect effect, string pass) + { + var sb = Main.spriteBatch; + var gd = Main.graphics.GraphicsDevice; + gd.SetRenderTarget(dest); + gd.Clear(Color.Transparent); + if (effect != null) + { + sb.Begin(SpriteSortMode.Immediate, BlendState.Opaque); + effect.CurrentTechnique.Passes[pass].Apply(); + sb.Draw(source, dest.Bounds, Color.White); + sb.End(); + } + else + { + sb.Begin(); + sb.Draw(source, dest.Bounds, Color.White); + sb.End(); + } + + } + + /// + /// The linear velocity corresponding to given angular velocity and arm + /// + /// + /// + public static Vector2 AnuglarVelocityToLinearVelocity(Vector2 a, float w) + { + return new Vector2(-a.Y, a.X) * w; + } + + public static Vector2 Rotate90(Vector2 a) + { + return new Vector2(-a.Y, a.X); + } + + public static Vector2 ProjectPointOnSegment(Vector2 p, Vector2 start, Vector2 end) + { + var u = (end - start).SafeNormalize(Vector2.Zero); + return start + Vector2.Dot(u, p - start) * u; + } + + public static float PointSignedDistanceToLine(Vector2 p, Vector2 start, Vector2 end) + { + Vector2 ab = end - start; + Vector2 ax = p - start; + + float crossProductMagnitude = GeometryUtils.Cross(ab, ax); + float ABLength = ab.Length(); + + return -crossProductMagnitude / ABLength; + } + + public static float PointSignedDistanceToSegment(Vector2 p, Vector2 start, Vector2 end) + { + var u = (end - start).SafeNormalize(Vector2.Zero); + float d = Vector2.Dot(u, p - start); + float sign = GeometryUtils.Cross(u, p - start) > 0 ? -1 : 1; + if (d <= 0) + { + return Vector2.Distance(p, start) * sign; + } + else if (d >= Vector2.Distance(start, end)) + { + return Vector2.Distance(p, end) * sign; + } + return (p - (start + d * u)).Length() * sign; + } + + public static float PointDistance2ToSegment(Vector2 p, Vector2 start, Vector2 end) + { + var u = (end - start).SafeNormalize(Vector2.Zero); + float d = Vector2.Dot(u, p - start); + if (d <= 0) + { + return Vector2.DistanceSquared(p, start); + } + else if (d * d >= Vector2.DistanceSquared(start, end)) + { + return Vector2.DistanceSquared(p, end); + } + return (p - (start + d * u)).LengthSquared(); + } + public static float PointDistance2ToSegmentWithClip(Vector2 p, Vector2 start, Vector2 end) + { + var u = (end - start).SafeNormalize(Vector2.Zero); + float d = Vector2.Dot(u, p - start); + if (d <= 0) + { + return float.PositiveInfinity; + } + else if (d * d >= Vector2.DistanceSquared(start, end)) + { + return float.PositiveInfinity; + } + return (p - (start + d * u)).LengthSquared(); + } + + public static void CirclePolygonContactInfo(Vector2 cSphere, float radius, List edges, + List corners, Vector2 centerPoly, + List> localPositions) + { + float minDepth = float.PositiveInfinity; + foreach (var edge in edges) + { + float d = GeometryUtils.PointDistance2ToSegmentWithClip(cSphere, edge._pA, edge._pB); + if (d < minDepth) + { + minDepth = d; + } + } + foreach (var v in corners) + { + float d = Vector2.DistanceSquared(v, cSphere); + if (d < minDepth) + { + minDepth = d; + } + } + + foreach (var edge in edges) + { + float d = GeometryUtils.PointDistance2ToSegmentWithClip(cSphere, edge._pA, edge._pB); + if (d == minDepth) + { + var p = GeometryUtils.ProjectPointOnSegment(cSphere, edge._pA, edge._pB); + localPositions.Add(new KeyValuePair( + (p - cSphere).SafeNormalize(Vector2.Zero) * radius, + p - centerPoly + )); + } + } + foreach (var v in corners) + { + float d = Vector2.DistanceSquared(v, cSphere); + if (d == minDepth) + { + localPositions.Add(new KeyValuePair( + (v - cSphere).SafeNormalize(Vector2.Zero) * radius, + v - centerPoly + )); + } + } + } + + public static bool CirclePolygonCollisionInfo(Vector2 cSphere, float radius, List edges, List corners, + out float depth, out Vector2 normal) + { + float maxDepth = float.NegativeInfinity; + float maxDepth2 = float.NegativeInfinity; + Edge2D keyEdge = null; + foreach (var edge in edges) + { + float d = GeometryUtils.PointSignedDistanceToLine(cSphere, edge._pA, edge._pB); + float d2 = GeometryUtils.PointSignedDistanceToSegment(cSphere, edge._pA, edge._pB); + if (d2 > maxDepth2) + { + maxDepth2 = d2; + } + if (d > maxDepth) + { + maxDepth = d; + keyEdge = edge; + } + } + + normal = keyEdge.GetNormal(); + depth = radius - maxDepth; + if (maxDepth2 > radius) + { + return false; + } + + foreach (var corner in corners) + { + float d = Vector2.Distance(corner, cSphere); + if (d <= radius && d > maxDepth) + { + maxDepth = d; + normal = Vector2.Normalize(cSphere - corner); + depth = radius - maxDepth; + } + } + return maxDepth < radius; + } + + public static bool CirclePolygonIsCollide(Vector2 cSphere, float radius, List edges, Vector2 centerPoly) + { + float maxDepth = float.NegativeInfinity; + foreach (var edge in edges) + { + float d = GeometryUtils.PointSignedDistanceToSegment(cSphere, edge._pA, edge._pB); + if (d > maxDepth) + { + maxDepth = d; + } + } + return maxDepth < radius; + } + + private static (float, float) GetProjectedInterval(List vertices, Vector2 start, Vector2 dir) + { + float v = Vector2.Dot(dir, vertices[0] - start); + float minA = v, maxA = v; + for (int i = 1; i < vertices.Count; i++) + { + float x = Vector2.Dot(dir, vertices[i] - start); + minA = Math.Min(minA, x); + maxA = Math.Max(maxA, x); + } + return (minA, maxA); + } + + public static bool PolygonPolygonCollisionInfo(List edgesA, List cornersA, + List edgesB, List cornersB, out float depth, out Vector2 normal) + { + depth = float.PositiveInfinity; + normal = Vector2.Zero; + for (int i = 0; i < edgesA.Count; i++) + { + Edge2D edge = edgesA[i]; + Vector2 axis = edge.GetNormal(); + + var (amin, amax) = GetProjectedInterval(cornersA, edge._pA, axis); + var (bmin, bmax) = GetProjectedInterval(cornersB, edge._pA, axis); + + if (Math.Max(amin, bmin) > Math.Min(amax, bmax)) + { + return false; + } + + float d = Math.Min(amax, bmax) - Math.Max(amin, bmin); + if (d < depth) + { + depth = d; + normal = axis; + } + } + for (int i = 0; i < edgesB.Count; i++) + { + Edge2D edge = edgesB[i]; + Vector2 axis = edge.GetNormal(); + + var (amin, amax) = GetProjectedInterval(cornersA, edge._pA, axis); + var (bmin, bmax) = GetProjectedInterval(cornersB, edge._pA, axis); + + if (Math.Max(amin, bmin) > Math.Min(amax, bmax)) + { + return false; + } + + float d = Math.Min(amax, bmax) - Math.Max(amin, bmin); + if (d < depth) + { + depth = d; + normal = axis; + } + } + return true; + } + + public static void PolygonPolygonContactInfo(List edgesA, List cornersA, + List edgesB, List cornersB, + Vector2 polyCenterA, Vector2 polyCenterB, List> localPositions) + { + float closestDistance = float.PositiveInfinity; + foreach (var curPoint in cornersA) + { + foreach (var curEdge in edgesB) + { + float dist = GeometryUtils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); + if (dist < closestDistance) + { + closestDistance = dist; + localPositions.Clear(); + localPositions.Add(new KeyValuePair( + curPoint - polyCenterA, + GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) + - polyCenterB + )); + } + else if (Math.Abs(dist - closestDistance) < 1e-6) + { + localPositions.Add(new KeyValuePair( + curPoint - polyCenterA, + GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) + - polyCenterB + )); + } + } + } + foreach (var curPoint in cornersB) + { + foreach (var curEdge in edgesA) + { + float dist = GeometryUtils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); + if (dist < closestDistance) + { + closestDistance = dist; + localPositions.Clear(); + localPositions.Add(new KeyValuePair( + GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterA, + curPoint - polyCenterB + )); + } + else if (Math.Abs(dist - closestDistance) < 1e-6) + { + localPositions.Add(new KeyValuePair( + GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterA, + curPoint - polyCenterB + )); + } + } + } + } + + public static Vector2 ConvertToPhysicsSpace(Vector2 pos) + { + return new Vector2(pos.X, -pos.Y); + } + + private static Random rng = new Random(); + + public static void FisherYatesShuffle(this IList list) + { + int n = list.Count; + while (n > 1) + { + n--; + int k = rng.Next(n + 1); + T value = list[k]; + list[k] = list[n]; + list[n] = value; + } + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs new file mode 100644 index 000000000..e81ba2bf2 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs @@ -0,0 +1,162 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine +{ + /// + /// 物理对象的容器,可以容纳Rigidbody,Collider等组件 + /// 此外,它还是物理模拟的主体 + /// + public class PhysicsObject + { + /// + /// 物理对象的几何组件,用于碰撞检测和质心计算等 + /// + public Collider2D Collider + { + get => _collider; + } + + /// + /// 物理对象的刚体物理组件,用于存储和模拟刚体数据 + /// + public RigidBody2D RigidBody + { + get => _rigidBody; + } + + /// + /// 物体的质心位置 + /// + public Vector2 Position + { + get => _rigidBody.CentroidWorldSpace; + set => _rigidBody.CentroidWorldSpace = value; + } + + /// + /// 物体的旋转,绕着质心 + /// + public float Rotation + { + get => _rotation; + set => _rotation = value; + } + + /// + /// 物体在迭代开始的时候的位置 + /// + public Vector2 OldPosition + { + get => _oldPos; + set => _oldPos = value; + } + + /// + /// 物体在迭代开始的时候的旋转 + /// + public float OldRotation + { + get => _oldRot; + set => _oldRot = value; + } + + /// + /// 物体的GUID + /// + public int GUID + { + get => _guid; + set => _guid = value; + } + + /// + /// 物体的碰撞组名字 + /// + public string Tag + { + get => _tag; + set => _tag = value; + } + + private Collider2D _collider; + private RigidBody2D _rigidBody; + private Vector2 _oldPos; + private float _rotation; + private float _oldRot; + private int _guid; + private string _tag; + + public PhysicsObject(Collider2D collider, RigidBody2D rigidBody) + { + _collider = collider; + _rigidBody = rigidBody; + _tag = "Default"; + + if (_rigidBody == null) + { + _rigidBody = new RigidBody2D(1) + { + MovementType = MovementType.Static, + UseGravity = false, + }; + } + + _collider.ParentObject = this; + _rigidBody.ParentObject = this; + } + + public void Initialize() + { + _rigidBody?.Initialize(); + } + + public void RecordOldState() + { + _oldPos = Position; + _oldRot = _rotation; + } + + public List GetWireFrameWires() + { + List wires = _collider.GetWireFrameWires(); + var R = Matrix2x2.CreateRotationMatrix(_rotation); + for (int i = 0; i < wires.Count; i++) + { + wires[i] = R.Multiply(wires[i]) + Position; + } + return wires; + } + public void CleanUp() + { + _rigidBody?.CleanUp(); + _rotation = MathHelper.WrapAngle(_rotation); + } + public void Update(float deltaTime) + { + _rigidBody?.Update(deltaTime); + } + + public void ApplyGravity(Vector2 gravity) + { + if(_rigidBody != null) + { + if (_rigidBody.UseGravity) + { + _rigidBody.ApplyForce(gravity * _rigidBody.Mass); + } + } + } + + public Vector2 LocalToWorldPos(Vector2 localPos) + { + return _rigidBody.CentroidWorldSpace + Matrix2x2.CreateRotationMatrix(_rotation).Multiply(localPos); + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs new file mode 100644 index 000000000..f29270279 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs @@ -0,0 +1,229 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Constrains; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine +{ + /// + /// 用于物理模拟的世界,可以设置各种世界属性,添加和管理物理对象 + /// 其中最重要的模拟部分也是由这个类完成 + /// + public class PhysicsSimulation + { + public const double EPS = 1e-6; + public const int GAUSS_SEIDEL_ITERS = 8; + + private List _dynamicPhysObjects; + private List _staticPhysObjects; + private List _constrains; + private BroadPhase _broadPhase; + private float _gravity; + + private Stopwatch _stopwatchPreIntegration; + private Stopwatch _stopwatchBroadPhase; + private Stopwatch _stopwatchNarrowPhase; + private readonly double _ticksPerMillisecond; + + /// + /// 用于性能检测,对各个阶段的耗时进行统计 + /// + public double MeasuredPreIntegrationTimeInMs + { + get => _stopwatchPreIntegration.ElapsedTicks / _ticksPerMillisecond; + } + public double MeasuredBroadPhaseTimeInMs + { + get => _stopwatchBroadPhase.ElapsedTicks / _ticksPerMillisecond; + } + public double MeasuredNarrowPhaseTimeInMs + { + get => _stopwatchNarrowPhase.ElapsedTicks / _ticksPerMillisecond; + } + + public PhysicsSimulation() + { + _dynamicPhysObjects = new List(); + _staticPhysObjects = new List(); + _constrains = new List(); + _broadPhase = new BruteForceDetect(CollisionGraph.DefualtGraph); + _gravity = 9.8f; + + _stopwatchPreIntegration = new Stopwatch(); + _stopwatchBroadPhase = new Stopwatch(); + _stopwatchNarrowPhase = new Stopwatch(); + + _ticksPerMillisecond = Stopwatch.Frequency / 1000.0; + } + + public PhysicsSimulation(CollisionGraph graph) + : this() + { + _broadPhase = new BruteForceDetect(graph); + } + + /// + /// 向世界添加物理对象,如果对象没有刚体部件,那么认为是静态物体 + /// + /// + public void AddPhysicsObject(PhysicsObject pobj) + { + pobj.GUID = _dynamicPhysObjects.Count + _staticPhysObjects.Count; + if (pobj.RigidBody.MovementType == MovementType.Static) + { + _staticPhysObjects.Add(pobj); + } + else + { + _dynamicPhysObjects.Add(pobj); + } + pobj.Initialize(); + } + + /// + /// 向物理世界添加约束对象 + /// + /// + public void AddConstrain(Constrain constrain) + { + _constrains.Add(constrain); + } + + /// + /// Initialize physical object properties + /// + public void Initialize() + { + foreach (PhysicsObject pobj in _dynamicPhysObjects) + { + pobj.Initialize(); + } + } + + /// + /// 模拟一个完整步长,内部会分为多个子步长 + /// + /// + public void Update(float deltaTime) + { + foreach (PhysicsObject pobj in _dynamicPhysObjects) + { + pobj.ApplyGravity(new Vector2(0, -_gravity)); + } + + _stopwatchPreIntegration.Reset(); + _stopwatchBroadPhase.Reset(); + _stopwatchNarrowPhase.Reset(); + float dt = deltaTime / GAUSS_SEIDEL_ITERS; + for (int i = 0; i < GAUSS_SEIDEL_ITERS; i++) + { + _stopwatchPreIntegration.Start(); + PreIntegration(dt); + _stopwatchPreIntegration.Stop(); + + Resolve(dt, _stopwatchBroadPhase, _stopwatchNarrowPhase); + } + CleanThisFrame(); + } + + public void CleanThisFrame() + { + CleanUp(); + } + + public List GetCurrentWireFrames() + { + List result = new List(); + foreach (PhysicsObject pobj in _dynamicPhysObjects) + { + result.AddRange(pobj.GetWireFrameWires()); + } + foreach (PhysicsObject pobj in _staticPhysObjects) + { + result.AddRange(pobj.GetWireFrameWires()); + } + foreach (Constrain joint in _constrains) + { + result.AddRange(joint.GetDrawMesh()); + } + return result; + } + + /// + /// 进行预积分,先模拟一个步长 + /// + /// + private void PreIntegration(float deltaTime) + { + foreach (PhysicsObject pobj in _staticPhysObjects) + { + pobj.RecordOldState(); + } + foreach (PhysicsObject pobj in _dynamicPhysObjects) + { + pobj.RecordOldState(); + pobj.Update(deltaTime); + } + } + + /// + /// 解算所有物体的约束条件,并且做出响应 + /// + /// + /// + /// + private void Resolve(float deltaTime, Stopwatch broadPhase, Stopwatch narrowPhase) + { + broadPhase.Start(); + _broadPhase.Prepare(_dynamicPhysObjects.Concat(_staticPhysObjects).ToList(), deltaTime); + var pairs = _broadPhase.GetCollisionPairs(deltaTime); + broadPhase.Stop(); + + narrowPhase.Start(); + foreach (var joint in _constrains) + { + joint.Apply(deltaTime); + } + foreach (var pair in pairs) + { + CollisionInfo info; + if (pair.Key.TestCollisionCondition(pair.Value, deltaTime, out info)) + { + float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + //if (weightA != 0 && weightB != 0) + // weightA = weightB = 0.5f; + + info.Source.RigidBody.MoveBody(info.Normal * weightA * info.Depth, deltaTime); + info.Target.RigidBody.MoveBody(-info.Normal * weightB * info.Depth, deltaTime); + + List events; + pair.Key.GetContactInfo(info, deltaTime, out events); + ApplyConstrains(events, pair.Key.ParentObject, deltaTime); + } + } + narrowPhase.Stop(); + } + + private void ApplyConstrains(List events, PhysicsObject pobj, float deltaTime) + { + if (deltaTime == 0) + { + return; + } + pobj.RigidBody.RespondToEvents(events, deltaTime); + } + private void CleanUp() + { + foreach (PhysicsObject pobj in _dynamicPhysObjects) + { + pobj.CleanUp(); + } + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs new file mode 100644 index 000000000..d4054426c --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs @@ -0,0 +1,229 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Constrains; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.PlayGround.Constrain +{ + /// + /// 储存了一些测试约束的场景 + /// + public class JointPlayground + { + public static PhysicsSimulation SimpleJoint() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(500, 660); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -40), new Vector2(0, 64)); + world.AddConstrain(joint); + + return world; + } + + public static PhysicsSimulation SimpleJointMultiple() + { + var world = new PhysicsSimulation(); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(32, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(500, 600); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(500, 500); + dynamicBox1.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox1); + + var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -50), new Vector2(0, 50)); + world.AddConstrain(joint); + + var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -70), new Vector2(0, 70)); + world.AddConstrain(joint2); + + //var kPlane = new PhysicsObject( + // new BoxCollider(200, 32), null); + //kPlane.Position = new Vector2(512, 400); + //kPlane.Rotation = 0; + //kPlane.RigidBody.ApplyAngularVelocity(0.9f); + //kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; + //world.AddPhysicsObject(kPlane); + return world; + } + + public static PhysicsSimulation DynamicJoints() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(500, 500); + dynamicBox.Rotation = 0.2f; + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(500, 440); + dynamicBox1.Rotation = 0.3f; + world.AddPhysicsObject(dynamicBox1); + + var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -50), new Vector2(0, 50)); + world.AddConstrain(joint2); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(700, 32), null); + staticPlane1.Position = new Vector2(512, 300); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + return world; + } + + public static PhysicsSimulation NewtonPendulum() + { + var world = new PhysicsSimulation(); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(512, 32), null); + staticPlane1.Position = new Vector2(512, 800); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + for (int i = 0; i < 5; i++) + { + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + 64 * (i - 2), 600); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + if (i == 0) + { + dynamicBox.RigidBody.LinearVelocity = new Vector2(-50, 0); + } + + var joint1 = new JointConstrain(staticPlane1, dynamicBox, new Vector2(64 * (i - 2), -16), new Vector2(0, 200)); + world.AddConstrain(joint1); + } + + + //var dynamicBox1 = new PhysicsObject( + // new SphereCollider(32), new RigidBody2D(256)); + //dynamicBox1.Position = new Vector2(512, 584); + //dynamicBox1.Rotation = 0.0f; + //world.AddPhysicsObject(dynamicBox1); + + //var dynamicBox2 = new PhysicsObject( + // new SphereCollider(32), new RigidBody2D(256)); + //dynamicBox2.Position = new Vector2(576, 584); + //dynamicBox2.Rotation = 0.0f; + //world.AddPhysicsObject(dynamicBox2); + + + + + + //var joint2 = new Joint(staticPlane1, dynamicBox1, new Vector2(0, -16), new Vector2(0, 200)); + //world.AddConstrain(joint2); + + //var joint3 = new Joint(staticPlane1, dynamicBox2, new Vector2(64, -16), new Vector2(0, 200)); + //world.AddConstrain(joint3); + + + return world; + } + + public static PhysicsSimulation SimpleSpring() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(300, 100); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); + world.AddConstrain(joint); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(400, 10); + dynamicBox1.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox1); + + var joint1 = new SpringConstrain(dynamicBox, dynamicBox1, 29f, 200); + world.AddConstrain(joint1); + + return world; + } + + public static PhysicsSimulation SpringTriganluar() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(300, 100); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(32, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(32, 32), null); + staticPlane2.Position = new Vector2(212, 300); + staticPlane2.Rotation = 0f; + world.AddPhysicsObject(staticPlane2); + + var staticPlane3 = new PhysicsObject( +new BoxCollider(32, 32), null); + staticPlane3.Position = new Vector2(812, 300); + staticPlane3.Rotation = 0f; + world.AddPhysicsObject(staticPlane3); + + var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); + world.AddConstrain(joint); + var joint1 = new SpringConstrain(staticPlane2, dynamicBox, 29f, 200); + world.AddConstrain(joint1); + + var joint2 = new SpringConstrain(staticPlane3, dynamicBox, 29f, 200); + world.AddConstrain(joint2); + + + return world; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs new file mode 100644 index 000000000..57929b5c5 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs @@ -0,0 +1,308 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.PlayGround.Contact +{ + /// + /// 储存了一些物理引擎接触测试的场景 + /// + public class ContactPlayGround1 + { + public static PhysicsSimulation SimpleGround1() + { + var world = new PhysicsSimulation(); + + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + return world; + } + + public static PhysicsSimulation InclinedGround() + { + var world = new PhysicsSimulation(); + + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0.22f; + world.AddPhysicsObject(staticPlane1); + + return world; + } + + public static PhysicsSimulation MultipleInclinedGround() + { + var world = new PhysicsSimulation(); + + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 900); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane1.Position = new Vector2(300, 700); + staticPlane1.Rotation = -0.32f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane2.Position = new Vector2(680, 200); + staticPlane2.Rotation = 0.32f; + world.AddPhysicsObject(staticPlane2); + + return world; + } + + public static PhysicsSimulation MultipleInclinedGround2() + { + var world = new PhysicsSimulation(); + + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 900); + world.AddPhysicsObject(dynamicBox); + + for (int i = 0; i < 5; i++) + { + + var staticPlane1 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane1.Position = new Vector2(300, 700 - i * 200); + staticPlane1.Rotation = -0.32f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane2.Position = new Vector2(680, 700 - i * 200 - 100); + staticPlane2.Rotation = 0.32f; + world.AddPhysicsObject(staticPlane2); + } + return world; + } + + public static PhysicsSimulation MultipleInclinedGround3() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 20; i++) + { + var dynamicBox = new PhysicsObject( + new SphereCollider(16), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i % 10) * 40, 900 + i / 10 * 40); + world.AddPhysicsObject(dynamicBox); + } + + for (int i = 0; i < 5; i++) + { + + var staticPlane1 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane1.Position = new Vector2(300, 700 - i * 250); + staticPlane1.Rotation = -0.38f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane2.Position = new Vector2(680, 700 - i * 250 - 125); + staticPlane2.Rotation = 0.38f; + world.AddPhysicsObject(staticPlane2); + } + return world; + } + + public static PhysicsSimulation MultipleInclinedGroundSphere() + { + var world = new PhysicsSimulation(); + + + for (int i = 0; i < 20; i++) + { + var dynamicBall = new PhysicsObject( + new SphereCollider(16), new RigidBody2D(256)); + dynamicBall.Position = new Vector2(512, 800 + i * 40); + world.AddPhysicsObject(dynamicBall); + } + + var staticPlane1 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane1.Position = new Vector2(200, 400); + staticPlane1.Rotation = -0.78f; + world.AddPhysicsObject(staticPlane1); + + + var staticPlane2 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane2.Position = new Vector2(400, 400); + staticPlane2.Rotation = 0.78f; + world.AddPhysicsObject(staticPlane2); + + return world; + } + + public static PhysicsSimulation DoubleSlope() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(162, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(300, 600); + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(300, 760); + dynamicBox1.RigidBody.ApplyForce(new Vector2(100, 0)); + world.AddPhysicsObject(dynamicBox1); + + + for (int i = 0; i < 100; i++) + { + var dynamicBox2 = new PhysicsObject( + new BoxCollider(55, 32), new RigidBody2D(256)); + dynamicBox2.Position = new Vector2(350, 700 + 36 * i); + dynamicBox2.RigidBody.ApplyForce(new Vector2(100, 0)); + world.AddPhysicsObject(dynamicBox2); + } + + var staticPlane1 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane1.Position = new Vector2(200, 400); + staticPlane1.Rotation = -0.78f; + world.AddPhysicsObject(staticPlane1); + + + var staticPlane2 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane2.Position = new Vector2(400, 400); + staticPlane2.Rotation = 0.78f; + world.AddPhysicsObject(staticPlane2); + + return world; + } + + public static PhysicsSimulation SimpleKinematic() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 200; i++) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i % 10) * 35, 900 + i / 10 * 35); + world.AddPhysicsObject(dynamicBox); + } + + var kPlane = new PhysicsObject( + new BoxCollider(500, 32), null); + kPlane.Position = new Vector2(512, 512); + kPlane.Rotation = 0; + kPlane.RigidBody.ApplyAngularVelocity(0.9f); + kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; + world.AddPhysicsObject(kPlane); + + var sPlane = new PhysicsObject( + new BoxCollider(32, 600), null); + sPlane.Position = new Vector2(0, 512); + sPlane.Rotation = 0; + world.AddPhysicsObject(sPlane); + + var sPlane2 = new PhysicsObject( + new BoxCollider(32, 600), null); + sPlane2.Position = new Vector2(1024, 512); + sPlane2.Rotation = 0; + world.AddPhysicsObject(sPlane2); + + var sPlane1 = new PhysicsObject( + new BoxCollider(1024, 32), null); + sPlane1.Position = new Vector2(512, 0); + sPlane1.Rotation = 0; + world.AddPhysicsObject(sPlane1); + return world; + } + + public static PhysicsSimulation SimpleSphere() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation SlopeSphere() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0.3f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation SphereAndBox() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 10; i++) + { + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i - 5) * 50, 800); + world.AddPhysicsObject(dynamicBox); + } + + for (int i = 0; i < 10; i++) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i - 5) * 50, 700); + world.AddPhysicsObject(dynamicBox); + } + + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs new file mode 100644 index 000000000..8d31c442f --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs @@ -0,0 +1,110 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.PlayGround.Contact +{ + /// + /// 储存了一些物理引擎接触测试的场景 + /// + public class ContactPlayGround2 + { + public static PhysicsSimulation SphereStairCase() + { + var world = new PhysicsSimulation(); + + + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + dynamicBox.RigidBody.LinearVelocity = new Vector2(12, 0); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 400); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(100, 16), null); + staticPlane2.Position = new Vector2(700, 424); + staticPlane2.Rotation = 0f; + world.AddPhysicsObject(staticPlane2); + + var staticPlane3 = new PhysicsObject( + new BoxCollider(100, 16), null); + staticPlane3.Position = new Vector2(400, 424); + staticPlane3.Rotation = 0f; + world.AddPhysicsObject(staticPlane3); + + return world; + } + + public static PhysicsSimulation ExtractionTest() + { + var world = new PhysicsSimulation(); + + + var dynamicBox = new PhysicsObject( + new BoxCollider(128, 32), new RigidBody2D(128)); + dynamicBox.Position = new Vector2(300, 800); + dynamicBox.RigidBody.LinearVelocity = new Vector2(0, 0); + world.AddPhysicsObject(dynamicBox); + + var dynamicball = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(512)); + dynamicball.Position = new Vector2(300, 900); + dynamicball.RigidBody.LinearVelocity = new Vector2(0, 0); + world.AddPhysicsObject(dynamicball); + + var dynamicball2 = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(512)); + dynamicball2.Position = new Vector2(380, 900); + dynamicball2.RigidBody.LinearVelocity = new Vector2(0, 0); + world.AddPhysicsObject(dynamicball2); + + + var staticPlane1 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane1.Position = new Vector2(200, 400); + staticPlane1.Rotation = -0.78f; + world.AddPhysicsObject(staticPlane1); + + + var staticPlane2 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane2.Position = new Vector2(400, 400); + staticPlane2.Rotation = 0.78f; + world.AddPhysicsObject(staticPlane2); + + return world; + } + + public static PhysicsSimulation MoveWithPad() + { + var world = new PhysicsSimulation(); + + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(400, 700); + dynamicBox.RigidBody.LinearVelocity = new Vector2(0, 0); + world.AddPhysicsObject(dynamicBox); + + var kPlane = new PhysicsObject( + new BoxCollider(500, 32), null); + kPlane.Position = new Vector2(512, 512); + kPlane.Rotation = 0; + kPlane.RigidBody.Drag = 0; + kPlane.RigidBody.LinearVelocity = new Vector2(10, 0); + kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; + world.AddPhysicsObject(kPlane); + + return world; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs new file mode 100644 index 000000000..43e294f08 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs @@ -0,0 +1,584 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Printing; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine +{ + /// + /// 2D刚体物理组件,用于实际的刚体运动模拟和响应的模块 + /// + public class RigidBody2D + { + /// + /// 指向物理对象的指针 + /// + public PhysicsObject ParentObject + { + get => _bindObject; + set => _bindObject = value; + } + + /// + /// 世界坐标下的质心坐标(指物理世界 + /// + public Vector2 CentroidWorldSpace + { + get => _globalCentroid; + set => _globalCentroid = value; + } + + /// + /// 质量 + /// + public float Mass + { + get => _mass; + set => _mass = value; + } + + /// + /// 质量的倒数 + /// + public float InvMass + { + get => (MovementType == MovementType.Dynamic || MovementType == MovementType.Player) ? 1 / _mass : 0; + } + + /// + /// 世界坐标下惯性张量的倒数 + /// + public double GlobalInverseInertiaTensor + { + get => MovementType == MovementType.Dynamic ? _globalInverseInertiaTensor : 0; + } + + /// + /// 是否启用重力 + /// + public bool UseGravity + { + get; set; + } + + /// + /// 线速度 + /// + public Vector2 LinearVelocity + { + get => MovementType == MovementType.Static ? Vector2.Zero : _linearVelocity; + set => _linearVelocity = value; + } + + + /// + /// 角速度 + /// + public float AngularVelocity + { + get => MovementType == MovementType.Static ? 0 : _angularVelocity; + set => _angularVelocity = value; + } + + /// + /// 移动模式 + /// + public MovementType MovementType + { + get; set; + } + + /// + /// 线速度的阻尼 + /// + public float Drag + { + get => _drag; + set => _drag = value; + } + + /// + /// 角速度的阻尼 + /// + public float AngularDrag + { + get => _angularDrag; + set => _angularDrag = value; + } + + /// + /// 碰撞弹出的系数 + /// + public float Stiffness + { + get => _stiffness; + set => _stiffness = value; + } + + /// + /// 摩擦力系数 + /// + public float Friction + { + get => _friction; + set => _friction = value; + } + + private double _localInverseInertiaTensor; + private double _globalInverseInertiaTensor; + + private Vector2 _globalCentroid; + + private Vector2 _linearVelocity; + private float _angularVelocity; + + private float _torque; + private Vector2 _force; + private float _mass; + private float _drag; + private float _angularDrag; + private float _stiffness; + private float _friction; + + private List _impluses; + + private bool _isAwake; + + private PhysicsObject _bindObject; + + public RigidBody2D(float mass) + { + _mass = mass; + MovementType = MovementType.Dynamic; + UseGravity = true; + _isAwake = true; + _linearVelocity = Vector2.Zero; + _angularVelocity = 0; + _drag = 0.06f; + _angularDrag = 0.06f; + _impluses = new List(); + _stiffness = 0.5f; + _friction = 0.5f; + } + + + private void CalculateMassCentroidAndMoI() + { + _globalInverseInertiaTensor = _localInverseInertiaTensor = 1 / _bindObject.Collider.InertiaTensor(_mass); + } + + public void Initialize() + { + CalculateMassCentroidAndMoI(); + } + + private void UpdateGlobalInertiaTensor() + { + // var R = Matrix2x2.CreateRotationMatrix(_rotation); + // _globalInertiaTensor = R.Multiply(_localInverseInertiaTensor).Multiply(R.Transpose()); + _globalInverseInertiaTensor = _localInverseInertiaTensor; + } + + /// + /// 进行一次运动积分 + /// + /// + public void Update(float deltaTime) + { + if (MovementType == MovementType.Static) + { + return; + } + + _angularVelocity += (float)(_globalInverseInertiaTensor * deltaTime * _torque); + _linearVelocity += deltaTime * _force / _mass; + // _oldRot = _rotation; + + // _oldPos = _globalCentroid; + _globalCentroid += deltaTime * _linearVelocity; + _bindObject.Rotation += deltaTime * _angularVelocity; + + + + Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); + Debug.Assert(!float.IsNaN(_angularVelocity)); + + StabilizeBody(); + + // CollisionDetection(deltaTime); + // CollisionResponse(deltaTime); + } + + private void StabilizeBody() + { + if (Math.Abs(_linearVelocity.X) < PhysicsSimulation.EPS + && Math.Abs(_linearVelocity.Y) < PhysicsSimulation.EPS) + { + _linearVelocity = Vector2.Zero; + } + + if(Math.Abs(_angularVelocity) < PhysicsSimulation.EPS) + { + _angularVelocity = 0; + } + } + + public void ApplyForce(Vector2 force) + { + _force += force; + } + + public void CleanUp() + { + _torque = 0; + _force = Vector2.Zero; + _impluses.Clear(); + // Main.NewText((_linearVelocity.LengthSquared() * _mass * 0.5f + _mass * 9.8f * _globalCentroid.Y).ToString("F1")); + + _linearVelocity *= 1 - _drag * _drag; + _angularVelocity *= 1 - _angularDrag * _angularDrag; + } + + //public void RespondToEvents(List events, float deltaTime) + //{ + // var ri0 = events[0].LocalOffsetSrc; + // var rb0 = events[0].LocalOffsetTarget; + // var n0 = events[0].Normal; + // var va0 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); + // var vb0 = events[0].Target.RigidBody._linearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); + // float relv0_n = Vector2.Dot(va0 - vb0, n0); + + // var ri1 = events[1].LocalOffsetSrc; + // var rb1 = events[1].LocalOffsetTarget; + // var n1 = events[1].Normal; + // var va1 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); + // var vb1 = events[1].Target.RigidBody._linearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); + // float relv1_n = Vector2.Dot(va1 - vb1, n1); + // Matrix2x2 matrix = new Matrix2x2() + // { + // [0, 0] = InvMass + events[0].Target.RigidBody.InvMass + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri0, n0))), n0) + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(rb0, n0))), n0), + // [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri1, n1))), n0), + + // [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri0, n0))), n1), + // [1, 1] = InvMass + events[1].Target.RigidBody.InvMass + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri1, n1))), n1) + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(rb1, n1))), n1), + // }; + // float C = 0.4f; + // var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); + // if (!J.HasNaNs()) + // { + // AddImpluse(J.X * events[0].Normal, ri0, events[0].Time, events[0].Normal); + // AddImpluse(J.Y * events[1].Normal, ri1, events[1].Time, events[1].Normal); + // } + // var t = matrix.Multiply(J); + // if (true) + // ; + //} + public void ApplyAngularVelocity(float w) + { + _angularVelocity += w; + } + private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); + var va = _linearVelocity + v2; + var vb = e.Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); + var bias = 0.1f * Math.Max(e.Depth - 0.02f, 0) / deltaTime; + + + float va_n = Vector2.Dot(va - vb, e.Normal); + if (va_n >= 0) + { + e.NormalVelOld = 0; + return; + } + float stiffness = Math.Max(0, (_stiffness + e.Target.RigidBody.Stiffness) / 2); + float vnew_n = stiffness * Math.Max(-va_n - 20 * deltaTime, 0); + + // (a × b) × c = (c • a)b - (c • b)a + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); + double R1 = rAdotN * rAdotN * GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + //* Utils.Cross(rb, e.Normal))), e.Normal); + double J_n = (vnew_n - va_n) / (InvMass + e.Target.RigidBody.InvMass + R1 + R2); + Vector2 J = (float)J_n * e.Normal / count;// + (float)J_t * va_t_unit; + e.NormalVelOld = (float)J_n; + + // var offset = e.Position - (_globalCentroid + ri); + AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + } + + private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); + var va = _linearVelocity + v2; + var vb = e.Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); + + if (e.NormalVelOld == 0) + { + return; + } + + float vel_n = Vector2.Dot(va - vb, e.Normal); + Vector2 vt = (va - vb) - vel_n * e.Normal; + float vel_t = vt.Length(); + float friction = Math.Max(0, (_friction + e.Target.RigidBody.Friction) / 2); + + vt = vt.SafeNormalize(Vector2.Zero); + + double effectiveMass = (InvMass + e.Target.RigidBody.InvMass + + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri, vt))), vt) + + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + * GeometryUtils.Cross(rb, vt))), vt)); + + // (a × b) × c = (c • a)b - (c • b)a + double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); + Vector2 J = (float)J_n * vt / count;// + (float)J_t * va_t_unit; + + AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + } + + /// + /// 对接触事件进行响应 + /// + /// + /// + public void RespondToEvents(List events, float deltaTime) + { + foreach (var e in events) + { + SolveContactImpluse(e, events.Count, deltaTime); + } + ApplyImpluses(); + foreach (var e in events) + { + SolveFrictionImpluse(e, events.Count, deltaTime); + } + ApplyImpluses(); + } + + public void Integrate(float deltaTime) + { + if (MovementType == MovementType.Static) + { + return; + } + _bindObject.Rotation += deltaTime * _angularVelocity; + _globalCentroid += deltaTime * _linearVelocity; + _bindObject.Position = _globalCentroid; + } + + //public void ResolveImpluse(float deltaTime) + //{ + // if (_impluses.Count == 0) + // { + // return; + // } + // //_bindObject.Rotation = _bindObject.OldRotation; + // //_globalCentroid = _bindObject.OldPosition; + // _impluses.Sort((a, b) => + // { + // return a.Time.CompareTo(b.Time); + // }); + // float lastTime = 0; + // int sameTimeCount = 0; + // for (int i = 0; i < _impluses.Count; i++) + // { + // sameTimeCount++; + // if (i == _impluses.Count - 1 || _impluses[i].Time != _impluses[i + 1].Time) + // { + // float dt = (_impluses[i].Time - lastTime); + + // Vector2 linearVelocityChange = Vector2.Zero; + // float angularVelocityChange = 0; + // Vector2 normalAvg = Vector2.Zero; + + // //linearVelocityChange /= sameTimeCount; + // //angularVelocityChange /= sameTimeCount; + // //normalAvg /= sameTimeCount; + // //normalAvg = normalAvg.SafeNormalize(Vector2.Zero); + + + + // var resImpluses = ResolveConstrains(); + // for (int j = i - sameTimeCount + 1; j <= i; j++) + // { + // linearVelocityChange += 1.0f / _mass * resImpluses[j] * _impluses[j].Normal; + // angularVelocityChange += (float)(Utils.Cross(_impluses[j].RelativePosition, + // resImpluses[j] *_impluses[j].Normal * (float)_globalInverseInertiaTensor)); + // } + + + // //var penetration = Math.Max(0, -Vector2.Dot(_linearVelocity * deltaTime, normalAvg)); + // //_linearVelocity += normalAvg * penetration * 0.7f; + + // deltaTime -= dt; + // lastTime = _impluses[i].Time; + // sameTimeCount = 0; + // } + // } + // _impluses.Clear(); + //} + + //private List ResolveConstrains() + //{ + // List impluseArray = new List(); + // List impluseArrayTemp = new List(); + // for (int k = 0; k < _impluses.Count; k++) + // { + // impluseArray.Add(_impluses[k].Impluse.Length()); + // impluseArrayTemp.Add(0f); + // } + // for (int iter = 0; iter < 8; iter++) + // { + // int sameSourceCount = 0; + // for (int k = 0; k < _impluses.Count; k++) + // { + // var ri0 = _impluses[k].CollisionEvent.LocalOffsetSrc; + // var rb0 = _impluses[k].CollisionEvent.LocalOffsetTarget; + // var n0 = _impluses[k].CollisionEvent.Normal; + // var va0 = _impluses[k].CollisionEvent.Source.RigidBody.LinearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(ri0, _impluses[k].CollisionEvent.Source.RigidBody.AngularVelocity); + // var vb0 = _impluses[k].CollisionEvent.Target.RigidBody.LinearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(rb0, _impluses[k].CollisionEvent.Target.RigidBody.AngularVelocity); + // float relv0_n = Vector2.Dot(va0 - vb0, n0); + // float relv0_plus = -(1 + 0.4f) * Vector2.Dot(va0 - vb0, n0); + + // float Kj = _impluses[k].CollisionEvent.Source.RigidBody.InvMass + _impluses[k].CollisionEvent.Target.RigidBody.InvMass + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(ri0, n0))), n0) + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(_impluses[k].CollisionEvent.Target.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(rb0, n0))), n0); + + // float KLU = 0f; + // for (int l = 0; l < _impluses.Count; l++) + // { + // if (l == k) + // { + // continue; + // } + // var ri1 = _impluses[l].CollisionEvent.LocalOffsetSrc; + // var rb1 = _impluses[l].CollisionEvent.LocalOffsetTarget; + // var n1 = _impluses[l].CollisionEvent.Normal; + + // KLU += (_impluses[k].CollisionEvent.Source.RigidBody.InvMass + // * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, + // (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(ri1, n1))), n0)) * impluseArray[l]; + // } + // impluseArrayTemp[k] = 1 / Kj * (relv0_plus - KLU); + // } + // sameSourceCount = 0; + // // Deep copy + // for (int k = 0; k < impluseArray.Count; k++) + // { + // impluseArray[k] = impluseArrayTemp[k]; + // } + // } + + // return impluseArray; + //} + + /// + /// 把冲量数据累加到这个刚体上 + /// + /// + /// + /// + /// + /// + public void AddImpluse(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) + { + var entry = new ImpluseEntry() + { + Source = this, + Target = other, + ImpluseSource = J, + RelativePositionSource = relativePos, + ImpluseTarget = J2, + RelativePositionTarget = relativePos2 + }; + _impluses.Add(entry); + } + + /// + /// 对累加的冲量进行应用,更新各种速度 + /// + public void ApplyImpluses() + { + if (_impluses.Count > 0) + { + foreach (var imp in _impluses) + { + if (imp.Source.MovementType == MovementType.Dynamic || imp.Source.MovementType == MovementType.Player) + { + _linearVelocity += 1.0f / _mass * imp.ImpluseSource; + if (imp.Source.MovementType != MovementType.Player) + { + _angularVelocity += GeometryUtils.Cross(imp.RelativePositionSource, imp.ImpluseSource * (float)_globalInverseInertiaTensor); + } + } + if (imp.Target.MovementType == MovementType.Dynamic || imp.Target.MovementType == MovementType.Player) + { + imp.Target._linearVelocity += imp.Target.InvMass * imp.ImpluseTarget; + if (imp.Target.MovementType != MovementType.Player) + { + imp.Target._angularVelocity += GeometryUtils.Cross(imp.RelativePositionTarget, imp.ImpluseTarget * (float)imp.Target.GlobalInverseInertiaTensor); + } + } + + } + } + StabilizeBody(); + _impluses.Clear(); + } + + /// + /// 立即施加一个冲量 + /// + /// + /// + public void AddImpluseImmediate(Vector2 J, Vector2 relativePos) + { + _linearVelocity += 1.0f / _mass * J; + Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); + _angularVelocity += (float)(GeometryUtils.Cross(relativePos, + J * (float)_globalInverseInertiaTensor)); + Debug.Assert(!float.IsNaN(_angularVelocity)); + } + + public void MoveBody(Vector2 dir, float deltaTime) + { + _globalCentroid += dir; + // _linearVelocity = (_globalCentroid - ParentObject.OldPosition) / deltaTime; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Shaders/Default.fx b/Sources/Everglow.Core/Physics/PBEngine/Shaders/Default.fx new file mode 100644 index 000000000..02c1e1446 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Shaders/Default.fx @@ -0,0 +1,43 @@ +sampler uImage0 : register(s0); + +float4x4 uTransform; +float uTime; + +struct VSInput +{ + float2 Pos : POSITION0; + float4 Color : COLOR0; + float2 Texcoord : TEXCOORD0; +}; + +struct PSInput +{ + float4 Pos : SV_POSITION; + float4 Color : COLOR0; + float2 Texcoord : TEXCOORD0; +}; + + +PSInput VertexShaderFunction(VSInput input) +{ + PSInput output; + output.Color = input.Color; + output.Texcoord = input.Texcoord; + output.Pos = mul(float4(input.Pos, 0, 1), uTransform); + return output; +} + + +float4 PixelShaderFunction(PSInput input) : COLOR0 +{ + return tex2D(uImage0, input.Texcoord) * input.Color; +} + +technique Technique1 +{ + pass Test + { + VertexShader = compile vs_3_0 VertexShaderFunction(); + PixelShader = compile ps_3_0 PixelShaderFunction(); + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Utilities/Matrix2x2.cs b/Sources/Everglow.Core/Utilities/Matrix2x2.cs new file mode 100644 index 000000000..5ed29f298 --- /dev/null +++ b/Sources/Everglow.Core/Utilities/Matrix2x2.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Utilities +{ + /// + /// 2x2 矩阵类 + /// + public class Matrix2x2 + { + public static Matrix2x2 Identity = new Matrix2x2( + new double[2, 2] + { + { 1, 0}, { 0, 1 }, + }); + public static Matrix2x2 Zero = new Matrix2x2( + new double[2, 2] + { + { 0, 0 }, { 0, 0 }, + }); + + private double[,] _matrix; + + public Matrix2x2() + { + _matrix = new double[2, 2]; + } + + public Matrix2x2(double[,] initialMatrix) + { + if (initialMatrix.GetLength(0) != 2 || initialMatrix.GetLength(1) != 2) + throw new ArgumentException("Initial matrix must be 2x2"); + + _matrix = initialMatrix; + } + + public double this[int row, int col] + { + get + { + return _matrix[row, col]; + } + set + { + _matrix[row, col] = value; + } + } + + public double Trace() + { + return _matrix[0, 0] + _matrix[1, 1]; + } + + public Matrix2x2 Inverse() + { + double det = Determinant(); + if (det == 0) + throw new InvalidOperationException("Matrix is not invertible."); + + var result = new Matrix2x2 + { + [0, 0] = _matrix[1, 1] / det, + [0, 1] = -_matrix[0, 1] / det, + [1, 0] = -_matrix[1, 0] / det, + [1, 1] = _matrix[0, 0] / det + }; + + return result; + } + + public Matrix2x2 Multiply(Matrix2x2 other) + { + var result = new Matrix2x2 + { + [0, 0] = this[0, 0] * other[0, 0] + this[0, 1] * other[1, 0], + [0, 1] = this[0, 0] * other[0, 1] + this[0, 1] * other[1, 1], + [1, 0] = this[1, 0] * other[0, 0] + this[1, 1] * other[1, 0], + [1, 1] = this[1, 0] * other[0, 1] + this[1, 1] * other[1, 1] + }; + + return result; + } + + public Vector2 Multiply(Vector2 other) + { + return new Vector2((float)(this[0, 0] * other.X + this[0, 1] * other.Y), + (float)(this[1, 0] * other.X + this[1, 1] * other.Y)); + } + + public double Determinant() + { + return _matrix[0, 0] * _matrix[1, 1] - _matrix[0, 1] * _matrix[1, 0]; + } + + public Matrix2x2 Adjoint() + { + return new Matrix2x2 + { + [0, 0] = _matrix[1, 1], + [0, 1] = -_matrix[0, 1], + [1, 0] = -_matrix[1, 0], + [1, 1] = _matrix[0, 0], + }; + } + + public static Matrix2x2 operator *(Matrix2x2 a, double b) + { + var result = new Matrix2x2 + { + [0, 0] = a[0, 0] * b, + [0, 1] = a[0, 1] * b, + [1, 0] = a[1, 0] * b, + [1, 1] = a[1, 1] * b + }; + + return result; + } + + public static Matrix2x2 operator *(double b, Matrix2x2 a) + { + var result = new Matrix2x2 + { + [0, 0] = a[0, 0] * b, + [0, 1] = a[0, 1] * b, + [1, 0] = a[1, 0] * b, + [1, 1] = a[1, 1] * b + }; + + return result; + } + + public static Matrix2x2 operator *(Matrix2x2 a, Matrix2x2 b) + { + var result = new Matrix2x2 + { + [0, 0] = a[0, 0] * b[0, 0] + a[0, 1] * b[1, 0], + [0, 1] = a[0, 0] * b[0, 1] + a[0, 1] * b[1, 1], + [1, 0] = a[1, 0] * b[0, 0] + a[1, 1] * b[1, 0], + [1, 1] = a[1, 0] * b[0, 1] + a[1, 1] * b[1, 1] + }; + + return result; + } + + public static Matrix2x2 operator +(Matrix2x2 a, Matrix2x2 b) + { + var result = new Matrix2x2 + { + [0, 0] = a[0, 0] + b[0, 0], + [0, 1] = a[0, 1] + b[0, 1], + [1, 0] = a[1, 0] + b[1, 0], + [1, 1] = a[1, 1] + b[1, 1] + }; + + return result; + } + + public static Matrix2x2 operator -(Matrix2x2 a, Matrix2x2 b) + { + var result = new Matrix2x2 + { + [0, 0] = a[0, 0] - b[0, 0], + [0, 1] = a[0, 1] - b[0, 1], + [1, 0] = a[1, 0] - b[1, 0], + [1, 1] = a[1, 1] - b[1, 1] + }; + + return result; + } + + public static Matrix2x2 operator /(Matrix2x2 a, Matrix2x2 b) + { + var result = new Matrix2x2 + { + [0, 0] = a[0, 0] / b[0, 0], + [0, 1] = a[0, 1] / b[0, 1], + [1, 0] = a[1, 0] / b[1, 0], + [1, 1] = a[1, 1] / b[1, 1] + }; + + return result; + } + + public Matrix2x2 Transpose() + { + return new Matrix2x2 + { + [0, 0] = _matrix[0, 0], + [0, 1] = _matrix[1, 0], + [1, 0] = _matrix[0, 1], + [1, 1] = _matrix[1, 1], + }; + } + + + public static Matrix2x2 CreateRotationMatrix(float r) + { + return new Matrix2x2 + { + [0, 0] = Math.Cos(r), + [0, 1] = -Math.Sin(r), + [1, 0] = Math.Sin(r), + [1, 1] = Math.Cos(r), + }; + } + } +} diff --git a/Sources/Everglow.Core/Utilities/Matrix3x3.cs b/Sources/Everglow.Core/Utilities/Matrix3x3.cs new file mode 100644 index 000000000..6180665ee --- /dev/null +++ b/Sources/Everglow.Core/Utilities/Matrix3x3.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Utilities +{ + public class Matrix3x3 + { + public static Matrix3x3 Identity = new Matrix3x3(new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)); + private double[,] _matrix; + + public Matrix3x3() + { + _matrix = new double[3, 3]; + } + public Matrix3x3(double[,] initialMatrix) + { + if (initialMatrix.GetLength(0) != 3 || initialMatrix.GetLength(1) != 3) + throw new ArgumentException("Initial matrix must be 3x3"); + + _matrix = initialMatrix; + } + + public double this[int row, int col] + { + get + { + return _matrix[row, col]; + } + set + { + _matrix[row, col] = value; + } + } + + public Matrix3x3(Vector3 col0, Vector3 col1, Vector3 col2) + { + _matrix = new double[3, 3]; + _matrix[0, 0] = col0.X; + _matrix[1, 0] = col0.Y; + _matrix[2, 0] = col0.Z; + + _matrix[0, 1] = col1.X; + _matrix[1, 1] = col1.Y; + _matrix[2, 1] = col1.Z; + + _matrix[0, 2] = col2.X; + _matrix[1, 2] = col2.Y; + _matrix[2, 2] = col2.Z; + } + + public static Matrix3x3 operator *(Matrix3x3 a, double x) + { + double[,] data = new double[3, 3]; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + data[i, j] = a._matrix[i, j] * x; + } + } + + return new Matrix3x3(data); + } + + public static Matrix3x3 operator -(Matrix3x3 a, Matrix3x3 b) + { + double[,] data = new double[3, 3]; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + data[i, j] = a._matrix[i, j] - b._matrix[i, j]; + } + } + + return new Matrix3x3(data); + } + + public static Matrix3x3 operator +(Matrix3x3 a, Matrix3x3 b) + { + double[,] data = new double[3, 3]; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + data[i, j] = a._matrix[i, j] + b._matrix[i, j]; + } + } + + return new Matrix3x3(data); + } + + public static Matrix3x3 operator /(Matrix3x3 a, Matrix3x3 b) + { + double[,] data = new double[3, 3]; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + data[i, j] = a._matrix[i, j] / b._matrix[i, j]; + } + } + + return new Matrix3x3(data); + } + + + + public Matrix3x3 Transpose() + { + double[,] m = new double[3, 3]; + m[0, 0] = _matrix[0, 0]; + m[0, 1] = _matrix[1, 0]; + m[0, 2] = _matrix[2, 0]; + + m[1, 0] = _matrix[0, 1]; + m[1, 1] = _matrix[1, 1]; + m[1, 2] = _matrix[2, 1]; + + m[2, 0] = _matrix[0, 2]; + m[2, 1] = _matrix[1, 2]; + m[2, 2] = _matrix[2, 2]; + return new Matrix3x3(m); + } + + public Matrix3x3 Multiply(Matrix3x3 other) + { + double[,] result = new double[3, 3]; + + result[0, 0] = _matrix[0, 0] * other._matrix[0, 0] + _matrix[0, 1] * other._matrix[1, 0] + + _matrix[0, 2] * other._matrix[2, 0]; + result[0, 1] = _matrix[0, 0] * other._matrix[0, 1] + _matrix[0, 1] * other._matrix[1, 1] + + _matrix[0, 2] * other._matrix[2, 1]; + result[0, 2] = _matrix[0, 0] * other._matrix[0, 2] + _matrix[0, 1] * other._matrix[1, 2] + + _matrix[0, 2] * other._matrix[2, 2]; + + result[1, 0] = _matrix[1, 0] * other._matrix[0, 0] + _matrix[1, 1] * other._matrix[1, 0] + + _matrix[1, 2] * other._matrix[2, 0]; + result[1, 1] = _matrix[1, 0] * other._matrix[0, 1] + _matrix[1, 1] * other._matrix[1, 1] + + _matrix[1, 2] * other._matrix[2, 1]; + result[1, 2] = _matrix[1, 0] * other._matrix[0, 2] + _matrix[1, 1] * other._matrix[1, 2] + + _matrix[1, 2] * other._matrix[2, 2]; + + result[2, 0] = _matrix[2, 0] * other._matrix[0, 0] + _matrix[2, 1] * other._matrix[1, 0] + + _matrix[2, 2] * other._matrix[2, 0]; + result[2, 1] = _matrix[2, 0] * other._matrix[0, 1] + _matrix[2, 1] * other._matrix[1, 1] + + _matrix[2, 2] * other._matrix[2, 1]; + result[2, 2] = _matrix[2, 0] * other._matrix[0, 2] + _matrix[2, 1] * other._matrix[1, 2] + + _matrix[2, 2] * other._matrix[2, 2]; + + return new Matrix3x3(result); + } + + public Vector3 Multiply(Vector3 v) + { + return new Vector3((float)(_matrix[0, 0] * v.X + _matrix[0, 1] * v.Y + _matrix[0, 2] * v.Z), + (float)(_matrix[1, 0] * v.X + _matrix[1, 1] * v.Y + _matrix[1, 2] * v.Z), + (float)(_matrix[2, 0] * v.X + _matrix[2, 1] * v.Y + _matrix[2, 2] * v.Z)); + } + + public double Determinant() + { + return _matrix[0, 0] * (_matrix[1, 1] * _matrix[2, 2] - _matrix[1, 2] * _matrix[2, 1]) - + _matrix[0, 1] * (_matrix[1, 0] * _matrix[2, 2] - _matrix[1, 2] * _matrix[2, 0]) + + _matrix[0, 2] * (_matrix[1, 0] * _matrix[2, 1] - _matrix[1, 1] * _matrix[2, 0]); + } + + public Matrix3x3 Inverse() + { + double det = Determinant(); + if (Math.Abs(det) < 1e-7) + throw new InvalidOperationException("Non-invertible matrix"); + + double[,] result = new double[3, 3]; + + result[0, 0] = (_matrix[1, 1] * _matrix[2, 2] - _matrix[1, 2] * _matrix[2, 1]) / det; + result[0, 1] = (_matrix[0, 2] * _matrix[2, 1] - _matrix[0, 1] * _matrix[2, 2]) / det; + result[0, 2] = (_matrix[0, 1] * _matrix[1, 2] - _matrix[0, 2] * _matrix[1, 1]) / det; + result[1, 0] = (_matrix[1, 2] * _matrix[2, 0] - _matrix[1, 0] * _matrix[2, 2]) / det; + result[1, 1] = (_matrix[0, 0] * _matrix[2, 2] - _matrix[0, 2] * _matrix[2, 0]) / det; + result[1, 2] = (_matrix[0, 2] * _matrix[1, 0] - _matrix[0, 0] * _matrix[1, 2]) / det; + result[2, 0] = (_matrix[1, 0] * _matrix[2, 1] - _matrix[1, 1] * _matrix[2, 0]) / det; + result[2, 1] = (_matrix[0, 1] * _matrix[2, 0] - _matrix[0, 0] * _matrix[2, 1]) / det; + result[2, 2] = (_matrix[0, 0] * _matrix[1, 1] - _matrix[0, 1] * _matrix[1, 0]) / det; + + return new Matrix3x3(result); + } + + public double Trace() + { + return _matrix[0, 0] + _matrix[1, 1] + _matrix[2, 2]; + } + + public Vector3 Column(int i) + { + return new Vector3((float)_matrix[0, i], (float)_matrix[1, i], (float)_matrix[2, i]); + } + + public bool HasNaN() + { + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + if (double.IsNaN(_matrix[i, j])) + { + return true; + } + } + } + return false; + } + } +} diff --git a/Sources/Everglow.Core/Utilities/Utils.md b/Sources/Everglow.Core/Utilities/Utils.md index 94a6b96c1..aa5fb7c4c 100644 --- a/Sources/Everglow.Core/Utilities/Utils.md +++ b/Sources/Everglow.Core/Utilities/Utils.md @@ -10,4 +10,10 @@ XNAһЩĽһװչ ## CSUtils -SystemһЩİװչһ鷳ķʲôģ \ No newline at end of file +SystemһЩİװչһ鷳ķʲôģ + +## Matrix2x2 +2x2ʵֳ֣֧þתá桢תȵ + +## Matrix3x3 +2x2ʵֳ֣֧þתá桢תȵ diff --git a/Sources/Modules/Example/RigidBodyDisplay.cs b/Sources/Modules/Example/RigidBodyDisplay.cs new file mode 100644 index 000000000..fc86db526 --- /dev/null +++ b/Sources/Modules/Example/RigidBodyDisplay.cs @@ -0,0 +1,97 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.PlayGround.Constrain; +using Everglow.Commons.Physics.PBEngine.PlayGround.Contact; +using Everglow.Commons.Vertex; +using ReLogic.Content; +using ReLogic.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using Terraria.GameContent; + +namespace Everglow.Commons.Physics.PBEngine +{ + internal class RigidBodyDisplay + { + private PhysicsSimulation _physicsSimulation; + private RenderTarget2D _canvasTarget; + private Asset _renderShader; + + public static RigidBodyDisplay Instance; + + public RigidBodyDisplay() + { + Instance = this; + } + + private const int CANVAS_SIZE = 1024; + + public void Update() + { + _physicsSimulation.Update(0.1f); + } + + public void Draw(SpriteBatch sb) + { + GeometryUtils.Blit(Main.screenTarget, Main.screenTargetSwap, null, null); + RenderPhysWorld(); + GeometryUtils.Blit(Main.screenTargetSwap, Main.screenTarget, null, null); + sb.Begin(); + var start = Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition; + sb.Draw(TextureAssets.MagicPixel.Value, new Rectangle((int)start.X - 2, (int)start.Y - 2, CANVAS_SIZE + 4, CANVAS_SIZE + 4), Color.White); + sb.Draw(_canvasTarget, Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition, + null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.FlipVertically, 0f); + + sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Green); + sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_physicsSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Green); + sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_physicsSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Green); + sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_physicsSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Green); + + sb.End(); + } + + private void RenderPhysWorld() + { + List vertices = new List(); + + foreach (var p in _physicsSimulation.GetCurrentWireFrames()) + { + vertices.Add(new Vertex2D(p, Color.White, Vector3.Zero)); + } + + Main.graphics.GraphicsDevice.SetRenderTarget(_canvasTarget); + Main.graphics.GraphicsDevice.Clear(Color.Black); + _renderShader.Value.Parameters["uTransform"].SetValue(Matrix.CreateOrthographicOffCenter(0f, CANVAS_SIZE, CANVAS_SIZE, 0f, 0f, 1f)); + _renderShader.Value.CurrentTechnique.Passes[0].Apply(); + Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; + Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); + } + + public void ReInitPhysWorld() + { + _physicsSimulation = JointPlayground.SpringTriganluar(); + + //var staticPlane3 = new PhysicsObject( + // new AABBCollider(1, 40), null); + //staticPlane3.Position = new Vector2(200, 300); + //staticPlane3.Rotation = 0f; + //_physicsSimulation.AddPhysicsObject(staticPlane3); + + _physicsSimulation.Initialize(); + } + + public void Initialize() + { + ReInitPhysWorld(); + Main.QueueMainThreadAction(() => + { + _canvasTarget = new RenderTarget2D(Main.graphics.GraphicsDevice, 1024, 1024); + }); + _renderShader = ModContent.Request("ChatGPT/Core/Physics/Shaders/Default"); + } + } +} From a900489b04c16c33b6df480878d839746e7ef42a Mon Sep 17 00:00:00 2001 From: CXUtk <2237367067@qq.com> Date: Fri, 30 Jun 2023 17:15:11 -0700 Subject: [PATCH 02/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...25\346\223\216\346\226\207\346\241\243.md" | 14 ++ .../Modules/Example/Everglow.Example.csproj | 3 + Sources/Modules/Example/PhysicsDemo.cs | 211 ++++++++++++++++++ .../Example/VFX}/Default.fx | 1 + .../Example/\346\226\207\346\241\243.md" | 2 + 5 files changed, 231 insertions(+) create mode 100644 "Sources/Everglow.Core/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" create mode 100644 Sources/Modules/Example/PhysicsDemo.cs rename Sources/{Everglow.Core/Physics/PBEngine/Shaders => Modules/Example/VFX}/Default.fx (97%) create mode 100644 "Sources/Modules/Example/\346\226\207\346\241\243.md" diff --git "a/Sources/Everglow.Core/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" "b/Sources/Everglow.Core/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" new file mode 100644 index 000000000..d518a714f --- /dev/null +++ "b/Sources/Everglow.Core/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" @@ -0,0 +1,14 @@ +# PBEngine + +## չ +мҪϵң +[ ] Һ͸彻ٵӰԭ˶ +[ ] Marching Cubes߱㷨ػתײ +[ ] εʷֺ͸Чײ +[ ] õŻԼ˳ﵽٶ +[ ] ԼؽڡƵ +[ ] Ӵ׼ij +[ ] ײ +[ ] ּ׶Żм +[ ] Զ߹ +[ ] ײ⹦Էֹ쳣͸ diff --git a/Sources/Modules/Example/Everglow.Example.csproj b/Sources/Modules/Example/Everglow.Example.csproj index ba938f7b3..3eb1642e0 100644 --- a/Sources/Modules/Example/Everglow.Example.csproj +++ b/Sources/Modules/Example/Everglow.Example.csproj @@ -1,3 +1,6 @@ + + + \ No newline at end of file diff --git a/Sources/Modules/Example/PhysicsDemo.cs b/Sources/Modules/Example/PhysicsDemo.cs new file mode 100644 index 000000000..b0b54c1e2 --- /dev/null +++ b/Sources/Modules/Example/PhysicsDemo.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Vertex; +using ReLogic.Content; +using Terraria.GameContent; +using Microsoft.Xna.Framework.Input; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.GameInteraction; +using Everglow.Commons.Physics.PBEngine.Collision; + +namespace Everglow.Example; + +internal class PhysicsPlayer : ModPlayer +{ + private PhysicsObject _movingPanel; + private Vector2 _prevPos; + private Vector2 _saveVelocity; + public override void OnEnterWorld() + { + } + public override void PostUpdate() + { + Main.LocalPlayer.velocity = _saveVelocity; + } + + public override void PreUpdateMovement() + { + _prevPos = Main.LocalPlayer.Center; + float dt = 0.1f; + var oldPos = Main.LocalPlayer.Center; + var preVelY = Main.LocalPlayer.velocity.Y; + PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.velocity / dt); + PhysicsDemo.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(_prevPos); + PhysicsDemo.Instance._realSimulation.Update(dt); + + // Main.LocalPlayer.Center = Physics.Utils.ConvertToPhysicsSpace(Display.Instance._dummyPlayer.Position); + Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity * dt); + _saveVelocity = Main.LocalPlayer.velocity; + if (preVelY > 0 && Math.Abs(Main.LocalPlayer.velocity.Y) < 0.01f) + { + Main.LocalPlayer.velocity.Y = 0; + } + + + if (_movingPanel != null) + { + + if (Main.time % 1200 < 600) + { + _movingPanel.RigidBody.LinearVelocity = new Vector2(-3, 0); + } + else if (Main.time % 1200 >= 600) + { + _movingPanel.RigidBody.LinearVelocity = new Vector2(3, 0); + } + } + + base.PreUpdateMovement(); + } + + public override void UpdateEquips() + { + base.UpdateEquips(); + } + public override void SetControls() + { + base.SetControls(); + if(Main.mouseLeft && Main.mouseLeftRelease) + { + var ball = new PhysicsObject(new SphereCollider(32), + new RigidBody2D(256)); + PhysicsDemo.Instance._realSimulation.AddPhysicsObject(ball); + ball.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); + ball.RigidBody.LinearVelocity = new Vector2(0, 0); + } + if (Main.keyState[Keys.R] == KeyState.Down && Main.oldKeyState[Keys.R] == KeyState.Up) + { + // RigidBodyDisplay.Instance.ReInitPhysWorld(); + PhysicsDemo.Instance.ReStart(); + } + + if (Main.keyState[Keys.T] == KeyState.Down && Main.oldKeyState[Keys.T] == KeyState.Up) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(256, 64), new RigidBody2D(10233)); + dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center - new Vector2(0, 200)); + dynamicBox.Rotation = 0.0f; + dynamicBox.RigidBody.MovementType = MovementType.Kinematic; + dynamicBox.RigidBody.UseGravity = false; + PhysicsDemo.Instance._realSimulation.AddPhysicsObject(dynamicBox); + _movingPanel = dynamicBox; + + //var ball = new PhysicsObject(new BoxCollider(32, 32), + // new RigidBody2D(256)); + //Display.Instance._realSimulation.AddPhysicsObject(ball); + //ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -300)); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center - new Vector2(0, 400)); + staticPlane1.Rotation = 0.4f; + PhysicsDemo.Instance._realSimulation.AddPhysicsObject(staticPlane1); + + //var joint = new Spring(staticPlane1, dynamicBox, 40f, 200f, new Vector2(100, 0), new Vector2(100, 0)); + //Display.Instance._realSimulation.AddConstrain(joint); + + //var joint1 = new Spring(staticPlane1, dynamicBox, 40f, 200f, new Vector2(-100, 0), new Vector2(-100, 0)); + //Display.Instance._realSimulation.AddConstrain(joint1); + + //if (Main.rand.NextBool(2)) + //{ + // var ball = new PhysicsObject(new BoxCollider(256, 32), + // new RigidBody2D(128)); + // ball.RigidBody.MovementType = Physics.Collision.MovementType.Kinematic; + // ball.RigidBody.UseGravity = false; + // ball.RigidBody.AngularDrag = 0; + // ball.RigidBody.AngularVelocity = 0.9f; + // Display.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -100)); + //} + //else + //{ + // var ball = new PhysicsObject(new SphereCollider(32), + // new RigidBody2D(256)); + // Display.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(100, 0)); + // ball.RigidBody.LinearVelocity = new Vector2(-78, 0); + //} + } + } +} + +internal class PhysicsDemo : ModSystem +{ + private RigidBodyDisplay rigidBodyDisplay; + private Asset _renderShader; + public PhysicsSimulation _realSimulation; + public static PhysicsDemo Instance; + public PhysicsObject _dummyPlayer; + + public override void PostUpdateEverything() + { + //rigidBodyDisplay.Update(); + } + public override void PostDrawTiles() + { + //rigidBodyDisplay.Draw(Main.spriteBatch); + + List vertices = new List(); + foreach (var p in _realSimulation.GetCurrentWireFrames()) + { + vertices.Add(new Vertex2D(GeometryUtils.ConvertToPhysicsSpace(p) - Main.screenPosition, Color.White, Vector3.Zero)); + } + + if (vertices.Count > 0) + { + _renderShader.Value.Parameters["uColor"].SetValue(Color.White.ToVector4()); + _renderShader.Value.Parameters["uTransform"].SetValue(Matrix.CreateOrthographicOffCenter(0, Main.screenWidth, Main.screenHeight, 0, 0, 1)); + _renderShader.Value.CurrentTechnique.Passes[0].Apply(); + Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; + Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); + } + base.PostDrawTiles(); + } + + public override void Load() + { + //rigidBodyDisplay = new RigidBodyDisplay(); + //rigidBodyDisplay.Initialize(); + + Instance = this; + ReStart(); + _renderShader = ModContent.Request("Everglow/Example/VFX/PureColor"); + base.Load(); + } + + public void ReStart() + { + var terrariaCollisionGroup = new CollisionGraph(); + _realSimulation = new PhysicsSimulation(terrariaCollisionGroup); + terrariaCollisionGroup.AddSingleEdge("Default", "Terrain"); + terrariaCollisionGroup.AddDoubleEdge("Player", "Default"); + + var terrain = new PhysicsObject(new TileCollider(), null); + _realSimulation.AddPhysicsObject(terrain); + terrain.Tag = "Terrain"; + terrain.RigidBody.MovementType = MovementType.Static; + + var rigidb = new RigidBody2D(64); + _dummyPlayer = new PhysicsObject(new BoxCollider(20, 42), rigidb); + _dummyPlayer.Tag = "Player"; + rigidb.MovementType = MovementType.Player; + rigidb.UseGravity = false; + rigidb.AngularDrag = 0; + rigidb.Drag = 0; + rigidb.Stiffness = -1; + rigidb.Friction = -1; + _realSimulation.AddPhysicsObject(_dummyPlayer); + _realSimulation.Initialize(); + } + + public override void Unload() + { + base.Unload(); + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Shaders/Default.fx b/Sources/Modules/Example/VFX/Default.fx similarity index 97% rename from Sources/Everglow.Core/Physics/PBEngine/Shaders/Default.fx rename to Sources/Modules/Example/VFX/Default.fx index 02c1e1446..406382297 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Shaders/Default.fx +++ b/Sources/Modules/Example/VFX/Default.fx @@ -1,3 +1,4 @@ +// ߿Ļ sampler uImage0 : register(s0); float4x4 uTransform; diff --git "a/Sources/Modules/Example/\346\226\207\346\241\243.md" "b/Sources/Modules/Example/\346\226\207\346\241\243.md" new file mode 100644 index 000000000..7188c2a67 --- /dev/null +++ "b/Sources/Modules/Example/\346\226\207\346\241\243.md" @@ -0,0 +1,2 @@ +# Գ˵ +ԳļڿʱЧIJԣڷǰְDemoļע͵ From a62701849b03e3c374a3cda8d1ad4e23c083c8e7 Mon Sep 17 00:00:00 2001 From: CXUtk <2237367067@qq.com> Date: Fri, 30 Jun 2023 23:05:29 -0700 Subject: [PATCH 03/13] spring fix --- .../PBEngine/Constrains/SpringConstrain.cs | 81 ++++++++++++++----- .../Physics/PBEngine/PhysicsSimulation.cs | 10 +-- .../Physics/PBEngine/RigidBody2D.cs | 6 ++ Sources/Modules/Example/PhysicsDemo.cs | 36 ++++----- 4 files changed, 87 insertions(+), 46 deletions(-) diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs b/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs index ed2447b90..144620b7a 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs @@ -40,7 +40,25 @@ public SpringConstrain(PhysicsObject A, PhysicsObject B, float elasticity, float _localPosB = localPosB; } - public override void Apply(float deltaTime) + public static double CalculateShortestAngleDifference(double angle1, double angle2) + { + double difference = angle2 - angle1; + difference = (difference + Math.PI) % (2 * Math.PI) - Math.PI; + + // Adjust the difference to the shortest rotating radians + if (difference < -Math.PI) + { + difference += 2 * Math.PI; + } + else if (difference >= Math.PI) + { + difference -= 2 * Math.PI; + } + + return difference; + } + + public override void Apply(float deltaTime) { var pA = _objA.LocalToWorldPos(_localPosA); var pB = _objB.LocalToWorldPos(_localPosB); @@ -54,32 +72,51 @@ public override void Apply(float deltaTime) var curPosA = pA + _lambda * _objA.RigidBody.InvMass * -unit; var curPosB = pB + _lambda * _objB.RigidBody.InvMass * unit; - var ra = Matrix2x2.CreateRotationMatrix(_objA.Rotation).Multiply(_localPosA); - var rb = Matrix2x2.CreateRotationMatrix(_objB.Rotation).Multiply(_localPosB); - var da = (_lambda * _objA.RigidBody.InvMass * -unit).SafeNormalize(Vector2.Zero); - var db = (_lambda * _objB.RigidBody.InvMass * unit).SafeNormalize(Vector2.Zero); - var oldVelA = Vector2.Dot(_objA.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ra, _objA.RigidBody.AngularVelocity), da); - var oldVelB = Vector2.Dot(_objB.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, _objB.RigidBody.AngularVelocity), db); - - double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ra), da); - double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), db); - double R1 = rAdotN * rAdotN * _objA.RigidBody.GlobalInverseInertiaTensor; - double R2 = rBdotN * rBdotN * _objB.RigidBody.GlobalInverseInertiaTensor; - var impluseA = ((curPosA - pA).Length() / deltaTime - oldVelA) / (_objA.RigidBody.InvMass + (float)R1); - var impluseB = ((curPosB - pB).Length() / deltaTime - oldVelB) / (_objB.RigidBody.InvMass + (float)R2); - - if (_objA.RigidBody.InvMass != 0) + + _objB.RigidBody.AddForce(40 * (L - _restLength) * -unit, _localPosB); + //float rotB_p = (pB - _objB.RigidBody.CentroidWorldSpace).ToRotation() - _localPosB.ToRotation(); + //float rotB = (curPosB - _objB.RigidBody.CentroidWorldSpace).ToRotation() - _localPosB.ToRotation(); + + //_objB.Rotation = rotB; + + //_objB.RigidBody.CentroidWorldSpace += curPosB - _objB.LocalToWorldPos(_localPosB); + //_objB.RigidBody.LinearVelocity = (_objB.Position - _objB.OldPosition) / deltaTime; + //_objB.RigidBody.AngularVelocity = (float)CalculateShortestAngleDifference(_objB.OldRotation, _objB.Rotation) / deltaTime; + + //var ra = Matrix2x2.CreateRotationMatrix(_objA.Rotation).Multiply(_localPosA); + //var rb = Matrix2x2.CreateRotationMatrix(_objB.Rotation).Multiply(_localPosB); + //var da = (_lambda * _objA.RigidBody.InvMass * -unit).SafeNormalize(Vector2.Zero); + //var db = (_lambda * _objB.RigidBody.InvMass * unit).SafeNormalize(Vector2.Zero); + //var oldVelA = Vector2.Dot(_objA.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ra, _objA.RigidBody.AngularVelocity), da); + //var oldVelB = Vector2.Dot(_objB.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, _objB.RigidBody.AngularVelocity), db); + + //double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ra), da); + //double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), db); + //double R1 = rAdotN * rAdotN * _objA.RigidBody.GlobalInverseInertiaTensor; + //double R2 = rBdotN * rBdotN * _objB.RigidBody.GlobalInverseInertiaTensor; + //var impluseA = ((curPosA - pA).Length() / deltaTime - oldVelA) / (_objA.RigidBody.InvMass + (float)R1); + //var impluseB = ((curPosB - pB).Length() / deltaTime - oldVelB) / (_objB.RigidBody.InvMass + (float)R2); + + if (_objA.RigidBody.InvMass != 0) { - _objA.RigidBody.AddImpluseImmediate(impluseA * da, ra); + // _objA.RigidBody.AddImpluseImmediate(impluseA * da, ra); _objA.Position += _objA.RigidBody.LinearVelocity * deltaTime; _objA.Rotation += _objA.RigidBody.AngularVelocity * deltaTime; - } + + _objA.RigidBody.LinearVelocity = (_objA.Position - _objA.OldPosition) / deltaTime; + } if (_objB.RigidBody.InvMass != 0) { - _objB.RigidBody.AddImpluseImmediate(impluseB * db, rb); - _objB.Position += _objB.RigidBody.LinearVelocity * deltaTime; - _objB.Rotation += _objB.RigidBody.AngularVelocity * deltaTime; - } + // _objB.RigidBody.AddImpluseImmediate(impluseB * db, rb); + //_objB.Position += _objB.RigidBody.LinearVelocity * deltaTime; + //_objB.Rotation += _objB.RigidBody.AngularVelocity * deltaTime; + //var test = _objB.LocalToWorldPos(_localPosB); + //if (true) + // ; + + //_objB.RigidBody.LinearVelocity = (_objB.Position - _objB.OldPosition) / deltaTime; + //_objB.RigidBody.AngularVelocity = (_objB.Rotation - _objB.OldRotation) / deltaTime; + } //_objA.RigidBody.AngularVelocity = Utils.Cross(_localPosA, _objA.RigidBody.LinearVelocity); //_objB.RigidBody.AngularVelocity = Utils.Cross(_localPosB, _objB.RigidBody.LinearVelocity); } diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs index f29270279..5c7f800cc 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs @@ -160,7 +160,11 @@ public List GetCurrentWireFrames() /// private void PreIntegration(float deltaTime) { - foreach (PhysicsObject pobj in _staticPhysObjects) + foreach (var joint in _constrains) + { + joint.Apply(deltaTime); + } + foreach (PhysicsObject pobj in _staticPhysObjects) { pobj.RecordOldState(); } @@ -185,10 +189,6 @@ private void Resolve(float deltaTime, Stopwatch broadPhase, Stopwatch narrowPhas broadPhase.Stop(); narrowPhase.Start(); - foreach (var joint in _constrains) - { - joint.Apply(deltaTime); - } foreach (var pair in pairs) { CollisionInfo info; diff --git a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs index 43e294f08..451ba9193 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs @@ -507,6 +507,12 @@ public void Integrate(float deltaTime) // return impluseArray; //} + public void AddForce(Vector2 force, Vector2 relPos) + { + _force += force; + _torque += GeometryUtils.Cross(relPos, force); + } + /// /// 把冲量数据累加到这个刚体上 /// diff --git a/Sources/Modules/Example/PhysicsDemo.cs b/Sources/Modules/Example/PhysicsDemo.cs index b0b54c1e2..4738e910e 100644 --- a/Sources/Modules/Example/PhysicsDemo.cs +++ b/Sources/Modules/Example/PhysicsDemo.cs @@ -12,6 +12,7 @@ using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Constrains; namespace Everglow.Example; @@ -25,7 +26,6 @@ public override void OnEnterWorld() } public override void PostUpdate() { - Main.LocalPlayer.velocity = _saveVelocity; } public override void PreUpdateMovement() @@ -40,8 +40,7 @@ public override void PreUpdateMovement() // Main.LocalPlayer.Center = Physics.Utils.ConvertToPhysicsSpace(Display.Instance._dummyPlayer.Position); Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity * dt); - _saveVelocity = Main.LocalPlayer.velocity; - if (preVelY > 0 && Math.Abs(Main.LocalPlayer.velocity.Y) < 0.01f) + if (preVelY > 0 && Math.Abs(Main.LocalPlayer.velocity.Y) < 0.04f) { Main.LocalPlayer.velocity.Y = 0; } @@ -50,14 +49,14 @@ public override void PreUpdateMovement() if (_movingPanel != null) { - if (Main.time % 1200 < 600) - { - _movingPanel.RigidBody.LinearVelocity = new Vector2(-3, 0); - } - else if (Main.time % 1200 >= 600) - { - _movingPanel.RigidBody.LinearVelocity = new Vector2(3, 0); - } + //if (Main.time % 1200 < 600) + //{ + // _movingPanel.RigidBody.LinearVelocity = new Vector2(-3, 0); + //} + //else if (Main.time % 1200 >= 600) + //{ + // _movingPanel.RigidBody.LinearVelocity = new Vector2(3, 0); + //} } base.PreUpdateMovement(); @@ -87,11 +86,10 @@ public override void SetControls() if (Main.keyState[Keys.T] == KeyState.Down && Main.oldKeyState[Keys.T] == KeyState.Up) { var dynamicBox = new PhysicsObject( - new BoxCollider(256, 64), new RigidBody2D(10233)); + new BoxCollider(256, 64), new RigidBody2D(1000)); dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center - new Vector2(0, 200)); dynamicBox.Rotation = 0.0f; - dynamicBox.RigidBody.MovementType = MovementType.Kinematic; - dynamicBox.RigidBody.UseGravity = false; + dynamicBox.RigidBody.MovementType = MovementType.Dynamic; PhysicsDemo.Instance._realSimulation.AddPhysicsObject(dynamicBox); _movingPanel = dynamicBox; @@ -103,14 +101,14 @@ public override void SetControls() var staticPlane1 = new PhysicsObject( new BoxCollider(300, 32), null); staticPlane1.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center - new Vector2(0, 400)); - staticPlane1.Rotation = 0.4f; + staticPlane1.Rotation = 0.0f; PhysicsDemo.Instance._realSimulation.AddPhysicsObject(staticPlane1); - //var joint = new Spring(staticPlane1, dynamicBox, 40f, 200f, new Vector2(100, 0), new Vector2(100, 0)); - //Display.Instance._realSimulation.AddConstrain(joint); + var joint = new SpringConstrain(staticPlane1, dynamicBox, 40f, 200f, new Vector2(100, 0), new Vector2(100, 0)); + PhysicsDemo.Instance._realSimulation.AddConstrain(joint); - //var joint1 = new Spring(staticPlane1, dynamicBox, 40f, 200f, new Vector2(-100, 0), new Vector2(-100, 0)); - //Display.Instance._realSimulation.AddConstrain(joint1); + var joint1 = new SpringConstrain(staticPlane1, dynamicBox, 100f, 200f, new Vector2(-100, 0), new Vector2(-100, 0)); + PhysicsDemo.Instance._realSimulation.AddConstrain(joint1); //if (Main.rand.NextBool(2)) //{ From 4664e09910c3154001037ed8114a64ae72eb0360 Mon Sep 17 00:00:00 2001 From: CXUtk <2237367067@qq.com> Date: Sat, 1 Jul 2023 00:28:56 -0700 Subject: [PATCH 04/13] inertia --- .../Physics/PBEngine/PhysicsSimulation.cs | 5 +- .../Physics/PBEngine/RigidBody2D.cs | 110 ++++++++++-------- Sources/Modules/Example/PhysicsDemo.cs | 52 ++++++--- 3 files changed, 101 insertions(+), 66 deletions(-) diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs index 5c7f800cc..1c52c3027 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs @@ -111,7 +111,8 @@ public void Initialize() /// public void Update(float deltaTime) { - foreach (PhysicsObject pobj in _dynamicPhysObjects) + CleanThisFrame(); + foreach (PhysicsObject pobj in _dynamicPhysObjects) { pobj.ApplyGravity(new Vector2(0, -_gravity)); } @@ -128,7 +129,7 @@ public void Update(float deltaTime) Resolve(dt, _stopwatchBroadPhase, _stopwatchNarrowPhase); } - CleanThisFrame(); + } public void CleanThisFrame() diff --git a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs index 451ba9193..559ce04f7 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs @@ -130,6 +130,11 @@ public float Friction set => _friction = value; } + public Vector2 TangetRelativeVelocity + { + get => _tangentRelativeVelocity; + } + private double _localInverseInertiaTensor; private double _globalInverseInertiaTensor; @@ -146,6 +151,8 @@ public float Friction private float _stiffness; private float _friction; + private Vector2 _tangentRelativeVelocity; + private List _impluses; private bool _isAwake; @@ -165,7 +172,9 @@ public RigidBody2D(float mass) _impluses = new List(); _stiffness = 0.5f; _friction = 0.5f; - } + _tangentRelativeVelocity = Vector2.Zero; + + } private void CalculateMassCentroidAndMoI() @@ -243,55 +252,56 @@ public void CleanUp() _linearVelocity *= 1 - _drag * _drag; _angularVelocity *= 1 - _angularDrag * _angularDrag; - } + _tangentRelativeVelocity = Vector2.Zero; + } - //public void RespondToEvents(List events, float deltaTime) - //{ - // var ri0 = events[0].LocalOffsetSrc; - // var rb0 = events[0].LocalOffsetTarget; - // var n0 = events[0].Normal; - // var va0 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); - // var vb0 = events[0].Target.RigidBody._linearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); - // float relv0_n = Vector2.Dot(va0 - vb0, n0); - - // var ri1 = events[1].LocalOffsetSrc; - // var rb1 = events[1].LocalOffsetTarget; - // var n1 = events[1].Normal; - // var va1 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); - // var vb1 = events[1].Target.RigidBody._linearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); - // float relv1_n = Vector2.Dot(va1 - vb1, n1); - // Matrix2x2 matrix = new Matrix2x2() - // { - // [0, 0] = InvMass + events[0].Target.RigidBody.InvMass - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri0, n0))), n0) - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor - // * Utils.Cross(rb0, n0))), n0), - // [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri1, n1))), n0), - - // [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri0, n0))), n1), - // [1, 1] = InvMass + events[1].Target.RigidBody.InvMass - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri1, n1))), n1) - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor - // * Utils.Cross(rb1, n1))), n1), - // }; - // float C = 0.4f; - // var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); - // if (!J.HasNaNs()) - // { - // AddImpluse(J.X * events[0].Normal, ri0, events[0].Time, events[0].Normal); - // AddImpluse(J.Y * events[1].Normal, ri1, events[1].Time, events[1].Normal); - // } - // var t = matrix.Multiply(J); - // if (true) - // ; - //} - public void ApplyAngularVelocity(float w) + //public void RespondToEvents(List events, float deltaTime) + //{ + // var ri0 = events[0].LocalOffsetSrc; + // var rb0 = events[0].LocalOffsetTarget; + // var n0 = events[0].Normal; + // var va0 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); + // var vb0 = events[0].Target.RigidBody._linearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); + // float relv0_n = Vector2.Dot(va0 - vb0, n0); + + // var ri1 = events[1].LocalOffsetSrc; + // var rb1 = events[1].LocalOffsetTarget; + // var n1 = events[1].Normal; + // var va1 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); + // var vb1 = events[1].Target.RigidBody._linearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); + // float relv1_n = Vector2.Dot(va1 - vb1, n1); + // Matrix2x2 matrix = new Matrix2x2() + // { + // [0, 0] = InvMass + events[0].Target.RigidBody.InvMass + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri0, n0))), n0) + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(rb0, n0))), n0), + // [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri1, n1))), n0), + + // [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri0, n0))), n1), + // [1, 1] = InvMass + events[1].Target.RigidBody.InvMass + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri1, n1))), n1) + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(rb1, n1))), n1), + // }; + // float C = 0.4f; + // var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); + // if (!J.HasNaNs()) + // { + // AddImpluse(J.X * events[0].Normal, ri0, events[0].Time, events[0].Normal); + // AddImpluse(J.Y * events[1].Normal, ri1, events[1].Time, events[1].Normal); + // } + // var t = matrix.Multiply(J); + // if (true) + // ; + //} + public void ApplyAngularVelocity(float w) { _angularVelocity += w; } @@ -348,6 +358,8 @@ private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime float vel_n = Vector2.Dot(va - vb, e.Normal); Vector2 vt = (va - vb) - vel_n * e.Normal; + _tangentRelativeVelocity -= vb / count; + e.Target.RigidBody._tangentRelativeVelocity -= va / count; float vel_t = vt.Length(); float friction = Math.Max(0, (_friction + e.Target.RigidBody.Friction) / 2); diff --git a/Sources/Modules/Example/PhysicsDemo.cs b/Sources/Modules/Example/PhysicsDemo.cs index 4738e910e..3dcb32ff0 100644 --- a/Sources/Modules/Example/PhysicsDemo.cs +++ b/Sources/Modules/Example/PhysicsDemo.cs @@ -20,16 +20,25 @@ internal class PhysicsPlayer : ModPlayer { private PhysicsObject _movingPanel; private Vector2 _prevPos; - private Vector2 _saveVelocity; + public Vector2 _extraVelocity; public override void OnEnterWorld() { } public override void PostUpdate() { + Main.LocalPlayer.velocity += _extraVelocity; + _extraVelocity = PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity * 0.1f; + _extraVelocity = Vector2.Dot(_extraVelocity, new Vector2(1, 0)) * new Vector2(1, 0); + } + public override void PreUpdate() + { + + base.PreUpdate(); } public override void PreUpdateMovement() { + Main.LocalPlayer.velocity -= _extraVelocity; _prevPos = Main.LocalPlayer.Center; float dt = 0.1f; var oldPos = Main.LocalPlayer.Center; @@ -44,19 +53,19 @@ public override void PreUpdateMovement() { Main.LocalPlayer.velocity.Y = 0; } - + // Main.LocalPlayer.velocity -= _extraVelocity; if (_movingPanel != null) { - //if (Main.time % 1200 < 600) - //{ - // _movingPanel.RigidBody.LinearVelocity = new Vector2(-3, 0); - //} - //else if (Main.time % 1200 >= 600) - //{ - // _movingPanel.RigidBody.LinearVelocity = new Vector2(3, 0); - //} + if (Main.time % 1200 < 600) + { + _movingPanel.RigidBody.LinearVelocity = new Vector2(-3, 0); + } + else if (Main.time % 1200 >= 600) + { + _movingPanel.RigidBody.LinearVelocity = new Vector2(3, 0); + } } base.PreUpdateMovement(); @@ -89,7 +98,8 @@ public override void SetControls() new BoxCollider(256, 64), new RigidBody2D(1000)); dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center - new Vector2(0, 200)); dynamicBox.Rotation = 0.0f; - dynamicBox.RigidBody.MovementType = MovementType.Dynamic; + dynamicBox.RigidBody.MovementType = MovementType.Kinematic; + dynamicBox.RigidBody.UseGravity = false; PhysicsDemo.Instance._realSimulation.AddPhysicsObject(dynamicBox); _movingPanel = dynamicBox; @@ -104,11 +114,11 @@ public override void SetControls() staticPlane1.Rotation = 0.0f; PhysicsDemo.Instance._realSimulation.AddPhysicsObject(staticPlane1); - var joint = new SpringConstrain(staticPlane1, dynamicBox, 40f, 200f, new Vector2(100, 0), new Vector2(100, 0)); - PhysicsDemo.Instance._realSimulation.AddConstrain(joint); + //var joint = new SpringConstrain(staticPlane1, dynamicBox, 40f, 200f, new Vector2(100, 0), new Vector2(100, 0)); + //PhysicsDemo.Instance._realSimulation.AddConstrain(joint); - var joint1 = new SpringConstrain(staticPlane1, dynamicBox, 100f, 200f, new Vector2(-100, 0), new Vector2(-100, 0)); - PhysicsDemo.Instance._realSimulation.AddConstrain(joint1); + //var joint1 = new SpringConstrain(staticPlane1, dynamicBox, 100f, 200f, new Vector2(-100, 0), new Vector2(-100, 0)); + //PhysicsDemo.Instance._realSimulation.AddConstrain(joint1); //if (Main.rand.NextBool(2)) //{ @@ -145,6 +155,8 @@ public override void PostUpdateEverything() { //rigidBodyDisplay.Update(); } + + public override void PostDrawTiles() { //rigidBodyDisplay.Draw(Main.spriteBatch); @@ -174,9 +186,19 @@ public override void Load() Instance = this; ReStart(); _renderShader = ModContent.Request("Everglow/Example/VFX/PureColor"); + + + On_Player.PlayerFrame += On_Player_PlayerFrame; base.Load(); } + private void On_Player_PlayerFrame(On_Player.orig_PlayerFrame orig, Player self) + { + Main.LocalPlayer.velocity += self.GetModPlayer()._extraVelocity; + orig(self); + Main.LocalPlayer.velocity -= self.GetModPlayer()._extraVelocity; + } + public void ReStart() { var terrariaCollisionGroup = new CollisionGraph(); From e63903f47fbf79429986c0d1b6bbd325ad937270 Mon Sep 17 00:00:00 2001 From: CXUtk <2237367067@qq.com> Date: Sat, 1 Jul 2023 09:59:24 -0700 Subject: [PATCH 05/13] Close other modules --- Localization/en-US_Mods.Everglow.hjson | 2994 +---------------- Sources/Everglow/Module.Build.props | 3 +- .../Modules/Example/Everglow.Example.csproj | 3 - Sources/Modules/Example/PhysicsDemo.cs | 2 +- 4 files changed, 9 insertions(+), 2993 deletions(-) diff --git a/Localization/en-US_Mods.Everglow.hjson b/Localization/en-US_Mods.Everglow.hjson index efc8c0216..7d96a12ce 100644 --- a/Localization/en-US_Mods.Everglow.hjson +++ b/Localization/en-US_Mods.Everglow.hjson @@ -1,2926 +1,20 @@ -Tiles: { - BottleOfNutShellFlowers.MapEntry: Bottle Of Nut Shell Flowers - PlumBlossomInABowl.MapEntry: Plum Blossom In A Bowl - WhiteLotusBonsai.MapEntry: White Lotus Bonsai - BloodifyPlatform.MapEntry: Bloodify Platform - BloodyMossWheel.MapEntry: Bloody Moss Wheel - BloodyMossWheelFinished.MapEntry: Bloody Moss Wheel Finished - StoneTusk.MapEntry: Stone Tusk - StoneTuskSmall.MapEntry: Stone Tusk Small - StrangeTuskStone.MapEntry: Strange Tusk Stone - StrangeTuskStone1.MapEntry: Strange Tusk Stone1 - StrangeTuskStone2.MapEntry: Strange Tusk Stone2 - StrangeTuskStone3.MapEntry: Strange Tusk Stone3 - StrangeTuskStone4.MapEntry: Strange Tusk Stone4 - TuskRelic.MapEntry: Tusk Relic - BlackFren.MapEntry: Black Fren - BlackFrenLarge.MapEntry: Black Fren Large - BlackStarShrub.MapEntry: Black Star Shrub - BlackStarShrubSmall.MapEntry: Black Star Shrub Small - BlackVine.MapEntry: Black Vine - BlueBlossom.MapEntry: Blue Blossom - CocoonRock.MapEntry: Cocoon Rock - GlowingReed.MapEntry: Glowing Reed - LampLotus.MapEntry: Lamp Lotus - MothWorldDoor.MapEntry: Moth World Door - CyanVineBar.MapEntry: Cyan Vine Bar - CyanVineOreTile.MapEntry: Cyan Vine Ore Tile - CyanVineStone.MapEntry: Cyan Vine Stone - LiftLamp.MapEntry: Lift Lamp -} - -Biomes: { - TuskBiome: { - DisplayName: Tusk Biome - TownNPCDialogueName: the Tusk Biome - } - - TuskSurfaceBiome: { - DisplayName: Tusk Surface Biome - TownNPCDialogueName: the Tusk Surface Biome - } - - FireflyBiome: { - DisplayName: Firefly Biome - TownNPCDialogueName: the Firefly Biome - } - - YggdrasilTownBiome: { - DisplayName: Yggdrasil Town Biome - TownNPCDialogueName: the Yggdrasil Town Biome - } - - KelpCurtainBiome: { - DisplayName: Kelp Curtain Biome - TownNPCDialogueName: the Kelp Curtain Biome - } -} - -MapEntry.FluorescentTree: Mods.Everglow.MapEntry.FluorescentTree - -Buffs: { - BloodSwordStrikeBuff: { - DisplayName: Blood Sword Strike Buff - Description: Mods.Everglow.Buffs.BloodSwordStrikeBuff.Description - } - - LifeRotten: { - DisplayName: Life Rotten - Description: Mods.Everglow.Buffs.LifeRotten.Description - } - - OnElectric: { - DisplayName: On Electric - Description: Mods.Everglow.Buffs.OnElectric.Description - } - - StarCrack: { - DisplayName: Star Crack - Description: Mods.Everglow.Buffs.StarCrack.Description - } - - AppleBuff: { - DisplayName: Apple Buff - Description: Mods.Everglow.Buffs.AppleBuff.Description - } - - ApplePieBuff: { - DisplayName: Apple Pie Buff - Description: Mods.Everglow.Buffs.ApplePieBuff.Description - } - - ApricotBuff: { - DisplayName: Apricot Buff - Description: Mods.Everglow.Buffs.ApricotBuff.Description - } - - BaconBuff: { - DisplayName: Bacon Buff - Description: Mods.Everglow.Buffs.BaconBuff.Description - } - - BananaBuff: { - DisplayName: Banana Buff - Description: Mods.Everglow.Buffs.BananaBuff.Description - } - - BananaSplitBuff: { - DisplayName: Banana Split Buff - Description: Mods.Everglow.Buffs.BananaSplitBuff.Description - } - - BBQRibsBuff: { - DisplayName: B B Q Ribs Buff - Description: Mods.Everglow.Buffs.BBQRibsBuff.Description - } - - BlackCurrantBuff: { - DisplayName: Black Currant Buff - Description: Mods.Everglow.Buffs.BlackCurrantBuff.Description - } - - BloodOrangeBuff: { - DisplayName: Blood Orange Buff - Description: Mods.Everglow.Buffs.BloodOrangeBuff.Description - } - - BowlofSoupBuff: { - DisplayName: Bowlof Soup Buff - Description: Mods.Everglow.Buffs.BowlofSoupBuff.Description - } - - BunnyStewBuff: { - DisplayName: Bunny Stew Buff - Description: Mods.Everglow.Buffs.BunnyStewBuff.Description - } - - BurgerBuff: { - DisplayName: Burger Buff - Description: Mods.Everglow.Buffs.BurgerBuff.Description - } - - CherryBuff: { - DisplayName: Cherry Buff - Description: Mods.Everglow.Buffs.CherryBuff.Description - } - - ChickenNuggetBuff: { - DisplayName: Chicken Nugget Buff - Description: Mods.Everglow.Buffs.ChickenNuggetBuff.Description - } - - ChocolateChipCookieBuff: { - DisplayName: Chocolate Chip Cookie Buff - Description: Mods.Everglow.Buffs.ChocolateChipCookieBuff.Description - } - - ChristmasPuddingBuff: { - DisplayName: Christmas Pudding Buff - Description: Mods.Everglow.Buffs.ChristmasPuddingBuff.Description - } - - CoconutBuff: { - DisplayName: Coconut Buff - Description: Mods.Everglow.Buffs.CoconutBuff.Description - } - - CookedFishBuff: { - DisplayName: Cooked Fish Buff - Description: Mods.Everglow.Buffs.CookedFishBuff.Description - } - - CookedMarshmallowBuff: { - DisplayName: Cooked Marshmallow Buff - Description: Mods.Everglow.Buffs.CookedMarshmallowBuff.Description - } - - CookedShrimpBuff: { - DisplayName: Cooked Shrimp Buff - Description: Mods.Everglow.Buffs.CookedShrimpBuff.Description - } - - DragonfruitBuff: { - DisplayName: Dragonfruit Buff - Description: Mods.Everglow.Buffs.DragonfruitBuff.Description - } - - ElderberryBuff: { - DisplayName: Elderberry Buff - Description: Mods.Everglow.Buffs.ElderberryBuff.Description - } - - EscargotBuff: { - DisplayName: Escargot Buff - Description: Mods.Everglow.Buffs.EscargotBuff.Description - } - - FriedEggBuff: { - DisplayName: Fried Egg Buff - Description: Mods.Everglow.Buffs.FriedEggBuff.Description - } - - FriesBuff: { - DisplayName: Fries Buff - Description: Mods.Everglow.Buffs.FriesBuff.Description - } - - FroggleBunwichBuff: { - DisplayName: Froggle Bunwich Buff - Description: Mods.Everglow.Buffs.FroggleBunwichBuff.Description - } - - FruitSaladBuff: { - DisplayName: Fruit Salad Buff - Description: Mods.Everglow.Buffs.FruitSaladBuff.Description - } - - GingerbreadCookieBuff: { - DisplayName: Gingerbread Cookie Buff - Description: Mods.Everglow.Buffs.GingerbreadCookieBuff.Description - } - - GoldenDelightBuff: { - DisplayName: Golden Delight Buff - Description: Mods.Everglow.Buffs.GoldenDelightBuff.Description - } - - GrapefruitBuff: { - DisplayName: Grapefruit Buff - Description: Mods.Everglow.Buffs.GrapefruitBuff.Description - } - - GrapesBuff: { - DisplayName: Grapes Buff - Description: Mods.Everglow.Buffs.GrapesBuff.Description - } - - GrilledSquirrelBuff: { - DisplayName: Grilled Squirrel Buff - Description: Mods.Everglow.Buffs.GrilledSquirrelBuff.Description - } - - GrubSoupBuff: { - DisplayName: Grub Soup Buff - Description: Mods.Everglow.Buffs.GrubSoupBuff.Description - } - - HotdogBuff: { - DisplayName: Hotdog Buff - Description: Mods.Everglow.Buffs.HotdogBuff.Description - } - - IceCreamBuff: { - DisplayName: Ice Cream Buff - Description: Mods.Everglow.Buffs.IceCreamBuff.Description - } - - LemonBuff: { - DisplayName: Lemon Buff - Description: Mods.Everglow.Buffs.LemonBuff.Description - } - - LobsterTailBuff: { - DisplayName: Lobster Tail Buff - Description: Mods.Everglow.Buffs.LobsterTailBuff.Description - } - - MangoBuff: { - DisplayName: Mango Buff - Description: Mods.Everglow.Buffs.MangoBuff.Description - } - - MarshmallowBuff: { - DisplayName: Marshmallow Buff - Description: Mods.Everglow.Buffs.MarshmallowBuff.Description - } - - MonsterLasagnaBuff: { - DisplayName: Monster Lasagna Buff - Description: Mods.Everglow.Buffs.MonsterLasagnaBuff.Description - } - - NachosBuff: { - DisplayName: Nachos Buff - Description: Mods.Everglow.Buffs.NachosBuff.Description - } - - PadThaiBuff: { - DisplayName: Pad Thai Buff - Description: Mods.Everglow.Buffs.PadThaiBuff.Description - } - - PeachBuff: { - DisplayName: Peach Buff - Description: Mods.Everglow.Buffs.PeachBuff.Description - } - - PhoBuff: { - DisplayName: Pho Buff - Description: Mods.Everglow.Buffs.PhoBuff.Description - } - - PineappleBuff: { - DisplayName: Pineapple Buff - Description: Mods.Everglow.Buffs.PineappleBuff.Description - } - - PizzaBuff: { - DisplayName: Pizza Buff - Description: Mods.Everglow.Buffs.PizzaBuff.Description - } - - PlumBuff: { - DisplayName: Plum Buff - Description: Mods.Everglow.Buffs.PlumBuff.Description - } - - PotatoChipsBuff: { - DisplayName: Potato Chips Buff - Description: Mods.Everglow.Buffs.PotatoChipsBuff.Description - } - - PumpkinPieBuff: { - DisplayName: Pumpkin Pie Buff - Description: Mods.Everglow.Buffs.PumpkinPieBuff.Description - } - - RambutanBuff: { - DisplayName: Rambutan Buff - Description: Mods.Everglow.Buffs.RambutanBuff.Description - } - - RoastedBirdBuff: { - DisplayName: Roasted Bird Buff - Description: Mods.Everglow.Buffs.RoastedBirdBuff.Description - } - - RoastedDuckBuff: { - DisplayName: Roasted Duck Buff - Description: Mods.Everglow.Buffs.RoastedDuckBuff.Description - } - - SashimiBuff: { - DisplayName: Sashimi Buff - Description: Mods.Everglow.Buffs.SashimiBuff.Description - } - - SauteedFrogLegsBuff: { - DisplayName: Sauteed Frog Legs Buff - Description: Mods.Everglow.Buffs.SauteedFrogLegsBuff.Description - } - - SeafoodDinnerBuff: { - DisplayName: Seafood Dinner Buff - Description: Mods.Everglow.Buffs.SeafoodDinnerBuff.Description - } - - ShrimpPoBoyBuff: { - DisplayName: Shrimp Po Boy Buff - Description: Mods.Everglow.Buffs.ShrimpPoBoyBuff.Description - } - - ShuckedOysterBuff: { - DisplayName: Shucked Oyster Buff - Description: Mods.Everglow.Buffs.ShuckedOysterBuff.Description - } - - SpaghettiBuff: { - DisplayName: Spaghetti Buff - Description: Mods.Everglow.Buffs.SpaghettiBuff.Description - } - - StarfruitBuff: { - DisplayName: Starfruit Buff - Description: Mods.Everglow.Buffs.StarfruitBuff.Description - } - - SteakBuff: { - DisplayName: Steak Buff - Description: Mods.Everglow.Buffs.SteakBuff.Description - } - - SugarCookieBuff: { - DisplayName: Sugar Cookie Buff - Description: Mods.Everglow.Buffs.SugarCookieBuff.Description - } - - AleBuff: { - DisplayName: Ale Buff - Description: Mods.Everglow.Buffs.AleBuff.Description - } - - AppleJuiceBuff: { - DisplayName: Apple Juice Buff - Description: Mods.Everglow.Buffs.AppleJuiceBuff.Description - } - - BananaDaiquiriBuff: { - DisplayName: Banana Daiquiri Buff - Description: Mods.Everglow.Buffs.BananaDaiquiriBuff.Description - } - - BloodyMoscatoBuff: { - DisplayName: Bloody Moscato Buff - Description: Mods.Everglow.Buffs.BloodyMoscatoBuff.Description - } - - CoffeeCupBuff: { - DisplayName: Coffee Cup Buff - Description: Mods.Everglow.Buffs.CoffeeCupBuff.Description - } - - CreamSodaBuff: { - DisplayName: Cream Soda Buff - Description: Mods.Everglow.Buffs.CreamSodaBuff.Description - } - - FruitJuiceBuff: { - DisplayName: Fruit Juice Buff - Description: Mods.Everglow.Buffs.FruitJuiceBuff.Description - } - - GrapeJuiceBuff: { - DisplayName: Grape Juice Buff - Description: Mods.Everglow.Buffs.GrapeJuiceBuff.Description - } - - LemonadeBuff: { - DisplayName: Lemonade Buff - Description: Mods.Everglow.Buffs.LemonadeBuff.Description - } - - MilkCartonBuff: { - DisplayName: Milk Carton Buff - Description: Mods.Everglow.Buffs.MilkCartonBuff.Description - } - - MilkshakeBuff: { - DisplayName: Milkshake Buff - Description: Mods.Everglow.Buffs.MilkshakeBuff.Description - } - - PeachSangriaBuff: { - DisplayName: Peach Sangria Buff - Description: Mods.Everglow.Buffs.PeachSangriaBuff.Description - } - - PinaColadaBuff: { - DisplayName: Pina Colada Buff - Description: Mods.Everglow.Buffs.PinaColadaBuff.Description - } - - PrismaticPunchBuff: { - DisplayName: Prismatic Punch Buff - Description: Mods.Everglow.Buffs.PrismaticPunchBuff.Description - } - - SakeBuff: { - DisplayName: Sake Buff - Description: Mods.Everglow.Buffs.SakeBuff.Description - } - - SmoothieofDarknessBuff: { - DisplayName: Smoothieof Darkness Buff - Description: Mods.Everglow.Buffs.SmoothieofDarknessBuff.Description - } - - TeacupBuff: { - DisplayName: Teacup Buff - Description: Mods.Everglow.Buffs.TeacupBuff.Description - } - - TropicalSmoothieBuff: { - DisplayName: Tropical Smoothie Buff - Description: Mods.Everglow.Buffs.TropicalSmoothieBuff.Description - } - - ArcticshellfishWarshipBuff: { - DisplayName: Arcticshellfish Warship Buff - Description: Mods.Everglow.Buffs.ArcticshellfishWarshipBuff.Description - } - - BakedMusselBuff: { - DisplayName: Baked Mussel Buff - Description: Mods.Everglow.Buffs.BakedMusselBuff.Description - } - - BakedOysterBuff: { - DisplayName: Baked Oyster Buff - Description: Mods.Everglow.Buffs.BakedOysterBuff.Description - } - - CantaloupeJellyBuff: { - DisplayName: Cantaloupe Jelly Buff - Description: Mods.Everglow.Buffs.CantaloupeJellyBuff.Description - } - - CaramelPuddingBuff: { - DisplayName: Caramel Pudding Buff - Description: Mods.Everglow.Buffs.CaramelPuddingBuff.Description - } - - CrabseedWarshipBuff: { - DisplayName: Crabseed Warship Buff - Description: Mods.Everglow.Buffs.CrabseedWarshipBuff.Description - } - - DurianBuff: { - DisplayName: Durian Buff - Description: Mods.Everglow.Buffs.DurianBuff.Description - } - - EggTartBuff: { - DisplayName: Egg Tart Buff - Description: Mods.Everglow.Buffs.EggTartBuff.Description - } - - KiwiFruitBuff: { - DisplayName: Kiwi Fruit Buff - Description: Mods.Everglow.Buffs.KiwiFruitBuff.Description - } - - KiwiIceCreamBuff: { - DisplayName: Kiwi Ice Cream Buff - Description: Mods.Everglow.Buffs.KiwiIceCreamBuff.Description - } - - MangosteenBuff: { - DisplayName: Mangosteen Buff - Description: Mods.Everglow.Buffs.MangosteenBuff.Description - } - - OrangeIcecreamBuff: { - DisplayName: Orange Icecream Buff - Description: Mods.Everglow.Buffs.OrangeIcecreamBuff.Description - } - - SalmonInPepperBuff: { - DisplayName: Salmon In Pepper Buff - Description: Mods.Everglow.Buffs.SalmonInPepperBuff.Description - } - - SalmonWarshipBuff: { - DisplayName: Salmon Warship Buff - Description: Mods.Everglow.Buffs.SalmonWarshipBuff.Description - } - - SeafoodPizzaBuff: { - DisplayName: Seafood Pizza Buff - Description: Mods.Everglow.Buffs.SeafoodPizzaBuff.Description - } - - StinkyTofuBuff: { - DisplayName: Stinky Tofu Buff - Description: Mods.Everglow.Buffs.StinkyTofuBuff.Description - } - - StrawberryBuff: { - DisplayName: Strawberry Buff - Description: Mods.Everglow.Buffs.StrawberryBuff.Description - } - - StrawberryIcecreamBuff: { - DisplayName: Strawberry Icecream Buff - Description: Mods.Everglow.Buffs.StrawberryIcecreamBuff.Description - } - - SweetshrimpSushiBuff: { - DisplayName: Sweetshrimp Sushi Buff - Description: Mods.Everglow.Buffs.SweetshrimpSushiBuff.Description - } - - TamakoSushiBuff: { - DisplayName: Tamako Sushi Buff - Description: Mods.Everglow.Buffs.TamakoSushiBuff.Description - } - - WakameWarshipBuff: { - DisplayName: Wakame Warship Buff - Description: Mods.Everglow.Buffs.WakameWarshipBuff.Description - } - - WatermelonBuff: { - DisplayName: Watermelon Buff - Description: Mods.Everglow.Buffs.WatermelonBuff.Description - } - - WatermelonPlateBuff: { - DisplayName: Watermelon Plate Buff - Description: Mods.Everglow.Buffs.WatermelonPlateBuff.Description - } - - BlueHawaiiBuff: { - DisplayName: Blue Hawaii Buff - Description: Mods.Everglow.Buffs.BlueHawaiiBuff.Description - } - - B_52Buff: { - DisplayName: B_52 Buff - Description: Mods.Everglow.Buffs.B_52Buff.Description - } - - DreamYearningBuff: { - DisplayName: Dream Yearning Buff - Description: Mods.Everglow.Buffs.DreamYearningBuff.Description - } - - DryMartiniBuff: { - DisplayName: Dry Martini Buff - Description: Mods.Everglow.Buffs.DryMartiniBuff.Description - } - - GreenStormBuff: { - DisplayName: Green Storm Buff - Description: Mods.Everglow.Buffs.GreenStormBuff.Description - } - - KiwiJuiceBuff: { - DisplayName: Kiwi Juice Buff - Description: Mods.Everglow.Buffs.KiwiJuiceBuff.Description - } - - LonelyJellyfishBuff: { - DisplayName: Lonely Jellyfish Buff - Description: Mods.Everglow.Buffs.LonelyJellyfishBuff.Description - } - - OrangeJuiceBuff: { - DisplayName: Orange Juice Buff - Description: Mods.Everglow.Buffs.OrangeJuiceBuff.Description - } - - PiercoldWindBuff: { - DisplayName: Piercold Wind Buff - Description: Mods.Everglow.Buffs.PiercoldWindBuff.Description - } - - PurpleHooterBuff: { - DisplayName: Purple Hooter Buff - Description: Mods.Everglow.Buffs.PurpleHooterBuff.Description - } - - QuinceMarryBuff: { - DisplayName: Quince Marry Buff - Description: Mods.Everglow.Buffs.QuinceMarryBuff.Description - } - - RedWineBuff: { - DisplayName: Red Wine Buff - Description: Mods.Everglow.Buffs.RedWineBuff.Description - } - - SunriseBuff: { - DisplayName: Sunrise Buff - Description: Mods.Everglow.Buffs.SunriseBuff.Description - } - - TricolourBuff: { - DisplayName: Tricolour Buff - Description: Mods.Everglow.Buffs.TricolourBuff.Description - } - - WaterMelonJuiceBuff: { - DisplayName: Water Melon Juice Buff - Description: Mods.Everglow.Buffs.WaterMelonJuiceBuff.Description - } - - TuskStaff: { - DisplayName: Tusk Staff - Description: Mods.Everglow.Buffs.TuskStaff.Description - } - - GlowMothBuff: { - DisplayName: Glow Moth Buff - Description: Mods.Everglow.Buffs.GlowMothBuff.Description - } - - OnMoth: { - DisplayName: On Moth - Description: Mods.Everglow.Buffs.OnMoth.Description - } - - Freeze: { - DisplayName: Freeze - Description: Mods.Everglow.Buffs.Freeze.Description - } - - LaserWormBuff: { - DisplayName: Laser Worm Buff - Description: Mods.Everglow.Buffs.LaserWormBuff.Description - } - - ShadowSupervisor: { - DisplayName: Shadow Supervisor - Description: Mods.Everglow.Buffs.ShadowSupervisor.Description - } - - MoonAndFragrans: { - DisplayName: Moon And Fragrans - Description: Mods.Everglow.Buffs.MoonAndFragrans.Description - } - - MoonAndFragransII: { - DisplayName: Moon And Fragrans I I - Description: Mods.Everglow.Buffs.MoonAndFragransII.Description - } - - MoonAndFragransIII: { - DisplayName: Moon And Fragrans I I I - Description: Mods.Everglow.Buffs.MoonAndFragransIII.Description - } - - WaterBoltII: { - DisplayName: Water Bolt I I - Description: Mods.Everglow.Buffs.WaterBoltII.Description - } - - BlueImmune: { - DisplayName: Blue Immune - Description: Mods.Everglow.Buffs.BlueImmune.Description - } - - BrownImmune: { - DisplayName: Brown Immune - Description: Mods.Everglow.Buffs.BrownImmune.Description - } - - GreenImmune: { - DisplayName: Green Immune - Description: Mods.Everglow.Buffs.GreenImmune.Description - } - - PinkImmune: { - DisplayName: Pink Immune - Description: Mods.Everglow.Buffs.PinkImmune.Description - } - - PurpleImmune: { - DisplayName: Purple Immune - Description: Mods.Everglow.Buffs.PurpleImmune.Description - } - - RedImmune: { - DisplayName: Red Immune - Description: Mods.Everglow.Buffs.RedImmune.Description - } - - WhiteImmune: { - DisplayName: White Immune - Description: Mods.Everglow.Buffs.WhiteImmune.Description - } - - YellowImmune: { - DisplayName: Yellow Immune - Description: Mods.Everglow.Buffs.YellowImmune.Description - } - - CactusBallBuff: { - DisplayName: Cactus Ball Buff - Description: Mods.Everglow.Buffs.CactusBallBuff.Description - } - - DeathFlame: { - DisplayName: Death Flame - Description: Mods.Everglow.Buffs.DeathFlame.Description - } -} - -Items: { - BottleOfNutShellFlowers: { - DisplayName: Bottle Of Nut Shell Flowers - Tooltip: "" - } - - PlumBlossomInABowl: { - DisplayName: Plum Blossom In A Bowl - Tooltip: "" - } - - WhiteLotusBonsai: { - DisplayName: White Lotus Bonsai - Tooltip: "" - } - - BloodGoldBayonet: { - DisplayName: Blood Gold Bayonet - Tooltip: "" - } - - BlueRibbonBayonet: { - DisplayName: Blue Ribbon Bayonet - Tooltip: "" - } - - CopperBayonet: { - DisplayName: Copper Bayonet - Tooltip: "" - } - - DreamStar: { - DisplayName: Dream Star - Tooltip: "" - } - - EnchantedBayonet: { - DisplayName: Enchanted Bayonet - Tooltip: "" - } - - EternalNight: { - DisplayName: Eternal Night - Tooltip: "" - } - - GoldenStabbingSword: { - DisplayName: Golden Stabbing Sword - Tooltip: "" - } - - HolyBayonet: { - DisplayName: Holy Bayonet - Tooltip: "" - } - - IronStabbingSword: { - DisplayName: Iron Stabbing Sword - Tooltip: "" - } - - LeadStabbingSword: { - DisplayName: Lead Stabbing Sword - Tooltip: "" - } - - PlatinumStabbingSword: { - DisplayName: Platinum Stabbing Sword - Tooltip: "" - } - - PrisonFireBayonet: { - DisplayName: Prison Fire Bayonet - Tooltip: "" - } - - RedRibbonBayonet: { - DisplayName: Red Ribbon Bayonet - Tooltip: "" - } - - RottenGoldBayonet: { - DisplayName: Rotten Gold Bayonet - Tooltip: "" - } - - SilverStabbingSword: { - DisplayName: Silver Stabbing Sword - Tooltip: "" - } - - TinStabbingSword: { - DisplayName: Tin Stabbing Sword - Tooltip: "" - } - - TungstenStabbingSword: { - DisplayName: Tungsten Stabbing Sword - Tooltip: "" - } - - VegetationBayonet: { - DisplayName: Vegetation Bayonet - Tooltip: "" - } - - YoenLeZed: { - DisplayName: Yoen Le Zed - Tooltip: "" - } - - StarSeed: { - DisplayName: Star Seed - Tooltip: "" - } - - FryingPanItem: { - DisplayName: Frying Pan Item - Tooltip: "" - } - - BloodGlucoseMonitor: { - DisplayName: Blood Glucose Monitor - Tooltip: "" - } - - OsmoticPressureMonitor: { - DisplayName: Osmotic Pressure Monitor - Tooltip: "" - } - - ArcticshellfishWarship: { - DisplayName: Arcticshellfish Warship - Tooltip: "" - } - - BakedMussel: { - DisplayName: Baked Mussel - Tooltip: "" - } - - BakedOyster: { - DisplayName: Baked Oyster - Tooltip: "" - } - - CantaloupeJelly: { - DisplayName: Cantaloupe Jelly - Tooltip: "" - } - - CaramelPudding: { - DisplayName: Caramel Pudding - Tooltip: "" - } - - CrabseedWarship: { - DisplayName: Crabseed Warship - Tooltip: "" - } - - Durian: { - DisplayName: Durian - Tooltip: "" - } - - EggTart: { - DisplayName: Egg Tart - Tooltip: "" - } - - KiwiFruit: { - DisplayName: Kiwi Fruit - Tooltip: "" - } - - KiwiIceCream: { - DisplayName: Kiwi Ice Cream - Tooltip: "" - } - - Mangosteen: { - DisplayName: Mangosteen - Tooltip: "" - } - - OrangeIcecream: { - DisplayName: Orange Icecream - Tooltip: "" - } - - SalmonInPepper: { - DisplayName: Salmon In Pepper - Tooltip: "" - } - - SalmonWarship: { - DisplayName: Salmon Warship - Tooltip: "" - } - - SeafoodPizza: { - DisplayName: Seafood Pizza - Tooltip: "" - } - - StinkyTofu: { - DisplayName: Stinky Tofu - Tooltip: "" - } - - Strawberry: { - DisplayName: Strawberry - Tooltip: "" - } - - StrawberryIcecream: { - DisplayName: Strawberry Icecream - Tooltip: "" - } - - SweetshrimpSushi: { - DisplayName: Sweetshrimp Sushi - Tooltip: "" - } - - TamakoSushi: { - DisplayName: Tamako Sushi - Tooltip: "" - } - - WakameWarship: { - DisplayName: Wakame Warship - Tooltip: "" - } - - Watermelon: { - DisplayName: Watermelon - Tooltip: "" - } - - WatermelonPlate: { - DisplayName: Watermelon Plate - Tooltip: "" - } - - BlueHawaii: { - DisplayName: Blue Hawaii - Tooltip: "" - } - - B_52: { - DisplayName: B_52 - Tooltip: "" - } - - DreamYearning: { - DisplayName: Dream Yearning - Tooltip: "" - } - - DryMartini: { - DisplayName: Dry Martini - Tooltip: "" - } - - GreenStorm: { - DisplayName: Green Storm - Tooltip: "" - } - - KiwiJuice: { - DisplayName: Kiwi Juice - Tooltip: "" - } - - LonelyJellyfish: { - DisplayName: Lonely Jellyfish - Tooltip: "" - } - - OrangeJuice: { - DisplayName: Orange Juice - Tooltip: "" - } - - PiercoldWind: { - DisplayName: Piercold Wind - Tooltip: "" - } - - PurpleHooter: { - DisplayName: Purple Hooter - Tooltip: "" - } - - QuinceMarry: { - DisplayName: Quince Marry - Tooltip: "" - } - - RedWine: { - DisplayName: Red Wine - Tooltip: "" - } - - Sunrise: { - DisplayName: Sunrise - Tooltip: "" - } - - Tricolour: { - DisplayName: Tricolour - Tooltip: "" - } - - WaterMelonJuice: { - DisplayName: Water Melon Juice - Tooltip: "" - } - - VFXGenerator: { - DisplayName: V F X Generator - Tooltip: "" - } - - AirTileItem: { - DisplayName: Air Tile Item - Tooltip: "" - } - - AirWallItem: { - DisplayName: Air Wall Item - Tooltip: "" - } - - VortexVanquisherItem: { - DisplayName: Vortex Vanquisher Item - Tooltip: "" - } - - Tester: { - DisplayName: Tester - Tooltip: "" - } - - BloodMossStone: { - DisplayName: Blood Moss Stone - Tooltip: "" - } - - BloodyStoneWall: { - DisplayName: Bloody Stone Wall - Tooltip: "" - } - - StoneTusk: { - DisplayName: Stone Tusk - Tooltip: "" - } - - TuskFlesh: { - DisplayName: Tusk Flesh - Tooltip: "" - } - - TuskStatusI: { - DisplayName: Tusk Status I - Tooltip: "" - } - - TuskStatusII: { - DisplayName: Tusk Status I I - Tooltip: "" - } - - TuskStatusIII: { - DisplayName: Tusk Status I I I - Tooltip: "" - } - - TuskStatusIV: { - DisplayName: Tusk Status I V - Tooltip: "" - } - - BloodyBoneYoyo: { - DisplayName: Bloody Bone Yoyo - Tooltip: "" - } - - SpineGun: { - DisplayName: Spine Gun - Tooltip: "" - } - - ToothBow: { - DisplayName: Tooth Bow - Tooltip: "" - } - - ToothKnife: { - DisplayName: Tooth Knife - Tooltip: "" - } - - ToothMagicBall: { - DisplayName: Tooth Magic Ball - Tooltip: "" - } - - ToothSpear: { - DisplayName: Tooth Spear - Tooltip: "" - } - - ToothStaff: { - DisplayName: Tooth Staff - Tooltip: "" - } - - BloodyTuskTrophy: { - DisplayName: Bloody Tusk Trophy - Tooltip: "" - } - - TuskRelic: { - DisplayName: Tusk Relic - Tooltip: "" - } - - TuskTreasureBag: { - DisplayName: Tusk Treasure Bag - Tooltip: "" - } - - TuskLace: { - DisplayName: Tusk Lace - Tooltip: "" - } - - FireflyPylonItem: { - DisplayName: Firefly Pylon Item - Tooltip: "" - } - - ShabbyPylonItem: { - DisplayName: Shabby Pylon Item - Tooltip: "" - } - - BlackStarShrub: { - DisplayName: Black Star Shrub - Tooltip: "" - } - - DarkCocoon: { - DisplayName: Dark Cocoon - Tooltip: "" - } - - DarkCocoonWall: { - DisplayName: Dark Cocoon Wall - Tooltip: "" - } - - EvilCocoon: { - DisplayName: Evil Cocoon - Tooltip: "" - } - - FireflyCapital: { - DisplayName: Firefly Capital - Tooltip: "" - } - - FireflyMoss: { - DisplayName: Firefly Moss - Tooltip: "" - } - - FireflyPigment: { - DisplayName: Firefly Pigment - Tooltip: "" - } - - GlowCrystal: { - DisplayName: Glow Crystal - Tooltip: "" - } - - GlowingFirefly: { - DisplayName: Glowing Firefly - Tooltip: "" - } - - GlowingPedal: { - DisplayName: Glowing Pedal - Tooltip: "" - } - - GlowMetal: { - DisplayName: Glow Metal - Tooltip: "" - } - - GlowStone: { - DisplayName: Glow Stone - Tooltip: "" - } - - GlowWood: { - DisplayName: Glow Wood - Tooltip: "" - } - - LiveFluorescentLeafWall: { - DisplayName: Live Fluorescent Leaf Wall - Tooltip: "" - } - - LiveFluorescentWoodWall: { - DisplayName: Live Fluorescent Wood Wall - Tooltip: "" - } - - MothScaleDust: { - DisplayName: Moth Scale Dust - Tooltip: "" - } - - DarknessFan: { - DisplayName: Darkness Fan - Tooltip: "" - } - - DreamWeaver: { - DisplayName: Dream Weaver - Tooltip: "" - } - - DustOfCorrupt: { - DisplayName: Dust Of Corrupt - Tooltip: "" - } - - EvilChrysalis: { - DisplayName: Evil Chrysalis - Tooltip: "" - } - - FlowLightMissile: { - DisplayName: Flow Light Missile - Tooltip: "" - } - - GlowBeadGun: { - DisplayName: Glow Bead Gun - Tooltip: "" - } - - GlowWoodSword: { - DisplayName: Glow Wood Sword - Tooltip: "" - } - - MothYoyo: { - DisplayName: Moth Yoyo - Tooltip: "" - } - - NavyThunder: { - DisplayName: Navy Thunder - Tooltip: "" - } - - PhosphorescenceGun: { - DisplayName: Phosphorescence Gun - Tooltip: "" - } - - ScaleWingBlade: { - DisplayName: Scale Wing Blade - Tooltip: "" - } - - ShadowWingBow: { - DisplayName: Shadow Wing Bow - Tooltip: "" - } - - FluorescentLeafStaff: { - DisplayName: Fluorescent Leaf Staff - Tooltip: "" - } - - FluorescentWoodStaff: { - DisplayName: Fluorescent Wood Staff - Tooltip: "" - } - - GlowingDrop: { - DisplayName: Glowing Drop - Tooltip: "" - } - - GlowWoodBed: { - DisplayName: Glow Wood Bed - Tooltip: "" - } - - GlowWoodBookcase: { - DisplayName: Glow Wood Bookcase - Tooltip: "" - } - - GlowWoodBookcaseType2: { - DisplayName: Glow Wood Bookcase Type2 - Tooltip: "" - } - - GlowWoodCampfire: { - DisplayName: Glow Wood Campfire - Tooltip: "" - } - - GlowWoodCandelabra: { - DisplayName: Glow Wood Candelabra - Tooltip: "" - } - - GlowWoodCandle: { - DisplayName: Glow Wood Candle - Tooltip: "" - } - - GlowWoodChair: { - DisplayName: Glow Wood Chair - Tooltip: "" - } - - GlowWoodChandelier: { - DisplayName: Glow Wood Chandelier - Tooltip: "" - } - - GlowWoodChandelierType2: { - DisplayName: Glow Wood Chandelier Type2 - Tooltip: "" - } - - GlowWoodChandelierType3: { - DisplayName: Glow Wood Chandelier Type3 - Tooltip: "" - } - - GlowWoodChandelierType4: { - DisplayName: Glow Wood Chandelier Type4 - Tooltip: "" - } - - GlowWoodChest: { - DisplayName: Glow Wood Chest - Tooltip: "" - } - - GlowWoodClock: { - DisplayName: Glow Wood Clock - Tooltip: "" - } - - GlowWoodDoor: { - DisplayName: Glow Wood Door - Tooltip: "" - } - - GlowWoodDoorType2: { - DisplayName: Glow Wood Door Type2 - Tooltip: "" - } - - GlowWoodDresser: { - DisplayName: Glow Wood Dresser - Tooltip: "" - } - - GlowWoodDresserType2: { - DisplayName: Glow Wood Dresser Type2 - Tooltip: "" - } - - GlowWoodLamp: { - DisplayName: Glow Wood Lamp - Tooltip: "" - } - - GlowWoodLantern: { - DisplayName: Glow Wood Lantern - Tooltip: "" - } - - GlowWoodLanternType2: { - DisplayName: Glow Wood Lantern Type2 - Tooltip: "" - } - - GlowWoodMusicBox: { - DisplayName: Glow Wood Music Box - Tooltip: "" - } - - GlowWoodPiano: { - DisplayName: Glow Wood Piano - Tooltip: "" - } - - GlowWoodPlatform: { - DisplayName: Glow Wood Platform - Tooltip: "" - } - - GlowWoodSink: { - DisplayName: Glow Wood Sink - Tooltip: "" - } - - GlowWoodSinkType2: { - DisplayName: Glow Wood Sink Type2 - Tooltip: "" - } - - GlowWoodSofa: { - DisplayName: Glow Wood Sofa - Tooltip: "" - } - - GlowWoodSofaType2: { - DisplayName: Glow Wood Sofa Type2 - Tooltip: "" - } - - GlowWoodTable: { - DisplayName: Glow Wood Table - Tooltip: "" - } - - GlowWoodTorch: { - DisplayName: Glow Wood Torch - Tooltip: "" - } - - GlowWoodWorkBench: { - DisplayName: Glow Wood Work Bench - Tooltip: "" - } - - CorruptMothTreasureBag: { - DisplayName: Corrupt Moth Treasure Bag - Tooltip: "" - } - - MothRelic: { - DisplayName: Moth Relic - Tooltip: "" - } - - FireflyBorageBadge: { - DisplayName: Firefly Borage Badge - Tooltip: "" - } - - MothEye: { - DisplayName: Moth Eye - Tooltip: "" - } - - LilyHarp: { - DisplayName: Lily Harp - Tooltip: "" - } - - BlueflowerBra: { - DisplayName: Blueflower Bra - Tooltip: "" - } - - BlueflowerCap: { - DisplayName: Blueflower Cap - Tooltip: "" - } - - BlueflowerDress: { - DisplayName: Blueflower Dress - Tooltip: "" - } - - AshBone: { - DisplayName: Ash Bone - Tooltip: "" - } - - BoneFeatherMagic: { - DisplayName: Bone Feather Magic - Tooltip: "" - } - - CloudGun: { - DisplayName: Cloud Gun - Tooltip: "" - } - - ComingGhost: { - DisplayName: Coming Ghost - Tooltip: "" - } - - CyanFrost: { - DisplayName: Cyan Frost - Tooltip: "" - } - - FeatherMagic: { - DisplayName: Feather Magic - Tooltip: "" - } - - FireFeatherMagic: { - DisplayName: Fire Feather Magic - Tooltip: "" - } - - FreezeFeatherMagic: { - DisplayName: Freeze Feather Magic - Tooltip: "" - } - - GoldRoundYoyo: { - DisplayName: Gold Round Yoyo - Tooltip: "" - } - - GoldRound: { - DisplayName: Gold Round - Tooltip: "" - } - - Hepuyuan: { - DisplayName: Hepuyuan - Tooltip: "" - } - - SilveralGun: { - DisplayName: Silveral Gun - Tooltip: "" - } - - SilveralRifle: { - DisplayName: Silveral Rifle - Tooltip: "" - } - - Sunflower: { - DisplayName: Sunflower - Tooltip: "" - } - - ThunderFlower: { - DisplayName: Thunder Flower - Tooltip: "" - } - - World: { - DisplayName: World - Tooltip: "" - } - - XmasWhip: { - DisplayName: Xmas Whip - Tooltip: "" - } - - AmbiguousNight: { - DisplayName: Ambiguous Night - Tooltip: "" - } - - AmethystSlingshot: { - DisplayName: Amethyst Slingshot - Tooltip: "" - } - - BorealWoodSlingshot: { - DisplayName: Boreal Wood Slingshot - Tooltip: "" - } - - CactusSlingShot: { - DisplayName: Cactus Sling Shot - Tooltip: "" - } - - DiamondSlingshot: { - DisplayName: Diamond Slingshot - Tooltip: "" - } - - EbonwoodSlingshot: { - DisplayName: Ebonwood Slingshot - Tooltip: "" - } - - EmeraldSlingshot: { - DisplayName: Emerald Slingshot - Tooltip: "" - } - - GelSlingshot: { - DisplayName: Gel Slingshot - Tooltip: "" - } - - JungleSlingshot: { - DisplayName: Jungle Slingshot - Tooltip: "" - } - - PalmWoodSlingshot: { - DisplayName: Palm Wood Slingshot - Tooltip: "" - } - - PearlwoodSlingshot: { - DisplayName: Pearlwood Slingshot - Tooltip: "" - } - - RichMahoganySlingshot: { - DisplayName: Rich Mahogany Slingshot - Tooltip: "" - } - - RubySlingshot: { - DisplayName: Ruby Slingshot - Tooltip: "" - } - - SapphireSlingshot: { - DisplayName: Sapphire Slingshot - Tooltip: "" - } - - ShadewoodSlingShot: { - DisplayName: Shadewood Sling Shot - Tooltip: "" - } - - StarSlingshot: { - DisplayName: Star Slingshot - Tooltip: "" - } - - TopazSlingshot: { - DisplayName: Topaz Slingshot - Tooltip: "" - } - - WoodSlingshot: { - DisplayName: Wood Slingshot - Tooltip: "" - } - - AdamantiteClub: { - DisplayName: Adamantite Club - Tooltip: "" - } - - BorealWoodClub: { - DisplayName: Boreal Wood Club - Tooltip: "" - } - - CactusClub: { - DisplayName: Cactus Club - Tooltip: "" - } - - ChlorophyteClub: { - DisplayName: Chlorophyte Club - Tooltip: "" - } - - CobaltClub: { - DisplayName: Cobalt Club - Tooltip: "" - } - - CopperClub: { - DisplayName: Copper Club - Tooltip: "" - } - - CorruptClub: { - DisplayName: Corrupt Club - Tooltip: "" - } - - CrimsonClub: { - DisplayName: Crimson Club - Tooltip: "" - } - - CrystalClub: { - DisplayName: Crystal Club - Tooltip: "" - } - - CurseClub: { - DisplayName: Curse Club - Tooltip: "" - } - - EbonwoodClub: { - DisplayName: Ebonwood Club - Tooltip: "" - } - - GoldClub: { - DisplayName: Gold Club - Tooltip: "" - } - - IchorClub: { - DisplayName: Ichor Club - Tooltip: "" - } - - IronClub: { - DisplayName: Iron Club - Tooltip: "" - } - - LeadClub: { - DisplayName: Lead Club - Tooltip: "" - } - - MeteorClub: { - DisplayName: Meteor Club - Tooltip: "" - } - - MithrilClub: { - DisplayName: Mithril Club - Tooltip: "" - } - - OrichalcumClub: { - DisplayName: Orichalcum Club - Tooltip: "" - } - - PalladiumClub: { - DisplayName: Palladium Club - Tooltip: "" - } - - PalmWoodClub: { - DisplayName: Palm Wood Club - Tooltip: "" - } - - PearlwoodClub: { - DisplayName: Pearlwood Club - Tooltip: "" - } - - PlatinumClub: { - DisplayName: Platinum Club - Tooltip: "" - } - - RichMahoganyClub: { - DisplayName: Rich Mahogany Club - Tooltip: "" - } - - ShadewoodClub: { - DisplayName: Shadewood Club - Tooltip: "" - } - - SilverClub: { - DisplayName: Silver Club - Tooltip: "" - } - - SpikeClub: { - DisplayName: Spike Club - Tooltip: "" - } - - StarDancer: { - DisplayName: Star Dancer - Tooltip: "" - } - - TinClub: { - DisplayName: Tin Club - Tooltip: "" - } - - TitaniumClub: { - DisplayName: Titanium Club - Tooltip: "" - } - - TungstenClub: { - DisplayName: Tungsten Club - Tooltip: "" - } - - WoodenClub: { - DisplayName: Wooden Club - Tooltip: "" - } - - CloudBall: { - DisplayName: Cloud Ball - Tooltip: "" - } - - BlueGel: { - DisplayName: Blue Gel - Tooltip: "" - } - - CorruptEye: { - DisplayName: Corrupt Eye - Tooltip: "" - } - - DukeTooth: { - DisplayName: Duke Tooth - Tooltip: "" - } - - GoldLiquidPupil: { - DisplayName: Gold Liquid Pupil - Tooltip: "" - } - - GreenGel: { - DisplayName: Green Gel - Tooltip: "" - } - - JungleVitamin: { - DisplayName: Jungle Vitamin - Tooltip: "" - } - - Odd8Ring: { - DisplayName: Odd8 Ring - Tooltip: "" - } - - RedGel: { - DisplayName: Red Gel - Tooltip: "" - } - - SilverWing: { - DisplayName: Silver Wing - Tooltip: "" - } - - WalnutClip: { - DisplayName: Walnut Clip - Tooltip: "" - } - - FixCoinCrit1: { - DisplayName: Fix Coin Crit1 - Tooltip: "" - } - - FixCoinCrit2: { - DisplayName: Fix Coin Crit2 - Tooltip: "" - } - - FixCoinCrit3: { - DisplayName: Fix Coin Crit3 - Tooltip: "" - } - - FixCoinCrit4: { - DisplayName: Fix Coin Crit4 - Tooltip: "" - } - - FixCoinCrit5: { - DisplayName: Fix Coin Crit5 - Tooltip: "" - } - - FixCoinDamage1: { - DisplayName: Fix Coin Damage1 - Tooltip: "" - } - - FixCoinDamage2: { - DisplayName: Fix Coin Damage2 - Tooltip: "" - } - - FixCoinDamage3: { - DisplayName: Fix Coin Damage3 - Tooltip: "" - } - - FixCoinDamage4: { - DisplayName: Fix Coin Damage4 - Tooltip: "" - } - - FixCoinDamage5: { - DisplayName: Fix Coin Damage5 - Tooltip: "" - } - - FixCoinDefense1: { - DisplayName: Fix Coin Defense1 - Tooltip: "" - } - - FixCoinDefense2: { - DisplayName: Fix Coin Defense2 - Tooltip: "" - } - - FixCoinDefense3: { - DisplayName: Fix Coin Defense3 - Tooltip: "" - } - - FixCoinDefense4: { - DisplayName: Fix Coin Defense4 - Tooltip: "" - } - - FixCoinDefense5: { - DisplayName: Fix Coin Defense5 - Tooltip: "" - } - - FixCoinMelee1: { - DisplayName: Fix Coin Melee1 - Tooltip: "" - } - - FixCoinMelee2: { - DisplayName: Fix Coin Melee2 - Tooltip: "" - } - - FixCoinMelee3: { - DisplayName: Fix Coin Melee3 - Tooltip: "" - } - - FixCoinSpeed1: { - DisplayName: Fix Coin Speed1 - Tooltip: "" - } - - FixCoinSpeed2: { - DisplayName: Fix Coin Speed2 - Tooltip: "" - } - - FixCoinSpeed3: { - DisplayName: Fix Coin Speed3 - Tooltip: "" - } - - CrystalSkull: { - DisplayName: Crystal Skull - Tooltip: "" - } - - BloodLamp: { - DisplayName: Blood Lamp - Tooltip: "" - } - - LanternBombRemoteControl: { - DisplayName: Lantern Bomb Remote Control - Tooltip: "" - } - - CactusBall: { - DisplayName: Cactus Ball - Tooltip: "" - } - - BlueFreeze: { - DisplayName: Blue Freeze - Tooltip: "" - } - - BluePedal: { - DisplayName: Blue Pedal - Tooltip: "" - } - - CyanHyacinth: { - DisplayName: Cyan Hyacinth - Tooltip: "" - } - - DarkPoppy: { - DisplayName: Dark Poppy - Tooltip: "" - } - - FlowerBrochure: { - DisplayName: Flower Brochure - Tooltip: "" - } - - GoldCup: { - DisplayName: Gold Cup - Tooltip: "" - } - - GoldWhip: { - DisplayName: Gold Whip - Tooltip: "" - } - - HotPinkTulip: { - DisplayName: Hot Pink Tulip - Tooltip: "" - } - - Lavender: { - DisplayName: Lavender - Tooltip: "" - } - - LightChrysanthemum: { - DisplayName: Light Chrysanthemum - Tooltip: "" - } - - LightPurpleBalls: { - DisplayName: Light Purple Balls - Tooltip: "" - } - - OrangeSausage: { - DisplayName: Orange Sausage - Tooltip: "" - } - - OrangeTulip: { - DisplayName: Orange Tulip - Tooltip: "" - } - - PinkSun: { - DisplayName: Pink Sun - Tooltip: "" - } - - PurplePhantom: { - DisplayName: Purple Phantom - Tooltip: "" - } - - PurpleTail: { - DisplayName: Purple Tail - Tooltip: "" - } - - RedFlame: { - DisplayName: Red Flame - Tooltip: "" - } - - ShallowNight: { - DisplayName: Shallow Night - Tooltip: "" - } - - SilverClock: { - DisplayName: Silver Clock - Tooltip: "" - } - - WhiteStar: { - DisplayName: White Star - Tooltip: "" - } - - WhiteTulip: { - DisplayName: White Tulip - Tooltip: "" - } - - WindMoveSeed: { - DisplayName: Wind Move Seed - Tooltip: "" - } - - BlueTorchFlower: { - DisplayName: Blue Torch Flower - Tooltip: "" - } - - CyanBranch: { - DisplayName: Cyan Branch - Tooltip: "" - } - - CyanPedal: { - DisplayName: Cyan Pedal - Tooltip: "" - } - - DarkHat: { - DisplayName: Dark Hat - Tooltip: "" - } - - HotPinkCrown: { - DisplayName: Hot Pink Crown - Tooltip: "" - } - - LightPurplePen: { - DisplayName: Light Purple Pen - Tooltip: "" - } - - ManyWhiteFlower: { - DisplayName: Many White Flower - Tooltip: "" - } - - OrangeCrown: { - DisplayName: Orange Crown - Tooltip: "" - } - - OrangeStick: { - DisplayName: Orange Stick - Tooltip: "" - } - - PinkFourPedal: { - DisplayName: Pink Four Pedal - Tooltip: "" - } - - PurpleBall: { - DisplayName: Purple Ball - Tooltip: "" - } - - PurpleReedHalo: { - DisplayName: Purple Reed Halo - Tooltip: "" - } - - RedTriangle: { - DisplayName: Red Triangle - Tooltip: "" - } - - SilverCupFlower: { - DisplayName: Silver Cup Flower - Tooltip: "" - } - - ThreeColorCrown: { - DisplayName: Three Color Crown - Tooltip: "" - } - - WhiteCrown: { - DisplayName: White Crown - Tooltip: "" - } - - WhitePedal: { - DisplayName: White Pedal - Tooltip: "" - } - - WhiteWoodFlower: { - DisplayName: White Wood Flower - Tooltip: "" - } - - YellowBellFlower: { - DisplayName: Yellow Bell Flower - Tooltip: "" - } - - TreePlacer: { - DisplayName: Tree Placer - Tooltip: "" - } - - TwilightWood: { - DisplayName: Twilight Wood - Tooltip: "" - } - - YggdrasilImpression: { - DisplayName: Yggdrasil Impression - Tooltip: "" - } - - ChinesePartitionLamp: { - DisplayName: Chinese Partition Lamp - Tooltip: "" - } - - ChineseStyleFloorLamp: { - DisplayName: Chinese Style Floor Lamp - Tooltip: "" - } - - CyanVineBar: { - DisplayName: Cyan Vine Bar - Tooltip: "" - } - - CyanVineOre: { - DisplayName: Cyan Vine Ore - Tooltip: "" - } - - DoubleArmsChineseStreetLamp: { - DisplayName: Double Arms Chinese Street Lamp - Tooltip: "" - } - - HangingSkyLantern: { - DisplayName: Hanging Sky Lantern - Tooltip: "" - } - - HexagonalCeilingLamp: { - DisplayName: Hexagonal Ceiling Lamp - Tooltip: "" - } - - ShoreMud: { - DisplayName: Shore Mud - Tooltip: "" - } - - SideHangingLantern: { - DisplayName: Side Hanging Lantern - Tooltip: "" - } - - SideHangingLantern_White: { - DisplayName: Side Hanging Lantern_ White - Tooltip: "" - } - - StoneDragonScaleWood: { - DisplayName: Stone Dragon Scale Wood - Tooltip: "" - } - - StoneDragonScaleWoodWall: { - DisplayName: Stone Dragon Scale Wood Wall - Tooltip: "" - } - - StreetLantern: { - DisplayName: Street Lantern - Tooltip: "" - } - - YellowDynastyShingles: { - DisplayName: Yellow Dynasty Shingles - Tooltip: "" - } - - YggdrasilAmber: { - DisplayName: Yggdrasil Amber - Tooltip: "" - } - - CyanVineAxe: { - DisplayName: Cyan Vine Axe - Tooltip: "" - } - - CyanVineHammer: { - DisplayName: Cyan Vine Hammer - Tooltip: "" - } - - CyanVinePickaxe: { - DisplayName: Cyan Vine Pickaxe - Tooltip: "" - } - - CyanVineSword: { - DisplayName: Cyan Vine Sword - Tooltip: "" - } - - CyanVineThrowingSpear: { - DisplayName: Cyan Vine Throwing Spear - Tooltip: "" - } - - DragonScaleWood: { - DisplayName: Dragon Scale Wood - Tooltip: "" - } - - DragonScaleWoodWall: { - DisplayName: Dragon Scale Wood Wall - Tooltip: "" - } - - OldMoss: { - DisplayName: Old Moss - Tooltip: "" - } - - YggdrasilDirt: { - DisplayName: Yggdrasil Dirt - Tooltip: "" - } - - AcroporaSpear: { - DisplayName: Acropora Spear - Tooltip: "" - } - - CyanWindGranite: { - DisplayName: Cyan Wind Granite - Tooltip: "" - } - - BloodLightCrystal: { - DisplayName: Blood Light Crystal - Tooltip: "" - } - - TrueDeathSickle: { - DisplayName: True Death Sickle - Tooltip: "" - } - - LiftLamp: { - DisplayName: Lift Lamp - Tooltip: "" - } - - LiftWinch: { - DisplayName: Lift Winch - Tooltip: "" - } - - HardmodeItem: { - DisplayName: Hardmode Item +Items: { + VFXGenerator: { + DisplayName: V F X Generator Tooltip: "" } - SightOfTile: { - DisplayName: Sight Of Tile + AirTileItem: { + DisplayName: Air Tile Item Tooltip: "" } - TestItem: { - DisplayName: Test Item + AirWallItem: { + DisplayName: Air Wall Item Tooltip: "" } } -NPCs: { - TuskKillEffect.DisplayName: Tusk Kill Effect - TuskWall.DisplayName: Tusk Wall - BloodTusk.DisplayName: Blood Tusk - BloodyMouth1.DisplayName: Bloody Mouth1 - BloodyMouth2.DisplayName: Bloody Mouth2 - CrimsonTusk.DisplayName: Crimson Tusk - CrimsonTusk1.DisplayName: Crimson Tusk1 - CrimsonTuskControlable.DisplayName: Crimson Tusk Controlable - CrimsonTuskFireWork.DisplayName: Crimson Tusk Fire Work - HugeMouth.DisplayName: Huge Mouth - LargeTusk.DisplayName: Large Tusk - LargeTusk2.DisplayName: Large Tusk2 - LittleMouth.DisplayName: Little Mouth - LittleTusk.DisplayName: Little Tusk - TuskCooling.DisplayName: Tusk Cooling - TuskPoolWave.DisplayName: Tusk Pool Wave - TuskRedLight.DisplayName: Tusk Red Light - TuskWallLeft.DisplayName: Tusk Wall Left - TuskWallRight.DisplayName: Tusk Wall Right - TuskWave.DisplayName: Tusk Wave - TuskWaveHuge.DisplayName: Tusk Wave Huge - GlowingHeal.DisplayName: Glowing Heal - BlackStarFruit.DisplayName: Black Star Fruit - CentipedeHead.DisplayName: Centipede Head - CentipedeBody.DisplayName: Centipede Body - CentipedeTail.DisplayName: Centipede Tail - Dendroid_normal.DisplayName: Dendroid_normal - FireflyPiranha.DisplayName: Firefly Piranha - FireflyPiranhaInfected.DisplayName: Firefly Piranha Infected - FireflyPiranhaInfected_small.DisplayName: Firefly Piranha Infected_small - FireflyPiranha_small.DisplayName: Firefly Piranha_small - GlowingFirefly.DisplayName: Glowing Firefly - LargeFireBulb.DisplayName: Large Fire Bulb - LittleFireBulb.DisplayName: Little Fire Bulb - Butterfly.DisplayName: Butterfly - CorruptMoth.DisplayName: Corrupt Moth - EvilPack.DisplayName: Evil Pack - MothSummonEffect.DisplayName: Moth Summon Effect - FloatLantern.DisplayName: Float Lantern - RedPackBomber.DisplayName: Red Pack Bomber - LanternGhostKing.DisplayName: Lantern Ghost King - BlueCore.DisplayName: Blue Core - FlamingDashCore.DisplayName: Flaming Dash Core - GreenCore.DisplayName: Green Core - RedCore.DisplayName: Red Core - YellowCore.DisplayName: Yellow Core - - Acytaea: { - DisplayName: Acytaea - - TownNPCMood: { - Content: I am content. - NoHome: I hate not having a home. - FarFromHome: I am too far from home. - LoveSpace: I love having so much space. - DislikeCrowded: I dislike how crowded I am. - HateCrowded: I hate how crowded I am. - LikeNPC_Princess: I like {NPCName}. - Princess_LovesNPC: I like {NPCName}. - } - } - - AcytaeaShadow.DisplayName: Acytaea Shadow - AcytaeaShadow2.DisplayName: Acytaea Shadow2 - AcytaeaShadow3.DisplayName: Acytaea Shadow3 - FakeAcytaea.DisplayName: Fake Acytaea - CyanOreBeetle.DisplayName: Cyan Ore Beetle - SquamousShell.DisplayName: Squamous Shell - TestNPC.DisplayName: Test N P C -} - -Prefixes: { - Accurate.DisplayName: Accurate - Aimed.DisplayName: Aimed -} - -Projectiles: { - BloodGoldBayonet_Pro.DisplayName: Blood Gold Bayonet_ Pro - BlueRibbonBayonet_Pro.DisplayName: Blue Ribbon Bayonet_ Pro - CopperStabbingSword_Pro.DisplayName: Copper Stabbing Sword_ Pro - DreamStar_Explosion.DisplayName: Dream Star_ Explosion - DreamStar_Pro.DisplayName: Dream Star_ Pro - EnchantedBayonet_Pro.DisplayName: Enchanted Bayonet_ Pro - EternalNight_Pro.DisplayName: Eternal Night_ Pro - GoldenStabbingSword_Pro.DisplayName: Golden Stabbing Sword_ Pro - HolyBayonet_Pro.DisplayName: Holy Bayonet_ Pro - IronStabbingSword_Pro.DisplayName: Iron Stabbing Sword_ Pro - LeadStabbingSword_Pro.DisplayName: Lead Stabbing Sword_ Pro - PlatinumStabbingSword_Pro.DisplayName: Platinum Stabbing Sword_ Pro - PrisonFireBayonet_Pro.DisplayName: Prison Fire Bayonet_ Pro - RedRibbonBayonet_Pro.DisplayName: Red Ribbon Bayonet_ Pro - RottenGoldBayonet_Mark.DisplayName: Rotten Gold Bayonet_ Mark - RottenGoldBayonet_Pro.DisplayName: Rotten Gold Bayonet_ Pro - RottenGoldBayonet_Slash.DisplayName: Rotten Gold Bayonet_ Slash - SilverStabbingSword_Pro.DisplayName: Silver Stabbing Sword_ Pro - TinStabbingSword_Pro.DisplayName: Tin Stabbing Sword_ Pro - TungstenStabbingSword_Pro.DisplayName: Tungsten Stabbing Sword_ Pro - VegetationBayonet_Pro.DisplayName: Vegetation Bayonet_ Pro - YoenLeZed_Pro.DisplayName: Yoen Le Zed_ Pro - FryingPan.DisplayName: Frying Pan - ButterflyDreamFriendly.DisplayName: Butterfly Dream Friendly - ButterflyDream.DisplayName: Butterfly Dream - DashingLightEff.DisplayName: Dashing Light Eff - VortexVanquisher.DisplayName: Vortex Vanquisher - VortexVanquisher2.DisplayName: Vortex Vanquisher2 - VortexVanquisher3.DisplayName: Vortex Vanquisher3 - VortexVanquisherThump.DisplayName: Vortex Vanquisher Thump - GoldShield.DisplayName: Gold Shield - StonePost.DisplayName: Stone Post - CrimsonTuskProj.DisplayName: Crimson Tusk Proj - CrimsonTuskProjGra.DisplayName: Crimson Tusk Proj Gra - EarthTusk.DisplayName: Earth Tusk - EarthTuskHostile.DisplayName: Earth Tusk Hostile - TuskCrack.DisplayName: Tusk Crack - TuskCurse.DisplayName: Tusk Curse - TuskSpice.DisplayName: Tusk Spice - TuskSpiceBlack.DisplayName: Tusk Spice Black - TuskSpicePro.DisplayName: Tusk Spice Pro - Tusk.DisplayName: Tusk - BloodyBoneYoyo.DisplayName: Bloody Bone Yoyo - LittleTusk.DisplayName: Little Tusk - LittleTusk0.DisplayName: Little Tusk0 - SpineGun.DisplayName: Spine Gun - SplieSpineBullet.DisplayName: Splie Spine Bullet - ToothMagic.DisplayName: Tooth Magic - ToothMagicBall.DisplayName: Tooth Magic Ball - ToothMagicHit.DisplayName: Tooth Magic Hit - ToothMagicSplit.DisplayName: Tooth Magic Split - ToothMagicSplit2.DisplayName: Tooth Magic Split2 - ToothSpear.DisplayName: Tooth Spear - Tusk0.DisplayName: Tusk0 - TuskArrow.DisplayName: Tusk Arrow - TuskSpear3.DisplayName: Tusk Spear3 - TuskSpear4.DisplayName: Tusk Spear4 - TuskSummon.DisplayName: Tusk Summon - BeadShakeWave.DisplayName: Bead Shake Wave - BlackCorruptRain.DisplayName: Black Corrupt Rain - BlackCorruptRain3.DisplayName: Black Corrupt Rain3 - BlackCorruptRain3Friendly.DisplayName: Black Corrupt Rain3 Friendly - BlackCorruptRainFriendly.DisplayName: Black Corrupt Rain Friendly - BlueMissil.DisplayName: Blue Missil - BlueMissilFriendly.DisplayName: Blue Missil Friendly - BombShakeWave.DisplayName: Bomb Shake Wave - CorMoth4DProj.DisplayName: Cor Moth4 D Proj - CorruptDust.DisplayName: Corrupt Dust - DarkEffect.DisplayName: Dark Effect - DarkFan.DisplayName: Dark Fan - DarkFanFly.DisplayName: Dark Fan Fly - DreamWeaver.DisplayName: Dream Weaver - EvilChrysalis.DisplayName: Evil Chrysalis - EvilChrysalisRightClick.DisplayName: Evil Chrysalis Right Click - FanHit.DisplayName: Fan Hit - FlowLightMissile.DisplayName: Flow Light Missile - FruitBomb.DisplayName: Fruit Bomb - GlowBeadGun.DisplayName: Glow Bead Gun - GlowBeadGunShootFlame.DisplayName: Glow Bead Gun Shoot Flame - GlowingButterfly.DisplayName: Glowing Butterfly - GlowMoth.DisplayName: Glow Moth - GlowStar.DisplayName: Glow Star - GlowStarExplosion.DisplayName: Glow Star Explosion - GlowWoodSword.DisplayName: Glow Wood Sword - MissileProj.DisplayName: Missile Proj - MissileProjHostile.DisplayName: Missile Proj Hostile - MothArrow.DisplayName: Moth Arrow - MothBall.DisplayName: Moth Ball - MothGrey.DisplayName: Moth Grey - MothMagicArray.DisplayName: Moth Magic Array - MothYoyoProjectile.DisplayName: Moth Yoyo Projectile - MothYoyoSub.DisplayName: Moth Yoyo Sub - NavyThunder.DisplayName: Navy Thunder - NavyThunderBomb.DisplayName: Navy Thunder Bomb - PhantomMoth.DisplayName: Phantom Moth - PhosphorescenceBullet.DisplayName: Phosphorescence Bullet - PhosphorescenceGun.DisplayName: Phosphorescence Gun - ScaleWingBladeProj.DisplayName: Scale Wing Blade Proj - ShadowWingBow.DisplayName: Shadow Wing Bow - StaffOfCorruptDust.DisplayName: Staff Of Corrupt Dust - LilyHarpNote.DisplayName: Lily Harp Note - LilyHarpNoteKill.DisplayName: Lily Harp Note Kill - LilyHarpProj.DisplayName: Lily Harp Proj - LilyHarpWave.DisplayName: Lily Harp Wave - VineProj.DisplayName: Vine Proj - VineProj2.DisplayName: Vine Proj2 - CloudBall.DisplayName: Cloud Ball - MagicHit.DisplayName: Magic Hit - AshBone.DisplayName: Ash Bone - XmasWhip.DisplayName: Xmas Whip - AmbiguousLine.DisplayName: Ambiguous Line - AmbiguousNight.DisplayName: Ambiguous Night - AmbiguousNightAmmo.DisplayName: Ambiguous Night Ammo - AmbiguousNightHit.DisplayName: Ambiguous Night Hit - AmethystBead.DisplayName: Amethyst Bead - AmethystSlingshot.DisplayName: Amethyst Slingshot - BorealWoodSlingshot.DisplayName: Boreal Wood Slingshot - CactusSlingShot.DisplayName: Cactus Sling Shot - DiamondBead.DisplayName: Diamond Bead - DiamondSlingshot.DisplayName: Diamond Slingshot - EbonwoodSlingshot.DisplayName: Ebonwood Slingshot - EmeraldBead.DisplayName: Emerald Bead - EmeraldSlingshot.DisplayName: Emerald Slingshot - GelBall.DisplayName: Gel Ball - GelSlingshot.DisplayName: Gel Slingshot - GlowSporeBead.DisplayName: Glow Spore Bead - JungleSlingshot.DisplayName: Jungle Slingshot - JungleSlingshotHit.DisplayName: Jungle Slingshot Hit - KSSlingshotHit.DisplayName: K S Slingshot Hit - KSSlingshotHitPink.DisplayName: K S Slingshot Hit Pink - NormalAmmo.DisplayName: Normal Ammo - NormalHit.DisplayName: Normal Hit - PalmWoodSlingshot.DisplayName: Palm Wood Slingshot - PearlwoodSlingshot.DisplayName: Pearlwood Slingshot - RichMahoganySlingshot.DisplayName: Rich Mahogany Slingshot - RubyBead.DisplayName: Ruby Bead - RubySlingshot.DisplayName: Ruby Slingshot - SapphireBead.DisplayName: Sapphire Bead - SapphireSlingshot.DisplayName: Sapphire Slingshot - ShadewoodSlingShot.DisplayName: Shadewood Sling Shot - StarAmmo.DisplayName: Star Ammo - StarSlingshot.DisplayName: Star Slingshot - TopazBead.DisplayName: Topaz Bead - TopazSlingshot.DisplayName: Topaz Slingshot - WoodSlingshot.DisplayName: Wood Slingshot - ComingGhost.DisplayName: Coming Ghost - ComingGhost2.DisplayName: Coming Ghost2 - GhostHit.DisplayName: Ghost Hit - GoldRound.DisplayName: Gold Round - GoldRoundYoyo.DisplayName: Gold Round Yoyo - MeleeHit.DisplayName: Melee Hit - MeleeHit2.DisplayName: Melee Hit2 - Sunflower.DisplayName: Sunflower - SunFlowerpetal.DisplayName: Sun Flowerpetal - World.DisplayName: World - WorldHit.DisplayName: World Hit - Hepuyuan.DisplayName: Hepuyuan - HepuyuanDown.DisplayName: Hepuyuan Down - HepuyuanShake.DisplayName: Hepuyuan Shake - HepuyuanSpice.DisplayName: Hepuyuan Spice - XiaoBlackWave.DisplayName: Xiao Black Wave - XiaoHit.DisplayName: Xiao Hit - AdamantiteClub.DisplayName: Adamantite Club - BorealWoodClub.DisplayName: Boreal Wood Club - CactusClub.DisplayName: Cactus Club - ChlorophyteClub.DisplayName: Chlorophyte Club - ChlorophyteClub_fly.DisplayName: Chlorophyte Club_fly - ChlorophyteClub_VFX.DisplayName: Chlorophyte Club_ V F X - CobaltClub.DisplayName: Cobalt Club - CopperClub.DisplayName: Copper Club - CorruptClub.DisplayName: Corrupt Club - CrimsonClub.DisplayName: Crimson Club - CrystalClub.DisplayName: Crystal Club - CrystalClub_fly.DisplayName: Crystal Club_fly - CurseClub.DisplayName: Curse Club - CurseClub_fly.DisplayName: Curse Club_fly - EbonwoodClub.DisplayName: Ebonwood Club - GoldClub.DisplayName: Gold Club - IchorClub.DisplayName: Ichor Club - IchorClub_fly.DisplayName: Ichor Club_fly - IronClub.DisplayName: Iron Club - LeadClub.DisplayName: Lead Club - MeteorClub.DisplayName: Meteor Club - MithrilClub.DisplayName: Mithril Club - OrichalcumClub.DisplayName: Orichalcum Club - PalladiumClub.DisplayName: Palladium Club - PalmWoodClub.DisplayName: Palm Wood Club - PearlwoodClub.DisplayName: Pearlwood Club - PlatinumClub.DisplayName: Platinum Club - RichMahoganyClub.DisplayName: Rich Mahogany Club - ShadewoodClub.DisplayName: Shadewood Club - SilverClub.DisplayName: Silver Club - SpikeClub.DisplayName: Spike Club - StarDancer.DisplayName: Star Dancer - TinClub.DisplayName: Tin Club - TitaniumClub.DisplayName: Titanium Club - TungstenClub.DisplayName: Tungsten Club - WoodenClub.DisplayName: Wooden Club - BoneFeather.DisplayName: Bone Feather - EndlessCurseFlame.DisplayName: Endless Curse Flame - FireFeather.DisplayName: Fire Feather - FreezeFeather.DisplayName: Freeze Feather - ThunderBall.DisplayName: Thunder Ball - ThunderBall2.DisplayName: Thunder Ball2 - ThunderBallToNPC.DisplayName: Thunder Ball To N P C - FreezeFeatherMagicArray.DisplayName: Freeze Feather Magic Array - FreezeFeatherMagicBook.DisplayName: Freeze Feather Magic Book - FreezeShoot.DisplayName: Freeze Shoot - GiantFreezeFeather.DisplayName: Giant Freeze Feather - GiantFreezeFeatherExplosion.DisplayName: Giant Freeze Feather Explosion - FireFeatherMagicArray.DisplayName: Fire Feather Magic Array - FireFeatherMagicBook.DisplayName: Fire Feather Magic Book - FlameShoot.DisplayName: Flame Shoot - GiantFireFeather.DisplayName: Giant Fire Feather - GiantFireFeatherExplosion.DisplayName: Giant Fire Feather Explosion - FlowerPetalPurple.DisplayName: Flower Petal Purple - PurpleBallEffect.DisplayName: Purple Ball Effect - FixCoinCrit1.DisplayName: Fix Coin Crit1 - FixCoinCrit2.DisplayName: Fix Coin Crit2 - FixCoinCrit3.DisplayName: Fix Coin Crit3 - FixCoinCrit4.DisplayName: Fix Coin Crit4 - FixCoinCrit5.DisplayName: Fix Coin Crit5 - FixCoinDamage1.DisplayName: Fix Coin Damage1 - FixCoinDamage2.DisplayName: Fix Coin Damage2 - FixCoinDamage3.DisplayName: Fix Coin Damage3 - FixCoinDamage4.DisplayName: Fix Coin Damage4 - FixCoinDamage5.DisplayName: Fix Coin Damage5 - FixCoinDefense1.DisplayName: Fix Coin Defense1 - FixCoinDefense2.DisplayName: Fix Coin Defense2 - FixCoinDefense3.DisplayName: Fix Coin Defense3 - FixCoinDefense4.DisplayName: Fix Coin Defense4 - FixCoinDefense5.DisplayName: Fix Coin Defense5 - FixCoinMelee1.DisplayName: Fix Coin Melee1 - FixCoinMelee2.DisplayName: Fix Coin Melee2 - FixCoinMelee3.DisplayName: Fix Coin Melee3 - FixCoinSpeed1.DisplayName: Fix Coin Speed1 - FixCoinSpeed2.DisplayName: Fix Coin Speed2 - FixCoinSpeed3.DisplayName: Fix Coin Speed3 - IchorRing.DisplayName: Ichor Ring - NewWaterBolt.DisplayName: New Water Bolt - WaterBoltArray.DisplayName: Water Bolt Array - WaterBoltBook.DisplayName: Water Bolt Book - WaterTeleport.DisplayName: Water Teleport - HurricaneMask.DisplayName: Hurricane Mask - RazorbladeTyphoonArray.DisplayName: Razorblade Typhoon Array - RazorbladeTyphoonBook.DisplayName: Razorblade Typhoon Book - TyphoonII.DisplayName: Typhoon I I - MagnetSphereArray.DisplayName: Magnet Sphere Array - MagnetSphereBook.DisplayName: Magnet Sphere Book - MagnetSphereHit.DisplayName: Magnet Sphere Hit - MagnetSphereII.DisplayName: Magnet Sphere I I - MagnetSphereLighting.DisplayName: Magnet Sphere Lighting - LunarFlareArray.DisplayName: Lunar Flare Array - LunarFlareBook.DisplayName: Lunar Flare Book - LunarFlareHit.DisplayName: Lunar Flare Hit - LunarFlareII.DisplayName: Lunar Flare I I - StarProj.DisplayName: Star Proj - GoldenShowerArray.DisplayName: Golden Shower Array - GoldenShowerBomb.DisplayName: Golden Shower Bomb - GoldenShowerBook.DisplayName: Golden Shower Book - GoldenShowerII.DisplayName: Golden Shower I I - DreamWeaverBook.DisplayName: Dream Weaver Book - DreamWeaverII.DisplayName: Dream Weaver I I - DemoHit.DisplayName: Demo Hit - DemonScytheArray.DisplayName: Demon Scythe Array - DemonScytheBook.DisplayName: Demon Scythe Book - DemonScythePlus.DisplayName: Demon Scythe Plus - DemonScythePlusCrack.DisplayName: Demon Scythe Plus Crack - DemoSpark.DisplayName: Demo Spark - CursedFlameHit.DisplayName: Cursed Flame Hit - CursedFlamesArray.DisplayName: Cursed Flames Array - CursedFlamesBook.DisplayName: Cursed Flames Book - CursedFlamesII.DisplayName: Cursed Flames I I - CrystalStormArray.DisplayName: Crystal Storm Array - CrystalStormBook.DisplayName: Crystal Storm Book - CrystalStormII.DisplayName: Crystal Storm I I - CrystalWind.DisplayName: Crystal Wind - LargeCrystal.DisplayName: Large Crystal - Storm.DisplayName: Storm - BoneSpike.DisplayName: Bone Spike - BookofSkullsArray.DisplayName: Bookof Skulls Array - BookofSkullsBook.DisplayName: Bookof Skulls Book - SkullHand.DisplayName: Skull Hand - SkullII.DisplayName: Skull I I - BlackHole.DisplayName: Black Hole - BloodLampProj.DisplayName: Blood Lamp Proj - LBloodEffect.DisplayName: L Blood Effect - LBloodEffectGra.DisplayName: L Blood Effect Gra - LMeteor.DisplayName: L Meteor - RainbowWave.DisplayName: Rainbow Wave - DarkLantern.DisplayName: Dark Lantern - DarkLantern3.DisplayName: Dark Lantern3 - DarkLanternBomb2.DisplayName: Dark Lantern Bomb2 - DarkLanternBombExplosion.DisplayName: Dark Lantern Bomb Explosion - DarkLanternBombExplosion_II.DisplayName: Dark Lantern Bomb Explosion_ I I - ExplodeLantern.DisplayName: Explode Lantern - floatLantern.DisplayName: float Lantern - floatLantern2.DisplayName: float Lantern2 - floatLantern3.DisplayName: float Lantern3 - floatLantern4.DisplayName: float Lantern4 - floatLantern5.DisplayName: float Lantern5 - GoldLantern.DisplayName: Gold Lantern - GoldLanternLine.DisplayName: Gold Lantern Line - GoldLanternLine2.DisplayName: Gold Lantern Line2 - GoldLanternLine3.DisplayName: Gold Lantern Line3 - GoldLanternLine4.DisplayName: Gold Lantern Line4 - GoldLanternLine5.DisplayName: Gold Lantern Line5 - GoldLanternLine6.DisplayName: Gold Lantern Line6 - GoldLanternLine7.DisplayName: Gold Lantern Line7 - GoldLanternLine8.DisplayName: Gold Lantern Line8 - GoldLanternRay.DisplayName: Gold Lantern Ray - GoldLanternStar.DisplayName: Gold Lantern Star - Redlight.DisplayName: Redlight - StrikePlayer.DisplayName: Strike Player - BlueFlame0.DisplayName: Blue Flame0 - BlueFlame0Little.DisplayName: Blue Flame0 Little - BlueFlame0LittleSoon.DisplayName: Blue Flame0 Little Soon - BlueFlame1Boom.DisplayName: Blue Flame1 Boom - BlueStarTrail.DisplayName: Blue Star Trail - BrownFlame0.DisplayName: Brown Flame0 - BrownFlame0Little.DisplayName: Brown Flame0 Little - GoldDashLine.DisplayName: Gold Dash Line - GreenFlame0.DisplayName: Green Flame0 - GreenFlame0Little.DisplayName: Green Flame0 Little - GreenFlame0Split.DisplayName: Green Flame0 Split - GreenFlame1Split.DisplayName: Green Flame1 Split - ImmuneCircle.DisplayName: Immune Circle - PinkFlame0.DisplayName: Pink Flame0 - PinkFlame0Little.DisplayName: Pink Flame0 Little - PurpleFlame0.DisplayName: Purple Flame0 - PurpleFlame0Little.DisplayName: Purple Flame0 Little - PurpleGreenFlame0.DisplayName: Purple Green Flame0 - PurpleGreenFlame0Little.DisplayName: Purple Green Flame0 Little - RedFlame0.DisplayName: Red Flame0 - RedFlame0Little.DisplayName: Red Flame0 Little - RedFlame0Split.DisplayName: Red Flame0 Split - RedFlame1Split.DisplayName: Red Flame1 Split - RedFlame2Split.DisplayName: Red Flame2 Split - YellowFlame0.DisplayName: Yellow Flame0 - YellowFlame0Little.DisplayName: Yellow Flame0 Little - YellowFlame0LittleShine.DisplayName: Yellow Flame0 Little Shine - YellowFlame0Shine.DisplayName: Yellow Flame0 Shine - YellowToRedFlame0Little.DisplayName: Yellow To Red Flame0 Little - AcytaeaArrow.DisplayName: Acytaea Arrow - AcytaeaArrow2.DisplayName: Acytaea Arrow2 - AcytaeaArrow3.DisplayName: Acytaea Arrow3 - AcytaeaArrowGra.DisplayName: Acytaea Arrow Gra - AcytaeaBow.DisplayName: Acytaea Bow - AcytaeaBow2.DisplayName: Acytaea Bow2 - AcytaeaCenterBow.DisplayName: Acytaea Center Bow - AcytaeaEffect.DisplayName: Acytaea Effect - AcytaeaEffectUp.DisplayName: Acytaea Effect Up - AcytaeaLaser.DisplayName: Acytaea Laser - AcytaeaLaserBall.DisplayName: Acytaea Laser Ball - AcytaeaLaserLight.DisplayName: Acytaea Laser Light - AcytaeaLight.DisplayName: Acytaea Light - AcytaeaMagicBook.DisplayName: Acytaea Magic Book - AcytaeaTornado.DisplayName: Acytaea Tornado - AcytaeaTornado2.DisplayName: Acytaea Tornado2 - AcytaeaTornado3.DisplayName: Acytaea Tornado3 - AcytaeaTornado4.DisplayName: Acytaea Tornado4 - AcytaeaTornado5.DisplayName: Acytaea Tornado5 - AcytaeaTornadoHit.DisplayName: Acytaea Tornado Hit - AcytaeaWarningArrow.DisplayName: Acytaea Warning Arrow - BloodBlade.DisplayName: Blood Blade - BloodBlade2.DisplayName: Blood Blade2 - BloodBladeShadow.DisplayName: Blood Blade Shadow - BrokenAcytaea.DisplayName: Broken Acytaea - BrokenAcytaea2.DisplayName: Broken Acytaea2 - Metero.DisplayName: Metero - Metero2.DisplayName: Metero2 - Metero3.DisplayName: Metero3 - Metero4.DisplayName: Metero4 - MeteroFri.DisplayName: Metero Fri - playerHit.DisplayName: player Hit - CactusBallProj.DisplayName: Cactus Ball Proj - CactusBallSpike.DisplayName: Cactus Ball Spike - CyanVineThrowingSpear.DisplayName: Cyan Vine Throwing Spear - AcroporaSpear.DisplayName: Acropora Spear - AcroporaThumpEff.DisplayName: Acropora Thump Eff - TrueDeathSickle.DisplayName: True Death Sickle - TrueDeathSickleHit.DisplayName: True Death Sickle Hit - SightOfTileProj.DisplayName: Sight Of Tile Proj - SightOfTileProjRead.DisplayName: Sight Of Tile Proj Read -} - Configs: { EverglowConfig: { DisplayName: Everglow Config @@ -2974,80 +68,6 @@ Configs: { TuskFighting.Label: Original OldTuskFighting.Label: Old } - - FogConfigs: { - DisplayName: Fog Configs - - EnableScattering: { - Label: 开启雾效 - Tooltip: 是否开启参与介质散射 - } - - EnableLightUpload: { - Label: 【性能测试】开启光源数据 - Tooltip: 是否启用发光体数据上传(CPU到GPU) - } - - MaxBloomRadius: { - Label: 模糊半径 - Tooltip: 光效的最大溢出半径,最终半径是以2为底的指数,越大越影响性能 - } - - BloomIntensity: { - Label: 光效强度 - Tooltip: 光效的强度,用于放大亮度 - } - - OffscreenTiles: { - Label: 离屏物块渲染数量 - Tooltip: 光效需要计算超出屏幕的物块数量 - } - - LightLuminanceThreashold: { - Label: 光源阈值 - Tooltip: 可以调整原版点亮物块的光源阈值 - } - - GaussianKernel: { - Label: 是否开启高斯卷积核 - Tooltip: 更柔和的效果,但是性能较差 - } - - FogAbsorptionR: { - Label: 雾吸收率(R) - Tooltip: 雾吸收红色通道直接光的比率,越大则越暗淡 - } - - FogAbsorptionG: { - Label: 雾吸收率(G) - Tooltip: 雾吸收绿色通道直接光的比率,越大则越暗淡 - } - - FogAbsorptionB: { - Label: 雾吸收率(B) - Tooltip: 雾吸收蓝色通道直接光的比率,越大则越暗淡 - } - - FogScatterWithDistance: { - Label: 扩散率随距离增大 - Tooltip: 是否让散射效果随着距离增大而增大 - } - - FogBloomRate: { - Label: 雾散射扩散率 - Tooltip: 散射光的扩散比率 - } - - FogBloomAbsorptionFactor: { - Label: 雾散射能见度影响系数 - Tooltip: 散射光受到能见度影响的比率,0为不受能见度影响,1完全受能见度影响 - } - - EnableTemporalInterp: { - Label: 开启Temporal插值 - Tooltip: 是否允许光照结果进行帧间插值来平滑 - } - } } Config: { diff --git a/Sources/Everglow/Module.Build.props b/Sources/Everglow/Module.Build.props index 3ee36aaab..37f541dc7 100644 --- a/Sources/Everglow/Module.Build.props +++ b/Sources/Everglow/Module.Build.props @@ -1,7 +1,6 @@ - - + Example $(DefineConstants);$(Modules) diff --git a/Sources/Modules/Example/Everglow.Example.csproj b/Sources/Modules/Example/Everglow.Example.csproj index 3eb1642e0..ba938f7b3 100644 --- a/Sources/Modules/Example/Everglow.Example.csproj +++ b/Sources/Modules/Example/Everglow.Example.csproj @@ -1,6 +1,3 @@ - - - \ No newline at end of file diff --git a/Sources/Modules/Example/PhysicsDemo.cs b/Sources/Modules/Example/PhysicsDemo.cs index 3dcb32ff0..51b78d573 100644 --- a/Sources/Modules/Example/PhysicsDemo.cs +++ b/Sources/Modules/Example/PhysicsDemo.cs @@ -219,7 +219,7 @@ public void ReStart() rigidb.AngularDrag = 0; rigidb.Drag = 0; rigidb.Stiffness = -1; - rigidb.Friction = -1; + rigidb.Friction = 0; _realSimulation.AddPhysicsObject(_dummyPlayer); _realSimulation.Initialize(); } From b0de72fc419a40f4c65542adfa3b5227470ea867 Mon Sep 17 00:00:00 2001 From: CXUtk <2237367067@qq.com> Date: Wed, 19 Jul 2023 22:15:41 -0700 Subject: [PATCH 06/13] update physics engine --- .../Everglow.Core/DataStructures/UnionFind.cs | 53 +++ .../Physics/PBEngine/Collision/AABB.cs | 53 ++- .../Collision/BroadPhase/BVHDetect.cs | 175 +++++++ .../Collision/BroadPhase/BruteForceDetect.cs | 91 +++- .../Collision/BroadPhase/CollisionGraph.cs | 4 +- .../Collision/BroadPhase/HashGridMethod.cs | 158 +++++++ .../Collision/BroadPhase/IBroadPhase.cs | 20 +- .../Collision/BroadPhase/Structure/BVH.cs | 400 ++++++++++++++++ .../Structure/MultiLevelHashGrid.cs | 432 ++++++++++++++++++ .../Collision/BroadPhase/SweepAndPrune.cs | 222 +++++---- .../Collision/Colliders/BoxCollider.cs | 79 ++-- .../Collision/Colliders/CapsuleCollider.cs | 267 +++++++++++ .../Collision/Colliders/SphereCollider.cs | 49 +- .../PBEngine/Collision/CollisionEvent2D.cs | 2 + .../Physics/PBEngine/Constrains/Constrain.cs | 3 +- .../PBEngine/Constrains/JointConstrain.cs | 13 +- .../PBEngine/Constrains/SpringConstrain.cs | 162 ++++--- .../GameInteraction/TileCollisionUtils.cs | 136 +++++- .../Physics/PBEngine/GeometryUtils.cs | 429 +++++++++++++++-- .../Physics/PBEngine/PhysicsObject.cs | 47 +- .../Physics/PBEngine/PhysicsSimulation.cs | 388 +++++++++++++--- .../PlayGround/Contact/ContactPlayGround1.cs | 51 +++ .../Physics/PBEngine/RigidBody2D.cs | 400 ++++++++++------ Sources/Modules/Example/PhysicsDemo.cs | 371 +++++++++++++-- Sources/Modules/Example/RigidBodyDisplay.cs | 10 +- 25 files changed, 3511 insertions(+), 504 deletions(-) create mode 100644 Sources/Everglow.Core/DataStructures/UnionFind.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs create mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs diff --git a/Sources/Everglow.Core/DataStructures/UnionFind.cs b/Sources/Everglow.Core/DataStructures/UnionFind.cs new file mode 100644 index 000000000..2bf110579 --- /dev/null +++ b/Sources/Everglow.Core/DataStructures/UnionFind.cs @@ -0,0 +1,53 @@ +namespace Everglow.Commons.DataStructures; + +public class UnionFind +{ + private int[] parent; + private int[] rank; + + public UnionFind(int size) + { + parent = new int[size]; + rank = new int[size]; + + for (int i = 0; i < size; i++) + { + parent[i] = i; + rank[i] = 0; + } + } + + public int Find(int x) + { + if (parent[x] != x) + { + parent[x] = Find(parent[x]); + } + return parent[x]; + } + + public void Union(int x, int y) + { + int rootX = Find(x); + int rootY = Find(y); + + if (rootX == rootY) + { + return; + } + + if (rank[rootX] < rank[rootY]) + { + parent[rootX] = rootY; + } + else if (rank[rootX] > rank[rootY]) + { + parent[rootY] = rootX; + } + else + { + parent[rootY] = rootX; + rank[rootX]++; + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs index 6ced81e82..e3813cd2c 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs @@ -6,15 +6,66 @@ namespace Everglow.Commons.Physics.PBEngine.Collision { - public struct AABB + public struct AABB { public Vector2 MinPoint; public Vector2 MaxPoint; + public AABB() + { + MinPoint = new Vector2(float.PositiveInfinity); + MaxPoint = new Vector2(float.NegativeInfinity); + } + + public AABB(Vector2 minP, Vector2 maxP) + { + MinPoint = minP; + MaxPoint = maxP; + } + + public Vector2 Center + { + get => (MaxPoint + MinPoint) / 2; + } + public bool Intersects(in AABB other) { return Math.Max(MinPoint.X, other.MinPoint.X) <= Math.Min(MaxPoint.X, other.MaxPoint.X) && Math.Max(MinPoint.Y, other.MinPoint.Y) <= Math.Min(MaxPoint.Y, other.MaxPoint.Y); } + + public AABB Move(Vector2 v) + { + return new AABB() { MinPoint = MinPoint + v, MaxPoint = MaxPoint + v }; + } + + public AABB Merge(in AABB other) + { + return new AABB() { MinPoint = Vector2.Min(MinPoint, other.MinPoint), MaxPoint = Vector2.Max(MaxPoint, other.MaxPoint) }; + } + + public void MergeWith(in AABB other) + { + MinPoint = Vector2.Min(MinPoint, other.MinPoint); + MaxPoint = Vector2.Max(MaxPoint, other.MaxPoint); + } + + public double GetArea() + { + double w = MaxPoint.X - MinPoint.X; + double h = MaxPoint.Y - MinPoint.Y; + return w * h; + } + + public bool CompletelyInside(in AABB other) + { + return other.MinPoint.X <= MinPoint.X && other.MaxPoint.X >= MaxPoint.X + && other.MinPoint.Y <= MinPoint.Y && other.MaxPoint.Y >= MaxPoint.Y; + } + + public AABB EnLarge(float size) + { + return new AABB() { MinPoint = MinPoint - new Vector2(size), MaxPoint = MaxPoint + new Vector2(size) }; + } } } diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs new file mode 100644 index 000000000..1526a23a5 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs @@ -0,0 +1,175 @@ +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria.GameContent; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +{ + /// + /// 使用BVH辅助加速碰撞检测 + /// + public class BVHDetect : IBroadPhase + { + private List _colliders; + private Dictionary> _groups; + private Dictionary _bvhForGroup; + public BVHDetect(CollisionGraph graph) : base(graph) + { + _colliders = new List(); + _groups = new Dictionary>(); + _bvhForGroup = new Dictionary(); + } + + private void GroupInnerDetection(string name, + List> collisionResults) + { + var pairs = _bvhForGroup[name].QueryPairs(); + foreach (var pair in pairs) + { + collisionResults.Add(new KeyValuePair(_colliders[pair.Key], + _colliders[pair.Value])); + } + } + + private void GroupOuterDetection(string groupA, string groupB, + List> collisionResults) + { + var groupAEntries = _groups[groupA]; + var groupBBVH = _bvhForGroup[groupB]; + for (int i = 0; i < groupAEntries.Count; i++) + { + foreach (var id in groupBBVH.QueryRange(groupAEntries[i].BoundingBox)) + { + if (groupAEntries[i].IsDynamic || _colliders[id].ParentObject.RigidBody.MovementType == MovementType.Player || + _colliders[id].ParentObject.RigidBody.MovementType == MovementType.Dynamic) + { + collisionResults.Add(new KeyValuePair(_colliders[groupAEntries[i].ColliderId], + _colliders[id])); + } + } + } + } + + public override List> GetCollisionPairs(float deltaTime) + { + List> finalPairs = new List>(); + foreach (var group in _groups) + { + var thisgroup = _groups[group.Key]; + if (_collisionGraph.Graph.ContainsKey(group.Key)) + { + foreach (var dual in _collisionGraph.Graph[group.Key]) + { + if (dual == group.Key) + { + GroupInnerDetection(group.Key, finalPairs); + continue; + } + if (!_groups.ContainsKey(dual)) + continue; + GroupOuterDetection(group.Key, dual, finalPairs); + } + } + } + return finalPairs; + } + + public override void Prepare(List objects, float deltaTime) + { + _colliders.Clear(); + _colliders.EnsureCapacity(objects.Count); + _groups.Clear(); + _bvhForGroup.Clear(); + for (int i = 0; i < objects.Count; i++) + { + var obj = objects[i]; + _colliders.Add(obj.Collider); + var entry = new ColliderEntry() + { + ColliderId = i, + BoundingBox = obj.Collider.GetAABB(deltaTime), + IsDynamic = obj.RigidBody.MovementType == MovementType.Dynamic || obj.RigidBody.MovementType == MovementType.Player + }; + + if (_groups.ContainsKey(obj.Tag)) + { + _groups[obj.Tag].Add(entry); + } + else + { + _groups.Add(obj.Tag, new List() + { + entry + }); + } + } + foreach (var group in _groups) + { + _bvhForGroup.Add(group.Key, new BVH(group.Value)); + } + } + + public override List GetSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags) + { + List colliders = new List(); + foreach (var tag in targetTags) + { + if (!_groups.ContainsKey(tag)) + { + continue; + } + var bvhThisGroup = _bvhForGroup[tag]; + + foreach (var id in bvhThisGroup.QueryRange(aabb)) + { + colliders.Add(_colliders[id]); + } + } + return colliders; + } + + public override void DrawDebugInfo(SpriteBatch sb) + { + if (!_groups.ContainsKey("Default")) + return; + //var data = _bvhForGroup["Default"].GetProfilingData(); + //sb.Begin(); + //foreach (var entry in data) + //{ + // int x = (int)(entry.GridBox.MinPoint.X - Main.screenPosition.X); + // int y = (int)(-entry.GridBox.MaxPoint.Y - Main.screenPosition.Y); + // int w = (int)(entry.GridBox.MaxPoint.X - entry.GridBox.MinPoint.X); + // int h = (int)(entry.GridBox.MaxPoint.Y - entry.GridBox.MinPoint.Y); + // sb.Draw(TextureAssets.MagicPixel.Value, new Rectangle(x, y, w, h), Color.White * MathHelper.Lerp(0.1f, 0.1f, entry.Layer / 32f)); + //} + + //sb.End(); + } + + public override bool TestSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags) + { + foreach (var tag in targetTags) + { + if (!_groups.ContainsKey(tag)) + { + continue; + } + var bvhThisGroup = _bvhForGroup[tag]; + + if (bvhThisGroup.QueryRange(aabb).Count > 0) + { + return true; + } + } + return false; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs index c18773689..bba12c11f 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs @@ -7,33 +7,27 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase { - /// - /// 暴力遍历AABB的粗粒度碰撞检测算法 - /// - public class BruteForceDetect : BroadPhase + /// + /// 暴力遍历AABB的粗粒度碰撞检测算法 + /// + public class BruteForceDetect : IBroadPhase { - private struct BruteForceEntry - { - public int ColliderId; - public AABB BoundingBox; - public bool IsDynamic; - } private List _colliders; - private Dictionary> _groups; + private Dictionary> _groups; public BruteForceDetect(CollisionGraph graph) : base(graph) { - _colliders = new List(); - _groups = new Dictionary>(); + _colliders = new List(); + _groups = new Dictionary>(); } - private void GroupInnerDetection(List entries, + private void GroupInnerDetection(List entries, List> collisionResults) { for (int i = 0; i < entries.Count; i++) { for (int j = i + 1; j < entries.Count; j++) { - if ((entries[i].IsDynamic || entries[j].IsDynamic) + if ((entries[i].IsDynamic || entries[j].IsDynamic) && entries[i].BoundingBox.Intersects(entries[j].BoundingBox)) { collisionResults.Add(new KeyValuePair(_colliders[entries[i].ColliderId], @@ -43,15 +37,15 @@ private void GroupInnerDetection(List entries, } } - private void GroupOuterDetection(List groupA, - List groupB, + private void GroupOuterDetection(List groupA, + List groupB, List> collisionResults) { for (int i = 0; i < groupA.Count; i++) { for (int j = 0; j < groupB.Count; j++) { - if ((groupA[i].IsDynamic || groupB[j].IsDynamic) + if ((groupA[i].IsDynamic || groupB[j].IsDynamic) && groupA[i].BoundingBox.Intersects(groupB[j].BoundingBox)) { collisionResults.Add(new KeyValuePair(_colliders[groupA[i].ColliderId], @@ -64,14 +58,19 @@ private void GroupOuterDetection(List groupA, public override List> GetCollisionPairs(float deltaTime) { List> finalPairs = new List>(); - foreach(var group in _groups) + foreach (var group in _groups) { var thisgroup = _groups[group.Key]; - GroupInnerDetection(thisgroup, finalPairs); + if (_collisionGraph.Graph.ContainsKey(group.Key)) { foreach (var dual in _collisionGraph.Graph[group.Key]) { + if (dual == group.Key) + { + GroupInnerDetection(thisgroup, finalPairs); + continue; + } if (!_groups.ContainsKey(dual)) continue; GroupOuterDetection(thisgroup, _groups[dual], finalPairs); @@ -86,11 +85,11 @@ public override void Prepare(List objects, float deltaTime) _colliders.Clear(); _colliders.EnsureCapacity(objects.Count); _groups.Clear(); - for(int i = 0; i < objects.Count; i++) + for (int i = 0; i < objects.Count; i++) { var obj = objects[i]; _colliders.Add(obj.Collider); - var entry = new BruteForceEntry() + var entry = new ColliderEntry() { ColliderId = i, BoundingBox = obj.Collider.GetAABB(deltaTime), @@ -103,13 +102,59 @@ public override void Prepare(List objects, float deltaTime) } else { - _groups.Add(obj.Tag, new List() + _groups.Add(obj.Tag, new List() { entry }); } } } + public override List GetSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags) + { + List colliders = new List(); + foreach (var tag in targetTags) + { + if (!_groups.ContainsKey(tag)) + { + continue; + } + var thisgroup = _groups[tag]; + + foreach (var entry in thisgroup) + { + if (entry.BoundingBox.Intersects(aabb)) + { + colliders.Add(_colliders[entry.ColliderId]); + } + } + } + return colliders; + } + public override void DrawDebugInfo(SpriteBatch sb) + { + + } + + public override bool TestSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags) + { + foreach (var tag in targetTags) + { + if (!_groups.ContainsKey(tag)) + { + continue; + } + var thisgroup = _groups[tag]; + + foreach (var entry in thisgroup) + { + if (entry.BoundingBox.Intersects(aabb)) + { + return true; + } + } + } + return false; + } } } diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs index 5a3183227..757313d91 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs @@ -13,7 +13,7 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase public class CollisionGraph { public static CollisionGraph DefualtGraph - = new CollisionGraph(new Dictionary>() { { "Default", new List() } }); + = new CollisionGraph(new Dictionary>() { { "Default", new List() { "Default"} } }); public Dictionary> Graph { get; @@ -47,8 +47,6 @@ public void AddDoubleEdge(string groupA, string groupB) public void AddSingleEdge(string groupA, string groupB) { - if (groupA == groupB) - return; if (Graph.ContainsKey(groupA)) { Graph[groupA].Add(groupB); diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs new file mode 100644 index 000000000..d7f303e00 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs @@ -0,0 +1,158 @@ +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using Terraria.GameContent; + +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +{ + internal class HashGridMethod : IBroadPhase + { + private List _colliders; + private Dictionary _groups; + private int _curHashGridSize; + private int _prevHashGridSize; + private long _prevPairTime; + private long _prev2PairTime; + + public HashGridMethod(CollisionGraph graph) : base(graph) + { + _colliders = new List(); + _groups = new Dictionary(); + _prevHashGridSize = 256; + _curHashGridSize = 256; + + _prev2PairTime = long.MaxValue; + _prevPairTime = long.MinValue; + } + + public override void DrawDebugInfo(SpriteBatch sb) + { + if (!_groups.ContainsKey("Default")) + return; + //var data = _groups["Default"].GetProfilingData(); + //sb.Begin(); + //foreach (var entry in data) + //{ + // int x = (int)(entry.GridBox.MinPoint.X - Main.screenPosition.X); + // int y = (int)(-entry.GridBox.MaxPoint.Y - Main.screenPosition.Y); + // int w = (int)(entry.GridBox.MaxPoint.X - entry.GridBox.MinPoint.X); + // int h = (int)(entry.GridBox.MaxPoint.Y - entry.GridBox.MinPoint.Y); + // sb.Draw(TextureAssets.MagicPixel.Value, new Rectangle(x, y, w, h), Color.White * MathHelper.Lerp(0.1f, 0.5f, entry.NumObjects / 64f)); + //} + + //sb.End(); + } + + public override List> GetCollisionPairs(float deltaTime) + { + Stopwatch sw = Stopwatch.StartNew(); + List> finalPairs = new List>(); + foreach (var group in _groups) + { + var thisgroup = _groups[group.Key]; + + if (_collisionGraph.Graph.ContainsKey(group.Key)) + { + foreach (var dual in _collisionGraph.Graph[group.Key]) + { + if (dual == group.Key) + { + foreach (var pair in thisgroup.QueryPairs()) + { + finalPairs.Add(new KeyValuePair(_colliders[pair.Key], + _colliders[pair.Value])); + } + continue; + } + if (!_groups.ContainsKey(dual)) + continue; + foreach (var pair in thisgroup.QueryPairsWith(_groups[dual])) + { + finalPairs.Add(new KeyValuePair(_colliders[pair.Key], + _colliders[pair.Value])); + } + } + } + } + sw.Stop(); + _prev2PairTime = _prevPairTime; + _prevPairTime = sw.ElapsedTicks; + return finalPairs; + } + + public override List GetSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags) + { + List colliders = new List(); + foreach (var tag in targetTags) + { + if (!_groups.ContainsKey(tag)) + { + continue; + } + + foreach (var id in _groups[tag].QueryRange(aabb)) + { + colliders.Add(_colliders[id]); + } + } + return colliders; + } + + + public override void Prepare(List objects, float deltaTime) + { + _colliders.Clear(); + _colliders.EnsureCapacity(objects.Count); + _groups.Clear(); + + for (int i = 0; i < objects.Count; i++) + { + var obj = objects[i]; + _colliders.Add(obj.Collider); + var entry = new ColliderEntry() + { + ColliderId = i, + BoundingBox = obj.Collider.GetAABB(deltaTime), + IsDynamic = obj.RigidBody.MovementType == MovementType.Dynamic || obj.RigidBody.MovementType == MovementType.Player + }; + + if (!_groups.ContainsKey(obj.Tag)) + { + _groups.Add(obj.Tag, new MultiLevelHashGrid(4, new int[] + { + 1024, + 256, + 64, + 16 + })); + + } + _groups[obj.Tag].Add(entry); + } + + // _prevHashGridSize = _curHashGridSize; + } + + public override bool TestSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags) + { + foreach (var tag in targetTags) + { + if (!_groups.ContainsKey(tag)) + { + continue; + } + + if (_groups[tag].QueryRange(aabb).Count > 0) + { + return true; + } + } + return false; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs index 0ec4116e7..890ec45ba 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs @@ -10,15 +10,29 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase /// /// 粗粒度碰撞检测的基类 /// - public abstract class BroadPhase + public abstract class IBroadPhase { protected CollisionGraph _collisionGraph; - public BroadPhase(CollisionGraph graph) + public IBroadPhase(CollisionGraph graph) { _collisionGraph = graph; } - public abstract void Prepare(List dynamicObjects, float deltaTime); + public abstract void Prepare(List objects, float deltaTime); public abstract List> GetCollisionPairs(float deltaTime); + + public abstract List GetSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags); + + public abstract bool TestSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags); + + + public abstract void DrawDebugInfo(SpriteBatch sb); + } + + public struct ColliderEntry + { + public int ColliderId; + public AABB BoundingBox; + public bool IsDynamic; } } diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs new file mode 100644 index 000000000..ac9e70779 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs @@ -0,0 +1,400 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using MonoMod.Cil; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure +{ + public struct ProfilingDataBVH + { + public AABB GridBox; + public int Layer; + } + public class BVHNode + { + public AABB Box + { + get; set; + } + public BVHNode Left + { + get; set; + } + public BVHNode Right + { + get; set; + } + public int SplitIndex + { + get; set; + } + public List Colliders + { + get; set; + } + public bool Checked + { + get; set; + } + + public BVHNode() + { + Colliders = new List(); + Left = null; + Right = null; + SplitIndex = -1; + Checked = false; + } + } + + public class BVH + { + public BVHNode Root + { + get; private set; + } + + private List _colliders; + private List> _pairQueryCache; + + private const int MAX_COLLIDERS_PER_NODE = 1; + + public BVH(List colliders) + { + _colliders = colliders; + _pairQueryCache = new List>(); + Root = _build(0, _colliders.Count - 1); + + //foreach (ColliderEntry collider in _colliders) + //{ + // Insert(collider); + //} + } + + + public void Insert(in ColliderEntry entry) + { + if (Root == null) + { + Root = new BVHNode() + { + Box = entry.BoundingBox, + Colliders = new List { entry }, + }; + return; + } + _insert(Root, entry); + } + + private void _insert(BVHNode node, in ColliderEntry entry) + { + if (node.Colliders.Count == 0) + { + var newLeftBox = node.Left.Box.Merge(entry.BoundingBox); + var newRightBox = node.Right.Box.Merge(entry.BoundingBox); + if (newLeftBox.GetArea() - node.Left.Box.GetArea() + < newRightBox.GetArea() - node.Right.Box.GetArea()) + { + _insert(node.Left, entry); + } + else + { + _insert(node.Right, entry); + } + node.Box = node.Left.Box.Merge(node.Right.Box); + } + else + { + node.Left = new BVHNode() + { + Box = node.Colliders[0].BoundingBox, + Colliders = new List() { node.Colliders[0] } + }; + + node.Right = new BVHNode() + { + Box = entry.BoundingBox, + Colliders = new List() { entry } + }; + + node.Colliders.Clear(); + node.Box = node.Left.Box.Merge(node.Right.Box); + } + } + + private BVHNode _build(int l, int r) + { + BVHNode node = new BVHNode(); + if (r - l + 1 <= MAX_COLLIDERS_PER_NODE) + { + node.Colliders = _colliders.GetRange(l, r - l + 1); + AABB aabb = new AABB(); + for (int i = l; i <= r; i++) + { + aabb.MergeWith(_colliders[i].BoundingBox); + } + node.Box = aabb; + return node; + } + + AABB fullBox = new AABB(); + for (int i = l; i <= r; i++) + { + fullBox.MergeWith(_colliders[i].BoundingBox); + } + + var sortByX = Comparer.Create((a, b) => + { + return a.BoundingBox.Center.X.CompareTo(b.BoundingBox.Center.X); + }); + var sortByY = Comparer.Create((a, b) => + { + return a.BoundingBox.Center.Y.CompareTo(b.BoundingBox.Center.Y); + }); + Comparer compFunc = (fullBox.MaxPoint.Y - fullBox.MinPoint.Y) > (fullBox.MaxPoint.X - fullBox.MinPoint.X) + ? sortByY : sortByX; + _colliders.Sort(l, r - l + 1, compFunc); + + int mid = l + (r - l) / 2; + + node.Left = _build(l, mid); + node.Right = _build(mid + 1, r); + node.Box = node.Left.Box.Merge(node.Right.Box); + node.SplitIndex = mid; + return node; + } + + private void _clearFlags(BVHNode node) + { + if (node == null) + return; + node.Checked = false; + _clearFlags(node.Left); + _clearFlags(node.Right); + } + + private void _innerPairQuery(BVHNode node) + { + if (node == null) + return; + if (node.Colliders.Count != 0) + { + int sz = node.Colliders.Count; + for (int i = 0; i < sz; i++) + { + for (int j = i + 1; j < sz; j++) + { + if ((node.Colliders[i].IsDynamic || node.Colliders[j].IsDynamic) + && node.Colliders[i].BoundingBox.Intersects(node.Colliders[j].BoundingBox)) + { + _pairQueryCache.Add(new KeyValuePair(node.Colliders[i].ColliderId, + node.Colliders[j].ColliderId)); + } + } + } + return; + } + _innerPairQuery(node.Left); + _innerPairQuery(node.Right); + } + private void _outerPairQuery(BVHNode node1, BVHNode node2) + { + int sz1 = node1.Colliders.Count; + int sz2 = node2.Colliders.Count; + for (int i = 0; i < sz1; i++) + { + for (int j = 0; j < sz2; j++) + { + if ((node1.Colliders[i].IsDynamic || node2.Colliders[j].IsDynamic) + && node1.Colliders[i].BoundingBox.Intersects(node2.Colliders[j].BoundingBox)) + { + _pairQueryCache.Add(new KeyValuePair(node1.Colliders[i].ColliderId, + node2.Colliders[j].ColliderId)); + } + } + } + } + + private void _queryPairHelper(BVHNode node1, BVHNode node2) + { + + if (node1.Colliders.Count > 0 && node2.Colliders.Count > 0) + { + _outerPairQuery(node1, node2); + return; + } + if (node1.Colliders.Count > 0 && node2.Colliders.Count == 0) + { + if (node1.Box.Intersects(node2.Left.Box)) + { + _queryPairHelper(node1, node2.Left); + } + if (node1.Box.Intersects(node2.Right.Box)) + { + _queryPairHelper(node1, node2.Right); + } + if (!node2.Checked) + { + node2.Checked = true; + _queryPairHelper(node2.Left, node2.Right); + } + return; + } + if (node2.Colliders.Count > 0 && node1.Colliders.Count == 0) + { + if (node2.Box.Intersects(node1.Left.Box)) + { + _queryPairHelper(node2, node1.Left); + } + if (node2.Box.Intersects(node1.Right.Box)) + { + _queryPairHelper(node2, node1.Right); + } + if (!node1.Checked) + { + node1.Checked = true; + _queryPairHelper(node1.Left, node1.Right); + } + return; + } + + if (node1.Box.Intersects(node2.Box)) + { + _queryPairHelper(node1.Left, node2.Left); + _queryPairHelper(node1.Right, node2.Left); + _queryPairHelper(node1.Left, node2.Right); + _queryPairHelper(node1.Right, node2.Right); + } + if (!node1.Checked) + { + node1.Checked = true; + _queryPairHelper(node1.Left, node1.Right); + } + if (!node2.Checked) + { + node2.Checked = true; + _queryPairHelper(node2.Left, node2.Right); + } + + } + + private void _queryPair(BVHNode node, int l, int r) + { + // If is a leaf node + if (node.Colliders.Count > 0) + { + for (int i = 0; i < node.Colliders.Count; i++) + { + for (int j = i + 1; j < node.Colliders.Count; j++) + { + if ((node.Colliders[i].IsDynamic || node.Colliders[j].IsDynamic) + && node.Colliders[i].BoundingBox.Intersects(node.Colliders[j].BoundingBox)) + { + _pairQueryCache.Add(new KeyValuePair(node.Colliders[i].ColliderId, + node.Colliders[j].ColliderId)); + } + } + } + return; + } + _queryPairHelper(node.Left, node.Right); + //_queryPair(node.Left, l, node.SplitIndex); + //_queryPair(node.Right, node.SplitIndex + 1, r); + + //if (node.Left.Box.Intersects(node.Right.Box)) + //{ + // for (int i = l; i <= node.SplitIndex; i++) + // { + // for (int j = node.SplitIndex + 1; j <= r; j++) + // { + // if ((_colliders[i].IsDynamic || _colliders[j].IsDynamic) && + // _colliders[i].BoundingBox.Intersects(_colliders[j].BoundingBox)) + // { + // _pairQueryCache.Add(new KeyValuePair(_colliders[i].ColliderId, + // _colliders[j].ColliderId)); + // } + // } + // } + //} + } + + public List> QueryPairs() + { + _pairQueryCache.Clear(); + if (_colliders.Count == 0) + { + return _pairQueryCache; + } + _clearFlags(Root); + _innerPairQuery(Root); + _queryPair(Root, 0, _colliders.Count - 1); + return _pairQueryCache; + } + + public List QueryRange(in AABB targetRange) + { + List result = new List(); + if (!targetRange.Intersects(Root.Box)) + return result; + + Stack stack = new Stack(); + stack.Push(Root); + + while (stack.Count > 0) + { + var cur = stack.Pop(); + if (!cur.Box.Intersects(targetRange)) + { + continue; + } + if (cur.Colliders.Count != 0) + { + foreach (var collider in cur.Colliders) + { + if (collider.BoundingBox.Intersects(targetRange)) + { + result.Add(collider.ColliderId); + } + } + } + else + { + if (cur.Left.Box.Intersects(targetRange)) + { + stack.Push(cur.Left); + } + if (cur.Right.Box.Intersects(targetRange)) + { + stack.Push(cur.Right); + } + } + } + return result; + } + + private void _getProfilingData(BVHNode node, int layer, List result) + { + if(node == null) return; + result.Add(new ProfilingDataBVH() + { + GridBox = node.Box, + Layer = layer, + }); + + _getProfilingData(node.Left, layer + 1, result); + _getProfilingData(node.Right, layer + 1, result); + } + public List GetProfilingData() + { + List result = new List(); + _getProfilingData(Root, 0, result); + return result; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs new file mode 100644 index 000000000..3c3c3ef67 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs @@ -0,0 +1,432 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using log4net.Core; +using Microsoft.Xna.Framework; +using Steamworks; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO.Pipes; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure +{ + public struct ProfilingData + { + public AABB GridBox; + public int NumObjects; + } + public class MultiLevelHashGrid + { + private class HGrid + { + public List Entries; + + + public HGrid() + { + Entries = new List(); + } + + public List> GetPairs() + { + List> pairs = new List>(); + + for (int i = 0; i < Entries.Count; i++) + { + for (int j = i + 1; j < Entries.Count; j++) + { + if (Entries[i].BoundingBox.Intersects(Entries[j].BoundingBox) + && (Entries[i].IsDynamic || Entries[j].IsDynamic)) + { + // 序号小的在前面,这样判重方便一点 + if (Entries[i].ColliderId < Entries[j].ColliderId) + { + pairs.Add(new KeyValuePair(Entries[i].ColliderId, Entries[j].ColliderId)); + } + else + { + pairs.Add(new KeyValuePair(Entries[j].ColliderId, Entries[i].ColliderId)); + } + } + } + } + return pairs; + } + + public List> GetPairsWith(HGrid grid) + { + List> pairs = new List>(); + + for (int i = 0; i < Entries.Count; i++) + { + for (int j = 0; j < grid.Entries.Count; j++) + { + if (Entries[i].BoundingBox.Intersects(grid.Entries[j].BoundingBox) + && (Entries[i].IsDynamic || grid.Entries[j].IsDynamic)) + { + pairs.Add(new KeyValuePair(Entries[i].ColliderId, grid.Entries[j].ColliderId)); + } + } + } + return pairs; + } + + public List QueryRange(in AABB aabb) + { + List result = new List(); + for (int i = 0; i < Entries.Count; i++) + { + if (Entries[i].BoundingBox.Intersects(aabb)) + { + result.Add(Entries[i].ColliderId); + } + } + return result; + } + } + + private int _maxLevels; + private int _maxOccupiedGridsPerLevel; + private int[] _sizeThisLevel; + private Dictionary[] _multiGrids; + public MultiLevelHashGrid(int maxGridsPerObjectPerLevel, int[] sizesEachLevel) + { + _maxLevels = sizesEachLevel.Length; + _maxOccupiedGridsPerLevel = maxGridsPerObjectPerLevel; + _sizeThisLevel = new int[_maxLevels + 1]; + _sizeThisLevel[0] = 0; + for(int i = 1; i <= _maxLevels; i++) + { + _sizeThisLevel[i] = sizesEachLevel[i - 1]; + } + _multiGrids = new Dictionary[_maxLevels + 1]; + for (int i = 0; i <= _maxLevels; i++) + { + _multiGrids[i] = new Dictionary(); + } + } + + public void AddRange(List entries) + { + foreach (var entry in entries) + { + int level = FindMatchingLevel(entry.BoundingBox); + foreach (var grid in OccupiedRange(entry.BoundingBox, level)) + { + Insert(level, grid, entry); + } + } + } + + public void Add(ColliderEntry entry) + { + int level = FindMatchingLevel(entry.BoundingBox); + foreach (var grid in OccupiedRange(entry.BoundingBox, level)) + { + Insert(level, grid, entry); + } + } + + public List QueryRange(in AABB aabb) + { + List results = new List(1024); + for (int i = 0; i <= _maxLevels; i++) + { + results.AddRange(QueryRange_Level(aabb, i)); + } + return results; + } + + + private List QueryRange_Level(in AABB aabb, int level) + { + List results = new List(); + if (level == 0) + { + if (_multiGrids[0].ContainsKey(new Point(0, 0))) + { + // 和大物体的碰撞 + foreach (var largeEntry in _multiGrids[0][new Point(0, 0)].Entries) + { + if (largeEntry.BoundingBox.Intersects(aabb)) + { + results.Add(largeEntry.ColliderId); + } + } + } + return results; + } + + if (aabb.GetArea() < (double)_sizeThisLevel[level] * _sizeThisLevel[level] * _multiGrids[level].Count) + { + foreach (var grid in OccupiedRange(aabb, level)) + { + if (_multiGrids[level].ContainsKey(grid)) + { + var selfAABB = GetGridAABB(grid, level); + if (selfAABB.CompletelyInside(aabb)) + { + results.AddRange(_multiGrids[level][grid].Entries.Select(x => x.ColliderId).ToList()); + } + else + { + results.AddRange(_multiGrids[level][grid].QueryRange(aabb)); + } + } + } + } + else + { + foreach (var gridpair in _multiGrids[level]) + { + var selfAABB = GetGridAABB(gridpair.Key, level); + if (selfAABB.Intersects(aabb)) + { + if (selfAABB.CompletelyInside(aabb)) + { + results.AddRange(gridpair.Value.Entries.Select(x => x.ColliderId).ToList()); + } + else + { + results.AddRange(gridpair.Value.QueryRange(aabb)); + } + } + } + } + return results; + } + + public List> QueryPairs() + { + List> pairs = new List>(1024); + + for (int i = 0; i <= _maxLevels; i++) + { + foreach (var pair in _multiGrids[i]) + { + pairs.AddRange(pair.Value.GetPairs()); + } + } + + + for (int i = 1; i <= _maxLevels; i++) + { + // 和上层进行相互检测 + for (int j = 0; j < i; j++) + { + if (_multiGrids[j].Count == 0) + { + continue; + } + foreach (var pair in _multiGrids[i]) + { + var rp = new Point(0, 0); + var pos = new Point(pair.Key.X * _sizeThisLevel[i], pair.Key.Y * _sizeThisLevel[i]); + // 获取上层中当前格子对应位置 + if (j != 0) + { + int X = (int)Math.Floor((float)pos.X / _sizeThisLevel[j]); + int Y = (int)Math.Floor((float)pos.Y / _sizeThisLevel[j]); + rp = new Point(X, Y); + } + if (_multiGrids[j].ContainsKey(rp)) + { + pairs.AddRange(pair.Value.GetPairsWith(_multiGrids[j][rp])); + } + } + } + } + return pairs.Distinct().ToList(); + } + + + public List> QueryPairsWith(MultiLevelHashGrid other) + { + + List> pairs = new List>(1024); + // 尺寸相等格子 + for (int i = 0; i <= _maxLevels; i++) + { + if (_multiGrids[i].Count < other._multiGrids[i].Count) + { + foreach (var pair in _multiGrids[i]) + { + if (other._multiGrids[i].ContainsKey(pair.Key)) + { + pairs.AddRange(pair.Value.GetPairsWith(other._multiGrids[i][pair.Key])); + } + } + } + else + { + foreach (var pair in other._multiGrids[i]) + { + if (_multiGrids[i].ContainsKey(pair.Key)) + { + pairs.AddRange(_multiGrids[i][pair.Key].GetPairsWith(pair.Value)); + } + } + } + } + + // 自己尺寸比对方尺寸大 + for (int i = 0; i <= _maxLevels; i++) + { + for (int j = i + 1; j <= _maxLevels; j++) + { + foreach (var pair_other in other._multiGrids[j]) + { + Point rp = new Point(0, 0); + if (i != 0) + { + var pos = new Point(pair_other.Key.X * _sizeThisLevel[j], pair_other.Key.Y * _sizeThisLevel[j]); + // 获取上层中当前格子对应位置 + int X = (int)Math.Floor((float)pos.X / _sizeThisLevel[i]); + int Y = (int)Math.Floor((float)pos.Y / _sizeThisLevel[i]); + rp = new Point(X, Y); + } + if (_multiGrids[i].ContainsKey(rp)) + { + pairs.AddRange(_multiGrids[i][rp].GetPairsWith(pair_other.Value)); + } + } + } + } + + // 自己尺寸比对方尺寸小 + for (int i = 0; i <= _maxLevels; i++) + { + for (int j = i + 1; j <= _maxLevels; j++) + { + foreach (var pair_self in _multiGrids[j]) + { + Point rp = new Point(0, 0); + if (i != 0) + { + var pos = new Point(pair_self.Key.X * _sizeThisLevel[j], pair_self.Key.Y * _sizeThisLevel[j]); + // 获取上层中当前格子对应位置 + int X = (int)Math.Floor((float)pos.X / _sizeThisLevel[i]); + int Y = (int)Math.Floor((float)pos.Y / _sizeThisLevel[i]); + rp = new Point(X, Y); + } + if (other._multiGrids[i].ContainsKey(rp)) + { + pairs.AddRange(pair_self.Value.GetPairsWith(other._multiGrids[i][rp])); + } + } + } + } + return pairs.Distinct().ToList(); + } + + private int FindMatchingLevel(in AABB aabb) + { + int ans = _maxLevels; + int l = 0, r = _maxLevels; + while (l <= r) + { + int mid = (l + r) >> 1; + if (CanFitInThisLevel(aabb, mid)) + { + l = mid + 1; + ans = mid; + } + else + { + r = mid - 1; + } + } + return ans; + } + + private void Insert(int level, Point pos, ColliderEntry entry) + { + if (!_multiGrids[level].ContainsKey(pos)) + { + _multiGrids[level].Add(pos, new HGrid()); + } + + _multiGrids[level][pos].Entries.Add(entry); + } + + private List OccupiedRange(in AABB aabb, int level) + { + if (level == 0) + { + return new List() + { + new Point(0, 0), + }; + } + int minX = (int)Math.Floor(aabb.MinPoint.X / _sizeThisLevel[level]); + int maxX = (int)Math.Floor(aabb.MaxPoint.X / _sizeThisLevel[level]); + int minY = (int)Math.Floor(aabb.MinPoint.Y / _sizeThisLevel[level]); + int maxY = (int)Math.Floor(aabb.MaxPoint.Y / _sizeThisLevel[level]); + + List points = new List(); + for (int x = minX; x <= maxX; x++) + { + for (int y = minY; y <= maxY; y++) + { + points.Add(new Point(x, y)); + } + } + return points; + } + + private bool CanFitInThisLevel(in AABB aabb, int level) + { + if (level == 0) + { + return true; + } + int minX = (int)Math.Floor(aabb.MinPoint.X / _sizeThisLevel[level]); + int maxX = (int)Math.Floor(aabb.MaxPoint.X / _sizeThisLevel[level]); + int minY = (int)Math.Floor(aabb.MinPoint.Y / _sizeThisLevel[level]); + int maxY = (int)Math.Floor(aabb.MaxPoint.Y / _sizeThisLevel[level]); + return Math.Floor((aabb.MaxPoint.X - aabb.MinPoint.X) / _sizeThisLevel[level]) + 1 <= _maxOccupiedGridsPerLevel + && Math.Floor((aabb.MaxPoint.Y - aabb.MinPoint.Y) / _sizeThisLevel[level]) + 1 <= _maxOccupiedGridsPerLevel + && (maxX - minX + 1) * (maxY - minY + 1) <= _maxOccupiedGridsPerLevel; + } + + private AABB GetGridAABB(Point p, int level) + { + if (level == 0) + { + // 0层无限大的AABB + return new AABB() + { + MinPoint = new Vector2(float.NegativeInfinity, float.NegativeInfinity), + MaxPoint = new Vector2(float.PositiveInfinity, float.PositiveInfinity) + }; + } + int sz = _sizeThisLevel[level]; + return new AABB() + { + MinPoint = new Vector2(p.X * sz, p.Y * sz), + MaxPoint = new Vector2(p.X * sz + sz, p.Y * sz + sz), + }; + } + + public List GetProfilingData() + { + List result = new List(); + for (int i = 1; i <= _maxLevels; i++) + { + foreach (var pair in _multiGrids[i]) + { + result.Add(new ProfilingData() + { + GridBox = GetGridAABB(pair.Key, i), + NumObjects = pair.Value.Entries.Count + }); + } + } + return result; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs index 7a888255e..d919e3371 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs @@ -14,130 +14,190 @@ // public float V; // public bool IsEnd; // public int ColliderId; +// public bool IsDynamic; // } -// private List _pointsX; -// private List _pointsY; -// private List _colliders; +// private class GroupInfo +// { +// public List IntervalPointsX; +// public List IntervalPointsY; + +// public GroupInfo() +// { +// IntervalPointsX = new List(); +// IntervalPointsY = new List(); +// } +// } -// public SweepAndPrune() +// private List _colliders; +// private Dictionary _groups; +// public SweepAndPrune(CollisionGraph graph) : base(graph) // { -// _pointsX = new List(); -// _pointsY = new List(); // _colliders = new List(); +// _groups = new Dictionary(); +// } +// private bool IsDynamic(int id) +// { +// return _colliders[id].ParentObject.RigidBody.MovementType == MovementType.Dynamic +// || _colliders[id].ParentObject.RigidBody.MovementType == MovementType.Player; +// } + +// public override void Prepare(List objects, float deltaTime) +// { +// _colliders.Clear(); +// _colliders.EnsureCapacity(objects.Count); +// _groups.Clear(); +// for (int i = 0; i < objects.Count; i++) +// { +// var obj = objects[i]; +// _colliders.Add(obj.Collider); +// var box = obj.Collider.GetAABB(deltaTime); +// bool isDynamic = obj.RigidBody.MovementType == MovementType.Dynamic +// || obj.RigidBody.MovementType == MovementType.Player; + +// if (!_groups.ContainsKey(obj.Tag)) +// { +// _groups.Add(obj.Tag, new GroupInfo()); +// } +// _groups[obj.Tag].IntervalPointsX.Add(new IntervalPoint() { ColliderId = i, IsEnd = false, V = box.MinPoint.X, IsDynamic = isDynamic }); +// _groups[obj.Tag].IntervalPointsX.Add(new IntervalPoint() { ColliderId = i, IsEnd = true, V = box.MaxPoint.X, IsDynamic = isDynamic }); +// _groups[obj.Tag].IntervalPointsY.Add(new IntervalPoint() { ColliderId = i, IsEnd = false, V = box.MinPoint.Y, IsDynamic = isDynamic }); +// _groups[obj.Tag].IntervalPointsY.Add(new IntervalPoint() { ColliderId = i, IsEnd = true, V = box.MaxPoint.Y, IsDynamic = isDynamic }); +// } + +// Comparer compFunc = Comparer.Create((a, b) => +// { +// int cmp1 = a.V.CompareTo(b.V); +// if (cmp1 == 0) +// { +// if (a.IsEnd && !b.IsEnd) +// { +// return 1; +// } +// else if (!a.IsEnd && b.IsEnd) +// { +// return -1; +// } +// return 0; +// } +// return cmp1; +// }); +// foreach (var group in _groups) +// { +// group.Value.IntervalPointsX.Sort(compFunc); +// group.Value.IntervalPointsY.Sort(compFunc); +// } // } -// public List> GetCollisionPairs(float deltaTime) + +// public override List> GetCollisionPairs(float deltaTime) // { // List> finalPairs = new List>(); -// HashSet> spottedPairs = new HashSet>(); -// HashSet current = new HashSet(); -// for (int i = 0; i < _pointsX.Count; i++) +// foreach (var group in _groups) // { -// var pt = _pointsX[i]; -// if (pt.IsEnd) +// GroupInnerDetection(group.Key, finalPairs); +// if (_collisionGraph.Graph.ContainsKey(group.Key)) // { -// foreach (var c in current) +// foreach (var dual in _collisionGraph.Graph[group.Key]) // { -// if (c == pt.ColliderId) +// if (!_groups.ContainsKey(dual)) // continue; -// var p = new KeyValuePair(pt.ColliderId, c); +// GroupOuterDetection(group.Key, dual, finalPairs); +// } +// } +// } +// return finalPairs; +// } -// if (_colliders[p.Key].ParentObject.RigidBody.MovementType == MovementType.Static -// && _colliders[p.Value].ParentObject.RigidBody.MovementType == MovementType.Static) +// private void GroupInnerDetection(string key, List> pairs) +// { +// var xpoints = _groups[key].IntervalPointsX; +// var ypoints = _groups[key].IntervalPointsY; + +// HashSet xSet = new HashSet(); +// HashSet ySet = new HashSet(); +// List> xPairs = new List>(); +// List> yPairs = new List>(); +// foreach (var pt in xpoints) +// { +// if (pt.IsEnd) +// { +// xSet.Remove(pt.ColliderId); +// foreach (var c in xSet) +// { +// if (pt.IsDynamic || IsDynamic(c)) // { -// continue; +// // 序号小的在前面,这样判重方便一点 +// if (pt.ColliderId < c) +// { +// xPairs.Add(new KeyValuePair(pt.ColliderId, c)); +// } +// else +// { +// xPairs.Add(new KeyValuePair(c, pt.ColliderId)); +// } // } -// spottedPairs.Add(p); // } -// current.Remove(pt.ColliderId); // } // else // { -// current.Add(pt.ColliderId); +// xSet.Add(pt.ColliderId); // } // } -// current.Clear(); - -// for (int i = 0; i < _pointsY.Count; i++) +// foreach (var pt in ypoints) // { -// var pt = _pointsY[i]; // if (pt.IsEnd) // { -// foreach (var c in current) +// ySet.Remove(pt.ColliderId); +// foreach (var c in ySet) // { -// if (c == pt.ColliderId) -// continue; -// var p = new KeyValuePair(pt.ColliderId, c); -// if (spottedPairs.Contains(p) -// || spottedPairs.Contains(new KeyValuePair(c, pt.ColliderId))) +// if (pt.IsDynamic || IsDynamic(c)) // { -// if (_colliders[p.Key].ParentObject.RigidBody.MovementType == MovementType.Static -// && _colliders[p.Value].ParentObject.RigidBody.MovementType == MovementType.Static) +// // 序号小的在前面,这样判重方便一点 +// if (pt.ColliderId < c) // { -// continue; +// yPairs.Add(new KeyValuePair(pt.ColliderId, c)); +// } +// else +// { +// yPairs.Add(new KeyValuePair(c, pt.ColliderId)); // } -// finalPairs.Add(new KeyValuePair(_colliders[p.Key], _colliders[p.Value])); // } // } -// current.Remove(pt.ColliderId); // } // else // { -// current.Add(pt.ColliderId); +// ySet.Add(pt.ColliderId); // } // } -// return finalPairs; -// } -// public void Prepare(List dynamicObjects, List staticObjects, float deltaTime) -// { -// _pointsX.Clear(); -// _pointsY.Clear(); -// _colliders.Clear(); -// _colliders.EnsureCapacity(dynamicObjects.Count + staticObjects.Count); -// for (int i = 0; i < dynamicObjects.Count; i++) -// { -// var obj = dynamicObjects[i]; -// var aabb = obj.Collider.GetAABB(deltaTime); -// _pointsX.Add(new IntervalPoint() { V = aabb.MinPoint.X, IsEnd = false, ColliderId = i }); -// _pointsX.Add(new IntervalPoint() { V = aabb.MaxPoint.X, IsEnd = true, ColliderId = i }); -// _pointsY.Add(new IntervalPoint() { V = aabb.MinPoint.Y, IsEnd = false, ColliderId = i }); -// _pointsY.Add(new IntervalPoint() { V = aabb.MaxPoint.Y, IsEnd = true, ColliderId = i }); -// _colliders.Add(obj.Collider); -// } +// xPairs.AddRange(yPairs); -// for (int i = 0; i < staticObjects.Count; i++) +// xPairs.Sort((a, b) => // { -// var obj = staticObjects[i]; -// var aabb = obj.Collider.GetAABB(deltaTime); -// int id = staticObjects.Count + i; -// _pointsX.Add(new IntervalPoint() { V = aabb.MinPoint.X, IsEnd = false, ColliderId = id }); -// _pointsX.Add(new IntervalPoint() { V = aabb.MaxPoint.X, IsEnd = true, ColliderId = id }); -// _pointsY.Add(new IntervalPoint() { V = aabb.MinPoint.Y, IsEnd = false, ColliderId = id }); -// _pointsY.Add(new IntervalPoint() { V = aabb.MaxPoint.Y, IsEnd = true, ColliderId = id }); -// _colliders.Add(obj.Collider); -// } +// int cmp1 = a.Key.CompareTo(b.Key); +// if (cmp1 == 0) +// { +// return a.Value.CompareTo(b.Value); +// } +// return cmp1; +// }); -// Comparison cmp = (a, b) => +// for(int i = 0; i < xPairs.Count; i++) // { -// int c = a.V.CompareTo(b.V); -// if (c == 0) +// var pt = xPairs[i]; +// if (i == xPairs.Count - 1 || xPairs[i + 1].Key != pt.Key || xPairs[i + 1].Value != pt.Value) // { -// if (a.IsEnd && !b.IsEnd) -// { -// return -1; -// } -// if (!a.IsEnd && b.IsEnd) -// { -// return 1; -// } -// return 0; +// continue; // } -// return c; -// }; -// _pointsX.Sort(cmp); -// _pointsY.Sort(cmp); +// pairs.Add(new KeyValuePair(_colliders[pt.Key], _colliders[pt.Value])); +// i++; +// } +// } + +// public override List GetSingleCollision(AABB aabb, Vector2 velocity, float deltaTime, List targetTags) +// { +// throw new NotImplementedException(); // } // } //} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs index 9ce31289c..db22fb884 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs @@ -1,3 +1,4 @@ +using ChatGPT.Core.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Utilities; using System; @@ -130,30 +131,13 @@ public List GetCornerPoints(float dt) new Vector2(_width / 2, _height / 2), new Vector2(-_width / 2, _height / 2) }; - if (_bindObject.RigidBody != null) - { - var M = Matrix2x2.CreateRotationMatrix(_bindObject.OldRotation + _bindObject.RigidBody.AngularVelocity * dt); - for (int i = 0; i < 4; i++) - { - var curPos = _bindObject.OldPosition + _bindObject.RigidBody.LinearVelocity * dt - + M.Multiply(localPoints[i]); - cornerPoints.Add(curPos); - } - } - else + var M = ParentObject.CachedRotationalMatrix; + for (int i = 0; i < 4; i++) { - var M = Matrix2x2.CreateRotationMatrix(_bindObject.Rotation); - for (int i = 0; i < 4; i++) - { - - var curPos = _bindObject.Position - + M.Multiply(localPoints[i]); - cornerPoints.Add(curPos); - } - + var curPos = ParentObject.Position + M.Multiply(localPoints[i]); + cornerPoints.Add(curPos); } - return cornerPoints; } @@ -169,7 +153,7 @@ public override bool TestCollisionCondition(Collider2D other, float deltaTime, o List cornersB = b.GetCornerPoints(deltaTime); float depth; Vector2 normal; - if (GeometryUtils.PolygonPolygonCollisionInfo(edgesA, cornersA, edgesB, cornersB, out depth, out normal)) + if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edgesA, cornersA, edgesB, cornersB, out depth, out normal)) { info.Source = this.ParentObject; info.Target = b.ParentObject; @@ -184,7 +168,7 @@ public override bool TestCollisionCondition(Collider2D other, float deltaTime, o SphereCollider b = (SphereCollider)other; float depth; Vector2 normal; - bool collided = GeometryUtils.CirclePolygonCollisionInfo(b.ParentObject.Position, b.Radius, GetEdges(deltaTime), + bool collided = GeometryUtils.SphereConvexPolygonCollisionInfo(b.ParentObject.Position, b.Radius, GetEdges(deltaTime), GetCornerPoints(deltaTime), out depth, out normal); if (collided) @@ -208,7 +192,23 @@ public override bool TestCollisionCondition(Collider2D other, float deltaTime, o info.Target = other.ParentObject; info.Time = deltaTime; info.Depth = depth; - info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - other.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + info.Normal = normal; + return true; + } + } + else if(other is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)other; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(A, B, b.Radius, GetCornerPoints(deltaTime), + GetEdges(deltaTime), + out float depth, out Vector2 normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; return true; } } @@ -238,7 +238,7 @@ public override void GetContactInfo(in CollisionInfo info, float deltaTime, Depth = info.Depth, }; List> contacts = new List>(); - GeometryUtils.PolygonPolygonContactInfo(edgesA, cornersA, edgesB, cornersB, + GeometryUtils.ConvexPolygonPolygonContactInfo(edgesA, cornersA, edgesB, cornersB, ParentObject.RigidBody.CentroidWorldSpace, b.ParentObject.RigidBody.CentroidWorldSpace, contacts); foreach (var c in contacts) @@ -266,7 +266,7 @@ public override void GetContactInfo(in CollisionInfo info, float deltaTime, }; List> contacts = new List>(); - GeometryUtils.CirclePolygonContactInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, GetEdges(deltaTime), + GeometryUtils.SphereConvexPolygonContactInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, GetEdges(deltaTime), GetCornerPoints(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, contacts); foreach (var c in contacts) { @@ -278,6 +278,33 @@ public override void GetContactInfo(in CollisionInfo info, float deltaTime, } } + else if (info.Target.Collider is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + List> contacts = new List>(); + GeometryUtils.CapsuleConvexPolygonContactInfo(A, B, b.Radius, + GetCornerPoints(deltaTime), GetEdges(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Value, + LocalOffsetTarget = c.Key + }); + } + } else if (info.Target.Collider is TileCollider) { TileCollisionUtils.GetPolygonTileContactInfo(GetAABB(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs new file mode 100644 index 000000000..d9f641060 --- /dev/null +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs @@ -0,0 +1,267 @@ +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.GameInteraction; +using Everglow.Commons.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using static Humanizer.In; + +namespace ChatGPT.Core.PBEngine.Collision.Colliders +{ + internal class CapsuleCollider : Collider2D + { + private float _length; + private float _radius; + + public float Length + { + get => _length; + set => _length = value; + } + public float Radius + { + get => _radius; + set => _radius = value; + } + public CapsuleCollider(float length, float radius) + { + _length = length; + _radius = radius; + } + + public override AABB GetAABB(float deltaTime) + { + var M = Matrix2x2.CreateRotationMatrix(ParentObject.OldRotation + ParentObject.RigidBody.AngularVelocity * deltaTime); + Vector2 P1 = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime + + M.Multiply(new Vector2(_length / 2, 0)); + Vector2 P2 = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime + + M.Multiply(new Vector2(-_length / 2, 0)); + + if(P1.X > P2.X) + { + (P2.X, P1.X) = (P1.X, P2.X); + } + if (P1.Y > P2.Y) + { + (P2.Y, P1.Y) = (P1.Y, P2.Y); + } + return new AABB() + { + MinPoint = P1 - new Vector2(_radius, _radius), + MaxPoint = P2 + new Vector2(_radius, _radius), + }; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) + { + collisionEvents = new List(); + if (info.Target.Collider is SphereCollider) + { + SphereCollider b = (SphereCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + List> contacts = new List>(); + GeometryUtils.SphereCapsuleContactInfo(b.ParentObject.RigidBody.CentroidWorldSpace, + b.Radius, A, B, _radius, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Value, + LocalOffsetTarget = c.Key, + }); + } + } + else if (info.Target.Collider is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + b.GetSegment(deltaTime, out Vector2 A2, out Vector2 B2); + List> contacts = new List>(); + GeometryUtils.CapsuleCapsuleContactInfo(A, B, _radius, A2, B2, b._radius, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is BoxCollider) + { + BoxCollider b = (BoxCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + List> contacts = new List>(); + GeometryUtils.CapsuleConvexPolygonContactInfo(A, B, _radius, + b.GetCornerPoints(deltaTime), b.GetEdges(deltaTime), b.ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is TileCollider) + { + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + TileCollisionUtils.GetCapsuleTileContactInfo(GetAABB(deltaTime), A, B,_radius, ParentObject, info.Target, deltaTime, collisionEvents); + } + } + + public override List GetWireFrameWires() + { + List lines = new List(); + lines.Add(new Vector2(_length / 2, _radius)); + lines.Add(new Vector2(-_length / 2, _radius)); + + float da = MathHelper.Pi / 32; + var leftCenter = new Vector2(-_length / 2, 0); + for (int i = 0; i < 32; i++) + { + lines.Add(leftCenter + (i * da + MathHelper.PiOver2).ToRotationVector2() * _radius); + lines.Add(leftCenter + ((i + 1) * da + MathHelper.PiOver2).ToRotationVector2() * _radius); + } + lines.Add(new Vector2(-_length / 2, -_radius)); + lines.Add(new Vector2(_length / 2, -_radius)); + var rightCenter = new Vector2(_length / 2, 0); + for (int i = 0; i < 32; i++) + { + lines.Add(rightCenter + (i * da - MathHelper.PiOver2).ToRotationVector2() * _radius); + lines.Add(rightCenter + ((i + 1) * da - MathHelper.PiOver2).ToRotationVector2() * _radius); + } + lines.Add(Vector2.Zero); + lines.Add(new Vector2(_length / 2 + _radius, 0)); + return lines; + } + + public override double InertiaTensor(float mass) + { + float massOfBox = mass * 2 * _radius * _length / (2 * _radius * _length + MathHelper.Pi * _radius * _radius); + float massOfSphere = mass - massOfBox; + float moi_box = massOfBox / 12.0f * (_length * _length + 4 * _radius * _radius); + float moi_sphere = 0.5f * massOfSphere * _radius * _radius; + float d1 = 3.0f / 8.0f * _radius; + float moi_hsphere = 0.5f * moi_sphere - 0.5f * massOfSphere * d1 * d1; + float d2 = _length / 2 + d1; + moi_hsphere += 0.5f * massOfSphere * d2 * d2; + return moi_box + 2 * moi_hsphere; + } + + public void GetSegment(float dt, out Vector2 segA, out Vector2 segB) + { + var M = ParentObject.CachedRotationalMatrix; + var pos = ParentObject.Position; + segA = pos + M.Multiply(new Vector2(_length / 2, 0)); + segB = pos + M.Multiply(new Vector2(-_length / 2, 0)); + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + info = new CollisionInfo(); + if (other is SphereCollider) + { + SphereCollider b = (SphereCollider)other; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.SphereCapsuleCollisionInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, + A, B, _radius, out float depth, out Vector2 normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = -normal; + return true; + } + } + else if (other is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)other; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + b.GetSegment(deltaTime, out Vector2 A2, out Vector2 B2); + if (GeometryUtils.CapsuleCapsuleCollisionInfo(A, B, _radius, A2, B2, b._radius, + out float depth, out Vector2 normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is BoxCollider) + { + BoxCollider b = (BoxCollider)other; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(A, B, _radius, b.GetCornerPoints(deltaTime), + b.GetEdges(deltaTime), + out float depth, out Vector2 normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is TileCollider) + { + float depth; + Vector2 normal; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (TileCollisionUtils.GetCapsuleTileCollisionInfo(GetAABB(deltaTime), A, B, _radius, out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = other.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = normal; + return true; + } + } + + return false; + } + } +} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs index d60629b3a..aaa78fb2d 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs @@ -1,3 +1,4 @@ +using ChatGPT.Core.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Utilities; using System; @@ -73,7 +74,7 @@ public override void GetContactInfo(in CollisionInfo info, float deltaTime, out }; List> contacts = new List>(); - GeometryUtils.CirclePolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, b.GetEdges(deltaTime), + GeometryUtils.SphereConvexPolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, b.GetEdges(deltaTime), b.GetCornerPoints(deltaTime), b.ParentObject.RigidBody.CentroidWorldSpace, contacts); foreach (var c in contacts) { @@ -89,6 +90,33 @@ public override void GetContactInfo(in CollisionInfo info, float deltaTime, out TileCollisionUtils.GetSphereTileContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, ParentObject, info.Target, deltaTime, collisionEvents); } + else if (info.Target.Collider is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + List> contacts = new List>(); + GeometryUtils.SphereCapsuleContactInfo(ParentObject.RigidBody.CentroidWorldSpace, + _radius, A, B, b.Radius, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } } //public override List GetContactInfo(CollisionEvent2D e, float deltaTime) @@ -138,7 +166,7 @@ public override List GetWireFrameWires() lines.Add(((i + 1) * da).ToRotationVector2() * _radius); } lines.Add(Vector2.Zero); - lines.Add(Matrix2x2.CreateRotationMatrix(ParentObject.Rotation).Multiply(new Vector2(_radius, 0))); + lines.Add(ParentObject.CachedRotationalMatrix.Multiply(new Vector2(_radius, 0))); return lines; } @@ -169,7 +197,7 @@ public override bool TestCollisionCondition(Collider2D other, float deltaTime, o BoxCollider b = (BoxCollider)other; float depth; Vector2 normal; - if (GeometryUtils.CirclePolygonCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, + if (GeometryUtils.SphereConvexPolygonCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, b.GetEdges(deltaTime), b.GetCornerPoints(deltaTime), out depth, out normal)) { info.Source = this.ParentObject; @@ -195,6 +223,21 @@ public override bool TestCollisionCondition(Collider2D other, float deltaTime, o return true; } } + else if (other is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)other; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.SphereCapsuleCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, Radius, + A, B, b.Radius, out float depth, out Vector2 normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = normal; + return true; + } + } return false; } } diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs index 1bf3abbaf..8d6826e8d 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs @@ -29,7 +29,9 @@ public class CollisionEvent2D /// From centroid of target object to hit point /// public Vector2 LocalOffsetTarget; + public float NormalVelOld; + public Vector2 TangentDir; public CollisionEvent2D(CollisionEvent2D e) { diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs b/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs index 3ec8fbbc5..5174fdc5e 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs @@ -9,7 +9,8 @@ namespace Everglow.Commons.Physics.PBEngine.Constrains public abstract class Constrain { public abstract void Apply(float deltaTime); + public abstract void ApplyForce(float deltaTime); - public abstract List GetDrawMesh(); + public abstract List<(Vector2, Color)> GetDrawMesh(); } } diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs b/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs index 7c1a8283d..05fa7f979 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs @@ -51,11 +51,16 @@ public override void Apply(float deltaTime) //_objB.RigidBody.AngularVelocity = MathHelper.WrapAngle(_objB.Rotation - _objB.OldRotation) / deltaTime; } - public override List GetDrawMesh() + public override void ApplyForce(float deltaTime) { - List drawMesh = new List(); - drawMesh.Add(_objA.LocalToWorldPos(_localPosA)); - drawMesh.Add(_objB.RigidBody.CentroidWorldSpace); + throw new NotImplementedException(); + } + + public override List<(Vector2, Color)> GetDrawMesh() + { + List<(Vector2, Color)> drawMesh = new List<(Vector2, Color)>(); + drawMesh.Add((_objA.LocalToWorldPos(_localPosA), Color.Green)); + drawMesh.Add((_objB.RigidBody.CentroidWorldSpace, Color.Green)); return drawMesh; } diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs b/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs index 144620b7a..d16bbf656 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs @@ -17,7 +17,7 @@ public class SpringConstrain : Constrain private PhysicsObject _objB; private float _compliance; private float _restLength; - private float _lambda; + private double _lambda; private Vector2 _localPosA; private Vector2 _localPosB; @@ -59,73 +59,117 @@ public static double CalculateShortestAngleDifference(double angle1, double angl } public override void Apply(float deltaTime) + { + //var pA = _objA.LocalToWorldPos(_localPosA); + //var pB = _objB.LocalToWorldPos(_localPosB); + //var ra = Matrix2x2.CreateRotationMatrix(_objA.Rotation).Multiply(_localPosA); + //var rb = Matrix2x2.CreateRotationMatrix(_objB.Rotation).Multiply(_localPosB); + //var L = (pB - pA).Length(); + //var unit = (pB - pA).SafeNormalize(Vector2.Zero); + //float C = L - _restLength; + //if (C == 0) + //{ + // return; + //} + + //double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ra), -(pB - pA)); + //double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), (pB - pA)); + //double R1 = rAdotN * rAdotN * _objA.RigidBody.GlobalInverseInertiaTensor; + //double R2 = rBdotN * rBdotN * _objB.RigidBody.GlobalInverseInertiaTensor; + //float effMassA = (float)(_objA.RigidBody.InvMass + R1); + //float effMassB = (float)(_objB.RigidBody.InvMass + R2); + //float alpha = _compliance / deltaTime / deltaTime; + //double d_lambda = (-C - alpha * _lambda) / (effMassA + effMassB + alpha); + //_lambda = _lambda + d_lambda; + + ////var posA = pA + (float)(_lambda * effMassA ) * -unit ; + ////var posB = pB + (float)(_lambda * effMassB) * unit; + ////float rotA_p = (pA - _objA.RigidBody.CentroidWorldSpace).ToRotation() - _localPosA.ToRotation(); + ////float rotA = (float)CalculateShortestAngleDifference((pA - _objA.RigidBody.CentroidWorldSpace).ToRotation(), + //// (pB - _objA.RigidBody.CentroidWorldSpace).ToRotation()); + ////float rotB_p = (pB - _objB.RigidBody.CentroidWorldSpace).ToRotation() - _localPosB.ToRotation(); + ////float rotB = (float)CalculateShortestAngleDifference((pB - _objB.RigidBody.CentroidWorldSpace).ToRotation(), + //// (posB - _objB.RigidBody.CentroidWorldSpace).ToRotation()); + + //if (_objB.RigidBody.GlobalInverseInertiaTensor != 0) + //{ + // _objB.RigidBody.AddImpluseImmediate((float)(d_lambda ) * (pB - pA), rb); + // //_objB.Rotation += _objB.RigidBody.AngularVelocity * deltaTime; + // //_objB.RigidBody.CentroidWorldSpace += _objB.RigidBody.LinearVelocity * deltaTime; + // //_objB.RigidBody.LinearVelocity = (_objB.RigidBody.CentroidWorldSpace - _objB.OldPosition) / deltaTime; + // //_objB.RigidBody.AngularVelocity = (float)CalculateShortestAngleDifference(_objB.OldRotation, _objB.Rotation) / deltaTime; + //} + //if (_objA.RigidBody.GlobalInverseInertiaTensor != 0) + //{ + // _objA.RigidBody.AddImpluseImmediate((float)(d_lambda) * -(pB - pA), ra); + // //_objA.Rotation += _objA.RigidBody.AngularVelocity * deltaTime; + // //_objA.RigidBody.CentroidWorldSpace += _objA.RigidBody.LinearVelocity * deltaTime; + // //_objA.Rotation += rotA; + // //_objA.RigidBody.CentroidWorldSpace += (posA - _objA.LocalToWorldPos(_localPosA)); + // //_objA.RigidBody.LinearVelocity = (_objA.RigidBody.CentroidWorldSpace - _objA.OldPosition) / deltaTime; + // //_objA.RigidBody.AngularVelocity = (float)CalculateShortestAngleDifference(_objA.OldRotation, _objA.Rotation) / deltaTime; + //} + + + + + + //var ra = Matrix2x2.CreateRotationMatrix(_objA.Rotation).Multiply(_localPosA); + //var rb = Matrix2x2.CreateRotationMatrix(_objB.Rotation).Multiply(_localPosB); + //var da = (_lambda * _objA.RigidBody.InvMass * -unit).SafeNormalize(Vector2.Zero); + //var db = (_lambda * _objB.RigidBody.InvMass * unit).SafeNormalize(Vector2.Zero); + //var oldVelA = Vector2.Dot(_objA.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ra, _objA.RigidBody.AngularVelocity), da); + //var oldVelB = Vector2.Dot(_objB.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, _objB.RigidBody.AngularVelocity), db); + + //double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ra), da); + //double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), db); + //double R1 = rAdotN * rAdotN * _objA.RigidBody.GlobalInverseInertiaTensor; + //double R2 = rBdotN * rBdotN * _objB.RigidBody.GlobalInverseInertiaTensor; + //var impluseA = ((curPosA - pA).Length() / deltaTime - oldVelA) / (_objA.RigidBody.InvMass + (float)R1); + //var impluseB = ((curPosB - pB).Length() / deltaTime - oldVelB) / (_objB.RigidBody.InvMass + (float)R2); + + //if (_objA.RigidBody.InvMass != 0) + // { + // // _objA.RigidBody.AddImpluseImmediate(impluseA * da, ra); + // _objA.Position += _objA.RigidBody.LinearVelocity * deltaTime; + // _objA.Rotation += _objA.RigidBody.AngularVelocity * deltaTime; + + // _objA.RigidBody.LinearVelocity = (_objA.Position - _objA.OldPosition) / deltaTime; + //} + // if (_objB.RigidBody.InvMass != 0) + // { + // _objB.RigidBody.AddImpluseImmediate(impluseB * db, rb); + //_objB.Position += _objB.RigidBody.LinearVelocity * deltaTime; + //_objB.Rotation += _objB.RigidBody.AngularVelocity * deltaTime; + //var test = _objB.LocalToWorldPos(_localPosB); + //if (true) + // ; + + //_objB.RigidBody.LinearVelocity = (_objB.Position - _objB.OldPosition) / deltaTime; + //_objB.RigidBody.AngularVelocity = (_objB.Rotation - _objB.OldRotation) / deltaTime; + // } + //_objA.RigidBody.AngularVelocity = Utils.Cross(_localPosA, _objA.RigidBody.LinearVelocity); + //_objB.RigidBody.AngularVelocity = Utils.Cross(_localPosB, _objB.RigidBody.LinearVelocity); + } + + public override void ApplyForce(float deltaTime) { var pA = _objA.LocalToWorldPos(_localPosA); var pB = _objB.LocalToWorldPos(_localPosB); var L = (pB - pA).Length(); var unit = (pB - pA).SafeNormalize(Vector2.Zero); float C = L - _restLength; - float alpha = _compliance / deltaTime / deltaTime; - float d_lambda = (-C - alpha * _lambda) / (_objA.RigidBody.InvMass + _objB.RigidBody.InvMass + alpha); - - _lambda += d_lambda; - var curPosA = pA + _lambda * _objA.RigidBody.InvMass * -unit; - var curPosB = pB + _lambda * _objB.RigidBody.InvMass * unit; - - - _objB.RigidBody.AddForce(40 * (L - _restLength) * -unit, _localPosB); - //float rotB_p = (pB - _objB.RigidBody.CentroidWorldSpace).ToRotation() - _localPosB.ToRotation(); - //float rotB = (curPosB - _objB.RigidBody.CentroidWorldSpace).ToRotation() - _localPosB.ToRotation(); - - //_objB.Rotation = rotB; - - //_objB.RigidBody.CentroidWorldSpace += curPosB - _objB.LocalToWorldPos(_localPosB); - //_objB.RigidBody.LinearVelocity = (_objB.Position - _objB.OldPosition) / deltaTime; - //_objB.RigidBody.AngularVelocity = (float)CalculateShortestAngleDifference(_objB.OldRotation, _objB.Rotation) / deltaTime; - - //var ra = Matrix2x2.CreateRotationMatrix(_objA.Rotation).Multiply(_localPosA); - //var rb = Matrix2x2.CreateRotationMatrix(_objB.Rotation).Multiply(_localPosB); - //var da = (_lambda * _objA.RigidBody.InvMass * -unit).SafeNormalize(Vector2.Zero); - //var db = (_lambda * _objB.RigidBody.InvMass * unit).SafeNormalize(Vector2.Zero); - //var oldVelA = Vector2.Dot(_objA.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ra, _objA.RigidBody.AngularVelocity), da); - //var oldVelB = Vector2.Dot(_objB.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, _objB.RigidBody.AngularVelocity), db); - - //double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ra), da); - //double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), db); - //double R1 = rAdotN * rAdotN * _objA.RigidBody.GlobalInverseInertiaTensor; - //double R2 = rBdotN * rBdotN * _objB.RigidBody.GlobalInverseInertiaTensor; - //var impluseA = ((curPosA - pA).Length() / deltaTime - oldVelA) / (_objA.RigidBody.InvMass + (float)R1); - //var impluseB = ((curPosB - pB).Length() / deltaTime - oldVelB) / (_objB.RigidBody.InvMass + (float)R2); - - if (_objA.RigidBody.InvMass != 0) - { - // _objA.RigidBody.AddImpluseImmediate(impluseA * da, ra); - _objA.Position += _objA.RigidBody.LinearVelocity * deltaTime; - _objA.Rotation += _objA.RigidBody.AngularVelocity * deltaTime; - - _objA.RigidBody.LinearVelocity = (_objA.Position - _objA.OldPosition) / deltaTime; - } - if (_objB.RigidBody.InvMass != 0) - { - // _objB.RigidBody.AddImpluseImmediate(impluseB * db, rb); - //_objB.Position += _objB.RigidBody.LinearVelocity * deltaTime; - //_objB.Rotation += _objB.RigidBody.AngularVelocity * deltaTime; - //var test = _objB.LocalToWorldPos(_localPosB); - //if (true) - // ; - - //_objB.RigidBody.LinearVelocity = (_objB.Position - _objB.OldPosition) / deltaTime; - //_objB.RigidBody.AngularVelocity = (_objB.Rotation - _objB.OldRotation) / deltaTime; - } - //_objA.RigidBody.AngularVelocity = Utils.Cross(_localPosA, _objA.RigidBody.LinearVelocity); - //_objB.RigidBody.AngularVelocity = Utils.Cross(_localPosB, _objB.RigidBody.LinearVelocity); + var ra = _objA.CachedRotationalMatrix.Multiply(_localPosA); + var rb = _objB.CachedRotationalMatrix.Multiply(_localPosB); + _objB.RigidBody.AddForce(C * -unit / _compliance, rb); + _objA.RigidBody.AddForce(C * unit / _compliance, ra); } - public override List GetDrawMesh() + public override List<(Vector2, Color)> GetDrawMesh() { - List drawMesh = new List(); - drawMesh.Add(_objA.LocalToWorldPos(_localPosA)); - drawMesh.Add(_objB.LocalToWorldPos(_localPosB)); + List<(Vector2, Color)> drawMesh = new List<(Vector2, Color)>(); + drawMesh.Add((_objA.LocalToWorldPos(_localPosA), Color.Green)); + drawMesh.Add((_objB.LocalToWorldPos(_localPosB), Color.Green)); return drawMesh; } diff --git a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs index c521d1859..0e40538ab 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs @@ -1,3 +1,4 @@ +using ChatGPT.Core.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using System; @@ -112,10 +113,12 @@ public static bool GetPolygonTileCollisionInfo(AABB boundingBox, Vector2 polyCen var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); float depth; Vector2 normal; - if (GeometryUtils.PolygonPolygonCollisionInfo(edges, corners, edgesTile, localPoints, + if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edges, corners, edgesTile, localPoints, out depth, out normal)) { - collided = true; + depth1 = 0; + normal1 = Vector2.Zero; + return true; if (depth > maxDepth) { @@ -137,7 +140,6 @@ public static void GetPolygonTileContactInfo(AABB boundingBox, Vector2 polyCente List outputList) { - Vector2 maxNormal = Vector2.Zero; int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); @@ -156,9 +158,12 @@ public static void GetPolygonTileContactInfo(AABB boundingBox, Vector2 polyCente edgesTile.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); } var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + + var boxCollider = (A.Collider as BoxCollider); float depth; Vector2 normal; - if (GeometryUtils.PolygonPolygonCollisionInfo(edges, corners, edgesTile, localPoints, + if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edges, + corners, edgesTile, localPoints, out depth, out normal)) { var e = new CollisionEvent2D() @@ -178,7 +183,8 @@ public static void GetPolygonTileContactInfo(AABB boundingBox, Vector2 polyCente e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); List> contacts = new List>(); - GeometryUtils.PolygonPolygonContactInfo(edges, corners, edgesTile, localPoints.ToList(), polyCenter, + GeometryUtils.ConvexPolygonPolygonContactInfo(boxCollider.GetEdges(deltaTime), + boxCollider.GetCornerPoints(deltaTime), edgesTile, localPoints.ToList(), polyCenter, centerTile, contacts); foreach (var c in contacts) { @@ -220,12 +226,9 @@ public static bool GetSphereTileCollisionInfo(Vector2 center, float radius, Phys var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); float depth; Vector2 normal; - if (GeometryUtils.CirclePolygonIsCollide(center, radius, edges, - Vector2.Zero)) + if (GeometryUtils.SphereConvexPolygonCollisionInfo(center, radius, edges, localPoints.ToList(), out depth, out normal)) { collided = true; - GeometryUtils.CirclePolygonCollisionInfo(center, radius, edges, localPoints.ToList(), out depth, out normal); - if (depth > maxDepth) { maxDepth = depth; @@ -263,7 +266,7 @@ public static void GetSphereTileContactInfo(Vector2 center, float radius, Physic var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); float depth; Vector2 normal; - if (GeometryUtils.CirclePolygonCollisionInfo(center, radius, edges, localPoints.ToList(), out depth, out normal)) + if (GeometryUtils.SphereConvexPolygonCollisionInfo(A.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), out depth, out normal)) { var e = new CollisionEvent2D() { @@ -282,7 +285,118 @@ public static void GetSphereTileContactInfo(Vector2 center, float radius, Physic e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); List> contacts = new List>(); - GeometryUtils.CirclePolygonContactInfo(e.Source.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), centerTile, contacts); + GeometryUtils.SphereConvexPolygonContactInfo(e.Source.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), centerTile, contacts); + foreach (var c in contacts) + { + outputList.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + } + } + } + } + + + public static bool GetCapsuleTileCollisionInfo(AABB boundingBox, Vector2 segA, Vector2 segB, float radius, + out float depth1, out Vector2 normal1) + { + bool collided = false; + float maxDepth = 0; + Vector2 maxNormal = Vector2.Zero; + int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); + int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(segA, segB, radius, localPoints.ToList(), edges, + out depth, out normal)) + { + collided = true; + + if (depth > maxDepth) + { + maxDepth = depth; + maxNormal = Vector2.Dot(maxNormal, boundingBox.Center - centerTile) < 0 ? -normal : normal; + } + } + } + } + } + depth1 = 0; + normal1 = maxNormal; + return collided; + } + + public static void GetCapsuleTileContactInfo(AABB boundingBox, Vector2 segA, Vector2 segB, float radius, + PhysicsObject A, PhysicsObject B, + float deltaTime, + List outputList) + { + int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); + int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + var capsule = A.Collider as CapsuleCollider; + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(segA, segB, radius, localPoints.ToList(), edges, + out depth, out normal)) + { + var e = new CollisionEvent2D() + { + Time = 0, + Source = A, + Target = B, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(normal, boundingBox.Center - centerTile) < 0 ? -normal : normal, + Position = Vector2.Zero, + Depth = depth, + }; + float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + float weightB = 1 - weightA; + e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); + e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); + + List> contacts = new List>(); + capsule.GetSegment(deltaTime, out Vector2 segAt, out Vector2 segBt); + GeometryUtils.CapsuleConvexPolygonContactInfo(segAt, segBt, radius, localPoints.ToList(), edges, + centerTile, contacts); foreach (var c in contacts) { outputList.Add(new CollisionEvent2D(e) diff --git a/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs b/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs index e2a4747c4..aa1380446 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs @@ -1,6 +1,7 @@ using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Utilities; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -11,6 +12,16 @@ namespace Everglow.Commons.Physics.PBEngine { public static class GeometryUtils { + + public static AABB ToAABBPhysSpace(this Rectangle rect) + { + return new AABB() + { + MinPoint = new Vector2(rect.X, -(rect.Y + rect.Height)), + MaxPoint = new Vector2(rect.X + rect.Width, -rect.Y) + }; + } + public static bool ApproxEqual(float a, float b) { return Math.Abs(a - b) < 1e-6; @@ -115,19 +126,22 @@ public static float PointSignedDistanceToLine(Vector2 p, Vector2 start, Vector2 return -crossProductMagnitude / ABLength; } - public static float PointSignedDistanceToSegment(Vector2 p, Vector2 start, Vector2 end) + public static float PointSignedDistanceToSegmentGetNearest(Vector2 p, Vector2 start, Vector2 end, out Vector2 pointOnSeg) { var u = (end - start).SafeNormalize(Vector2.Zero); float d = Vector2.Dot(u, p - start); float sign = GeometryUtils.Cross(u, p - start) > 0 ? -1 : 1; if (d <= 0) { + pointOnSeg = start; return Vector2.Distance(p, start) * sign; } else if (d >= Vector2.Distance(start, end)) { + pointOnSeg = end; return Vector2.Distance(p, end) * sign; } + pointOnSeg = start + d * u; return (p - (start + d * u)).Length() * sign; } @@ -145,6 +159,25 @@ public static float PointDistance2ToSegment(Vector2 p, Vector2 start, Vector2 en } return (p - (start + d * u)).LengthSquared(); } + + public static float PointDistance2ToSegmentGetNearest(Vector2 p, Vector2 start, Vector2 end, out Vector2 pointOnSeg) + { + var u = (end - start).SafeNormalize(Vector2.Zero); + float d = Vector2.Dot(u, p - start); + if (d <= 0) + { + pointOnSeg = start; + return Vector2.DistanceSquared(p, start); + } + else if (d * d >= Vector2.DistanceSquared(start, end)) + { + pointOnSeg = end; + return Vector2.DistanceSquared(p, end); + } + pointOnSeg = start + d * u; + return (p - (start + d * u)).LengthSquared(); + } + public static float PointDistance2ToSegmentWithClip(Vector2 p, Vector2 start, Vector2 end) { var u = (end - start).SafeNormalize(Vector2.Zero); @@ -160,7 +193,7 @@ public static float PointDistance2ToSegmentWithClip(Vector2 p, Vector2 start, Ve return (p - (start + d * u)).LengthSquared(); } - public static void CirclePolygonContactInfo(Vector2 cSphere, float radius, List edges, + public static void SphereConvexPolygonContactInfo(Vector2 cSphere, float radius, List edges, List corners, Vector2 centerPoly, List> localPositions) { @@ -207,60 +240,59 @@ public static void CirclePolygonContactInfo(Vector2 cSphere, float radius, List< } } - public static bool CirclePolygonCollisionInfo(Vector2 cSphere, float radius, List edges, List corners, + public static bool SphereConvexPolygonCollisionInfo(Vector2 cSphere, float radius, List edges, List corners, out float depth, out Vector2 normal) { float maxDepth = float.NegativeInfinity; - float maxDepth2 = float.NegativeInfinity; - Edge2D keyEdge = null; + normal = Vector2.Zero; + bool anyOutside = false; foreach (var edge in edges) { - float d = GeometryUtils.PointSignedDistanceToLine(cSphere, edge._pA, edge._pB); - float d2 = GeometryUtils.PointSignedDistanceToSegment(cSphere, edge._pA, edge._pB); - if (d2 > maxDepth2) - { - maxDepth2 = d2; - } - if (d > maxDepth) + float d = GeometryUtils.PointDistance2ToSegmentGetNearest(cSphere, edge._pA, edge._pB, out Vector2 pointOnSeg); + // ijһ߶ε棬ô˵ڶε + if (d > maxDepth && GeometryUtils.Cross(edge._pB - edge._pA, cSphere - edge._pA) <= 0) { maxDepth = d; - keyEdge = edge; + normal = (cSphere - pointOnSeg).SafeNormalize(Vector2.Zero); } + + //if (GeometryUtils.Cross(cSphere - edge._pA, edge._pB - edge._pA) <= 0) + //{ + // anyOutside = true; + //} + //if (d > maxDepth) + //{ + // maxDepth = d; + // keyEdge = edge; + //} } - - normal = keyEdge.GetNormal(); - depth = radius - maxDepth; - if (maxDepth2 > radius) + if (maxDepth < 0) { + depth = 0; return false; } - - foreach (var corner in corners) + maxDepth = (float)Math.Sqrt(maxDepth); + depth = radius - maxDepth; + if (maxDepth >= radius) { - float d = Vector2.Distance(corner, cSphere); - if (d <= radius && d > maxDepth) - { - maxDepth = d; - normal = Vector2.Normalize(cSphere - corner); - depth = radius - maxDepth; - } + return false; } - return maxDepth < radius; + return true; } - public static bool CirclePolygonIsCollide(Vector2 cSphere, float radius, List edges, Vector2 centerPoly) - { - float maxDepth = float.NegativeInfinity; - foreach (var edge in edges) - { - float d = GeometryUtils.PointSignedDistanceToSegment(cSphere, edge._pA, edge._pB); - if (d > maxDepth) - { - maxDepth = d; - } - } - return maxDepth < radius; - } + //public static bool SphereConvexPolygonIsCollide(Vector2 cSphere, float radius, List edges, Vector2 centerPoly) + //{ + // float maxDepth = float.NegativeInfinity; + // foreach (var edge in edges) + // { + // float d = GeometryUtils.PointSignedDistanceToSegment(cSphere, edge._pA, edge._pB); + // if (d > maxDepth) + // { + // maxDepth = d; + // } + // } + // return maxDepth < radius; + //} private static (float, float) GetProjectedInterval(List vertices, Vector2 start, Vector2 dir) { @@ -275,7 +307,7 @@ private static (float, float) GetProjectedInterval(List vertices, Vecto return (minA, maxA); } - public static bool PolygonPolygonCollisionInfo(List edgesA, List cornersA, + public static bool ConvexPolygonPolygonCollisionInfo(List edgesA, List cornersA, List edgesB, List cornersB, out float depth, out Vector2 normal) { depth = float.PositiveInfinity; @@ -293,7 +325,7 @@ public static bool PolygonPolygonCollisionInfo(List edgesA, List edgesA, List edgesA, List edgesA, List cornersA, + public static void ConvexPolygonPolygonContactInfo(List edgesA, List cornersA, List edgesB, List cornersB, Vector2 polyCenterA, Vector2 polyCenterB, List> localPositions) { @@ -343,7 +375,7 @@ public static void PolygonPolygonContactInfo(List edgesA, List - polyCenterB )); } - else if (Math.Abs(dist - closestDistance) < 1e-6) + else if (Math.Abs(dist - closestDistance) < 1e-3) { localPositions.Add(new KeyValuePair( curPoint - polyCenterA, @@ -378,14 +410,315 @@ public static void PolygonPolygonContactInfo(List edgesA, List } } + public static bool SphereCapsuleCollisionInfo(Vector2 center, float radius, Vector2 segA, Vector2 segB, + float radius2, out float depth, out Vector2 normal) + { + float d = PointDistance2ToSegmentGetNearest(center, segA, segB, out Vector2 p); + depth = radius + radius2 - (float)Math.Sqrt(d); + normal = Vector2.Zero; + if (d >= (radius + radius2) * (radius + radius2)) + { + return false; + } + normal = (p - center).SafeNormalize(Vector2.Zero); + return true; + } + + public static void SphereCapsuleContactInfo(Vector2 center, float radius, Vector2 segA, Vector2 segB, + float radius2, List> localPositions) + { + var u = (segB - segA).SafeNormalize(Vector2.Zero); + float d_plane = Vector2.Dot(u, center - segA); + float length = (segA - segB).Length(); + Vector2 capsuleCenter = (segA + segB) / 2; + d_plane = MathHelper.Clamp(d_plane, 0, length); + + Vector2 vcenter = segA + u * d_plane; + Vector2 unit = (center - vcenter).SafeNormalize(Vector2.Zero); + localPositions.Add(new KeyValuePair( + -unit * radius, + vcenter + unit * radius2 - capsuleCenter + )); + } + + public static bool CapsuleCapsuleCollisionInfo(Vector2 segA1, Vector2 segB1, + float radius1, Vector2 segA2, Vector2 segB2, + float radius2, out float depth, out Vector2 normal) + { + Vector2 pointA = segA2; + bool flipSign = false; + float d = PointDistance2ToSegmentGetNearest(segA2, segA1, segB1, out Vector2 pointB); + float d2; + if ((d2 = PointDistance2ToSegmentGetNearest(segB2, segA1, segB1, out Vector2 p)) < d) + { + d = d2; + pointA = segB2; + pointB = p; + } + + if ((d2 = PointDistance2ToSegmentGetNearest(segA1, segA2, segB2, out p)) < d) + { + d = d2; + pointA = segA1; + pointB = p; + flipSign = true; + } + + if ((d2 = PointDistance2ToSegmentGetNearest(segB1, segA2, segB2, out p)) < d) + { + d = d2; + pointA = segB1; + pointB = p; + flipSign = true; + } + + + depth = radius1 + radius2 - (float)Math.Sqrt(d); + normal = (pointA - pointB).SafeNormalize(Vector2.UnitX); + // ߷볯Sphereײ + if (flipSign) + { + normal = -normal; + } + if (depth <= 0) + return false; + return true; + } + + public static void CapsuleCapsuleContactInfo(Vector2 segA1, Vector2 segB1, + float radius1, Vector2 segA2, Vector2 segB2, + float radius2, List> localPositions) + { + Vector2 pointA = segA2; + Vector2 capsuleCenter1 = (segA1 + segB1) / 2; + Vector2 capsuleCenter2 = (segA2 + segB2) / 2; + float d = PointDistance2ToSegmentGetNearest(segA2, segA1, segB1, out Vector2 pointB); + Vector2 N = (pointA - pointB).SafeNormalize(Vector2.UnitX); + localPositions.Add(new KeyValuePair( + pointB + N * radius1 - capsuleCenter1, + pointA - N * radius2 - capsuleCenter2 + )); + float d2; + if ((d2 = PointDistance2ToSegmentGetNearest(segB2, segA1, segB1, out Vector2 p)) < d) + { + d = d2; + pointA = segB2; + pointB = p; + localPositions.Clear(); + N = (pointA - pointB).SafeNormalize(Vector2.UnitX); + localPositions.Add(new KeyValuePair( + pointB + N * radius1 - capsuleCenter1, + pointA - N * radius2 - capsuleCenter2 + )); + } + else if (d2 == d) + { + pointA = segB2; + pointB = p; + N = (pointA - pointB).SafeNormalize(Vector2.UnitX); + localPositions.Add(new KeyValuePair( + pointB + N * radius1 - capsuleCenter1, + pointA - N * radius2 - capsuleCenter2 + )); + } + + // ֹĸȫȡֻܱӴ + if ((d2 = PointDistance2ToSegmentGetNearest(segA1, segA2, segB2, out p)) <= d) + { + d = d2; + pointA = segA1; + pointB = p; + N = (pointA - pointB).SafeNormalize(Vector2.UnitX); + localPositions.Clear(); + localPositions.Add(new KeyValuePair( + pointA - N * radius1 - capsuleCenter1, + pointB + N * radius2 - capsuleCenter2 + )); + } + + if ((d2 = PointDistance2ToSegmentGetNearest(segB1, segA2, segB2, out p)) < d) + { + d = d2; + pointA = segB1; + pointB = p; + N = (pointA - pointB).SafeNormalize(Vector2.UnitX); + localPositions.Clear(); + localPositions.Add(new KeyValuePair( + pointA - N * radius1 - capsuleCenter1, + pointB + N * radius2 - capsuleCenter2 + )); + } + else if (d2 == d) + { + pointA = segB1; + pointB = p; + N = (pointA - pointB).SafeNormalize(Vector2.UnitX); + localPositions.Add(new KeyValuePair( + pointA - N * radius1 - capsuleCenter1, + pointB + N * radius2 - capsuleCenter2 + )); + } + + if (localPositions.Count > 2) + { + // Remove all elements after the first two + localPositions.RemoveRange(2, localPositions.Count - 2); + } + } + + + public static bool CapsuleConvexPolygonCollisionInfo(Vector2 segA1, Vector2 segB1, + float radius1, List corners, List edges, out float depth, out Vector2 normal) + { + depth = float.PositiveInfinity; + normal = Vector2.Zero; + + { + // ߶εķߺ߶αǹؼ + Edge2D edge = new Edge2D(segA1, segB1); + Vector2 axis = edge.GetNormal(); + var (amin, amax) = GetProjectedInterval(new List() { segA1, segB1 }, edge._pA, axis); + var (bmin, bmax) = GetProjectedInterval(corners, edge._pA, axis); + + amin -= radius1; + amax += radius1; + + if (Math.Max(amin, bmin) > Math.Min(amax, bmax)) + { + return false; + } + + float d = MinimumSeparatingDistance(amin, amax, bmin, bmax); + if (d < depth) + { + depth = d; + normal = axis; + } + + axis = (segB1 - segA1).SafeNormalize(Vector2.Zero); + (amin, amax) = GetProjectedInterval(new List() { segA1, segB1 }, edge._pA, axis); + (bmin, bmax) = GetProjectedInterval(corners, edge._pA, axis); + + amin -= radius1; + amax += radius1; + + if (Math.Max(amin, bmin) > Math.Min(amax, bmax)) + { + return false; + } + + d = MinimumSeparatingDistance(amin, amax, bmin, bmax); + if (d < depth) + { + depth = d; + normal = axis; + } + } + + var capsuleCorners = new List() { segA1, segB1 }; + for (int i = 0; i < edges.Count; i++) + { + Edge2D edge = edges[i]; + Vector2 axis = edge.GetNormal(); + + var (amin, amax) = GetProjectedInterval(capsuleCorners, edge._pA, axis); + var (bmin, bmax) = GetProjectedInterval(corners, edge._pA, axis); + + amin -= radius1; + amax += radius1; + + if (Math.Max(amin, bmin) > Math.Min(amax, bmax)) + { + return false; + } + + float d = MinimumSeparatingDistance(amin, amax, bmin, bmax); + if (d < depth) + { + depth = d; + normal = axis; + } + } + return true; + } + + public static void CapsuleConvexPolygonContactInfo(Vector2 segA1, Vector2 segB1, + float radius1, List corners, List edges, Vector2 polyCenter, List> localPositions) + { + float closestDistance = float.PositiveInfinity; + Edge2D edgeSeg = new Edge2D(segA1, segB1); + List capsuleVertices = new List() { segA1, segB1 }; + Vector2 capsuleCenter = (segA1 + segB1) / 2; + Vector2 ultimateDir = (polyCenter - capsuleCenter).SafeNormalize(Vector2.Zero); + foreach (var curPoint in corners) + { + float dist = GeometryUtils.PointDistance2ToSegmentGetNearest(curPoint, edgeSeg._pA, edgeSeg._pB, out Vector2 p); + if (dist < closestDistance) + { + closestDistance = dist; + localPositions.Clear(); + localPositions.Add(new KeyValuePair( + p + (curPoint - p).SafeNormalize(ultimateDir) * radius1 - capsuleCenter, + curPoint - polyCenter + )); + } + else if (Math.Abs(dist - closestDistance) < 1e-6) + { + localPositions.Add(new KeyValuePair( + p + (curPoint - p).SafeNormalize(ultimateDir) * radius1 - capsuleCenter, + curPoint - polyCenter + )); + } + + } + foreach (var capsulePoint in capsuleVertices) + { + foreach (var curEdge in edges) + { + float dist = GeometryUtils.PointDistance2ToSegmentGetNearest(capsulePoint, curEdge._pA, curEdge._pB, out Vector2 p); + if (dist < closestDistance) + { + closestDistance = dist; + localPositions.Clear(); + localPositions.Add(new KeyValuePair( + capsulePoint + (p - capsulePoint).SafeNormalize(ultimateDir) * radius1 - capsuleCenter, + p - polyCenter + )); + } + else if (Math.Abs(dist - closestDistance) < 1e-6) + { + localPositions.Add(new KeyValuePair( + capsulePoint + (p - capsulePoint).SafeNormalize(ultimateDir) * radius1 - capsuleCenter, + p - polyCenter + )); + } + } + } + + if (localPositions.Count > 2) + { + // Remove all elements after the first two + localPositions.RemoveRange(2, localPositions.Count - 2); + } + } + + + public static float MinimumSeparatingDistance(float amin, float amax, float bmin, float bmax) + { + float d = Math.Min(amax, bmax) - Math.Max(amin, bmin); + if ((amin >= bmin && amax <= bmax) || (amin <= bmin && amax >= bmax)) + { + d = Math.Min(amax - bmin, bmax - amin); + } + return d; + } + public static Vector2 ConvertToPhysicsSpace(Vector2 pos) { return new Vector2(pos.X, -pos.Y); } - private static Random rng = new Random(); - - public static void FisherYatesShuffle(this IList list) + public static void FisherYatesShuffle(this IList list, Random rng) { int n = list.Count; while (n > 1) diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs index e81ba2bf2..629db3984 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs @@ -46,8 +46,17 @@ public Vector2 Position public float Rotation { get => _rotation; - set => _rotation = value; + set + { + _rotation = value; + _cachedRotationalMatrix = Matrix2x2.CreateRotationMatrix(_rotation); + } + } + internal Matrix2x2 CachedRotationalMatrix + { + get => _cachedRotationalMatrix; } + private Matrix2x2 _cachedRotationalMatrix; /// /// 物体在迭代开始的时候的位置 @@ -85,6 +94,12 @@ public string Tag set => _tag = value; } + public bool IsActive + { + get => _isActive; + set => _isActive = value; + } + private Collider2D _collider; private RigidBody2D _rigidBody; private Vector2 _oldPos; @@ -92,12 +107,15 @@ public string Tag private float _oldRot; private int _guid; private string _tag; + private bool _isActive; public PhysicsObject(Collider2D collider, RigidBody2D rigidBody) { _collider = collider; _rigidBody = rigidBody; _tag = "Default"; + _isActive = true; + if (_rigidBody == null) { @@ -108,6 +126,9 @@ public PhysicsObject(Collider2D collider, RigidBody2D rigidBody) }; } + Rotation = 0; + Position = Vector2.Zero; + _collider.ParentObject = this; _rigidBody.ParentObject = this; } @@ -123,20 +144,24 @@ public void RecordOldState() _oldRot = _rotation; } - public List GetWireFrameWires() + public List<(Vector2, Color)> GetWireFrameWires() { + List<(Vector2, Color)> wires_color = new List<(Vector2, Color)>(); List wires = _collider.GetWireFrameWires(); - var R = Matrix2x2.CreateRotationMatrix(_rotation); for (int i = 0; i < wires.Count; i++) { - wires[i] = R.Multiply(wires[i]) + Position; + wires_color.Add((_cachedRotationalMatrix.Multiply(wires[i]) + Position, RigidBody.IsAwake ? Color.White : Color.Gray)); } - return wires; + return wires_color; + } + public void CleanThisFrame(float deltaTime) + { + _rigidBody?.CleanInformationThisFrame(deltaTime); } - public void CleanUp() + public void CleanThisSubstep(float deltaTime) { - _rigidBody?.CleanUp(); - _rotation = MathHelper.WrapAngle(_rotation); + _rigidBody?.CleanInformationSubstep(deltaTime); + Rotation = MathHelper.WrapAngle(Rotation); } public void Update(float deltaTime) { @@ -145,6 +170,10 @@ public void Update(float deltaTime) public void ApplyGravity(Vector2 gravity) { + if (!_isActive) + { + return; + } if(_rigidBody != null) { if (_rigidBody.UseGravity) @@ -156,7 +185,7 @@ public void ApplyGravity(Vector2 gravity) public Vector2 LocalToWorldPos(Vector2 localPos) { - return _rigidBody.CentroidWorldSpace + Matrix2x2.CreateRotationMatrix(_rotation).Multiply(localPos); + return _rigidBody.CentroidWorldSpace + _cachedRotationalMatrix.Multiply(localPos); } } } diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs index 1c52c3027..45379b4b2 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs @@ -1,12 +1,15 @@ +using Everglow.Commons.DataStructures; using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.Constrains; +using Everglow.Commons.Utilities; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Terraria; namespace Everglow.Commons.Physics.PBEngine { @@ -17,12 +20,16 @@ namespace Everglow.Commons.Physics.PBEngine public class PhysicsSimulation { public const double EPS = 1e-6; - public const int GAUSS_SEIDEL_ITERS = 8; + public int GAUSS_SEIDEL_ITERS + { + get => 8; + } - private List _dynamicPhysObjects; - private List _staticPhysObjects; + private List _objects; + private LinkedList _objectFreeList; + private int _maximumAllocatedId; private List _constrains; - private BroadPhase _broadPhase; + private IBroadPhase _broadPhase; private float _gravity; private Stopwatch _stopwatchPreIntegration; @@ -30,9 +37,12 @@ public class PhysicsSimulation private Stopwatch _stopwatchNarrowPhase; private readonly double _ticksPerMillisecond; - /// - /// 用于性能检测,对各个阶段的耗时进行统计 - /// + private long _numBroadPhasePairs; + private long _numNarrowPhasePairs; + + /// + /// 用于性能检测,对各个阶段的耗时进行统计 + /// public double MeasuredPreIntegrationTimeInMs { get => _stopwatchPreIntegration.ElapsedTicks / _ticksPerMillisecond; @@ -46,12 +56,45 @@ public double MeasuredNarrowPhaseTimeInMs get => _stopwatchNarrowPhase.ElapsedTicks / _ticksPerMillisecond; } + /// + /// 粗碰撞检测得到的碰撞对数量 + /// + public long NumOfBroadPhasePairs + { + get => _numBroadPhasePairs; + } + + /// + /// 细碰撞检测实际碰撞出现的物体对数量 + /// + public long NumOfNarrowPhasePairs + { + get => _numNarrowPhasePairs; + } + public float Gravity + { + get => _gravity; + } + + public IBroadPhase BroadPhaseCollisionDetector + { + get => _broadPhase; + } + public PhysicsSimulation() + : this(CollisionGraph.DefualtGraph) + { + + } + + public PhysicsSimulation(CollisionGraph graph) { - _dynamicPhysObjects = new List(); - _staticPhysObjects = new List(); + _objectFreeList = new LinkedList(); + _objects = new List(); + _maximumAllocatedId = 0; _constrains = new List(); - _broadPhase = new BruteForceDetect(CollisionGraph.DefualtGraph); + _broadPhase = new HashGridMethod(graph); + _gravity = 9.8f; _stopwatchPreIntegration = new Stopwatch(); @@ -59,12 +102,9 @@ public PhysicsSimulation() _stopwatchNarrowPhase = new Stopwatch(); _ticksPerMillisecond = Stopwatch.Frequency / 1000.0; - } - public PhysicsSimulation(CollisionGraph graph) - : this() - { - _broadPhase = new BruteForceDetect(graph); + _numBroadPhasePairs = 0; + _numNarrowPhasePairs = 0; } /// @@ -73,14 +113,14 @@ public PhysicsSimulation(CollisionGraph graph) /// public void AddPhysicsObject(PhysicsObject pobj) { - pobj.GUID = _dynamicPhysObjects.Count + _staticPhysObjects.Count; - if (pobj.RigidBody.MovementType == MovementType.Static) + pobj.GUID = AllocateGUID(); + if (pobj.GUID == _objects.Count) { - _staticPhysObjects.Add(pobj); + _objects.Add(pobj); } else { - _dynamicPhysObjects.Add(pobj); + _objects[pobj.GUID] = pobj; } pobj.Initialize(); } @@ -99,7 +139,7 @@ public void AddConstrain(Constrain constrain) /// public void Initialize() { - foreach (PhysicsObject pobj in _dynamicPhysObjects) + foreach (PhysicsObject pobj in _objects) { pobj.Initialize(); } @@ -111,15 +151,10 @@ public void Initialize() /// public void Update(float deltaTime) { - CleanThisFrame(); - foreach (PhysicsObject pobj in _dynamicPhysObjects) - { - pobj.ApplyGravity(new Vector2(0, -_gravity)); - } - _stopwatchPreIntegration.Reset(); _stopwatchBroadPhase.Reset(); _stopwatchNarrowPhase.Reset(); + CleanUpThisFrame(deltaTime); float dt = deltaTime / GAUSS_SEIDEL_ITERS; for (int i = 0; i < GAUSS_SEIDEL_ITERS; i++) { @@ -129,23 +164,19 @@ public void Update(float deltaTime) Resolve(dt, _stopwatchBroadPhase, _stopwatchNarrowPhase); } + //foreach (var pobj in _objects) + //{ + // Main.NewText(pobj.RigidBody.LinearVelocity); + //} } - public void CleanThisFrame() - { - CleanUp(); - } - - public List GetCurrentWireFrames() + public List<(Vector2, Color)> GetCurrentWireFrames() { - List result = new List(); - foreach (PhysicsObject pobj in _dynamicPhysObjects) - { - result.AddRange(pobj.GetWireFrameWires()); - } - foreach (PhysicsObject pobj in _staticPhysObjects) + List<(Vector2, Color)> result = new List<(Vector2, Color)>(); + foreach (PhysicsObject pobj in _objects) { + if(!pobj.IsActive) continue; result.AddRange(pobj.GetWireFrameWires()); } foreach (Constrain joint in _constrains) @@ -161,18 +192,31 @@ public List GetCurrentWireFrames() /// private void PreIntegration(float deltaTime) { - foreach (var joint in _constrains) + CleanUpThisSubstep(deltaTime); + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + pobj.ApplyGravity(new Vector2(0, -_gravity)); + } + foreach (var constrain in _constrains) { - joint.Apply(deltaTime); + constrain.ApplyForce(deltaTime); } - foreach (PhysicsObject pobj in _staticPhysObjects) + foreach (PhysicsObject pobj in _objects) { + if (!pobj.IsActive) + { + continue; + } pobj.RecordOldState(); + pobj.Update(deltaTime); } - foreach (PhysicsObject pobj in _dynamicPhysObjects) + foreach (var constrain in _constrains) { - pobj.RecordOldState(); - pobj.Update(deltaTime); + constrain.Apply(deltaTime); } } @@ -185,32 +229,217 @@ private void PreIntegration(float deltaTime) private void Resolve(float deltaTime, Stopwatch broadPhase, Stopwatch narrowPhase) { broadPhase.Start(); - _broadPhase.Prepare(_dynamicPhysObjects.Concat(_staticPhysObjects).ToList(), deltaTime); + List activeObjects = _objects.Where(obj => obj.IsActive).ToList(); + _broadPhase.Prepare(activeObjects, deltaTime); var pairs = _broadPhase.GetCollisionPairs(deltaTime); + _numBroadPhasePairs = pairs.Count; broadPhase.Stop(); narrowPhase.Start(); + _numNarrowPhasePairs = 0; + List contacts = new List(); foreach (var pair in pairs) { - CollisionInfo info; - if (pair.Key.TestCollisionCondition(pair.Value, deltaTime, out info)) + if (pair.Key.TestCollisionCondition(pair.Value, deltaTime, out CollisionInfo info)) { + // 非线性投影:预先移开以获得contact points float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); - //if (weightA != 0 && weightB != 0) - // weightA = weightB = 0.5f; info.Source.RigidBody.MoveBody(info.Normal * weightA * info.Depth, deltaTime); info.Target.RigidBody.MoveBody(-info.Normal * weightB * info.Depth, deltaTime); List events; pair.Key.GetContactInfo(info, deltaTime, out events); - ApplyConstrains(events, pair.Key.ParentObject, deltaTime); + contacts.AddRange(events); + + //// 非线性投影:回溯,计算实际投影后姿态 + //foreach (var e in events) + //{ + // double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(e.LocalOffsetSrc), e.Normal); + // double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(e.LocalOffsetTarget), e.Normal); + // double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; + // double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; + // double effectiveMass = R1 + R2 + e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass; + + // double linearMoveA = info.Depth * e.Source.RigidBody.InvMass / effectiveMass; + // double linearMoveB = -info.Depth * e.Target.RigidBody.InvMass / effectiveMass; + // double angularMoveA = info.Depth * R1 / effectiveMass; + // double angularMoveB = -info.Depth * R2 / effectiveMass; + + // info.Source.RigidBody.MoveBody(info.Normal * (float)linearMoveA, deltaTime); + // info.Target.RigidBody.MoveBody(info.Normal * (float)linearMoveB, deltaTime); + + // double implusePerMoveA = GeometryUtils.Cross(e.LocalOffsetSrc, e.Normal) * e.Source.RigidBody.GlobalInverseInertiaTensor; + // double implusePerMoveB = GeometryUtils.Cross(e.LocalOffsetTarget, e.Normal) * e.Target.RigidBody.GlobalInverseInertiaTensor; + // if (R1 > 0) + // { + // info.Source.Rotation += (float)(angularMoveA * implusePerMoveA / R1); + // } + // if (R2 > 0) + // { + // info.Target.Rotation += (float)(angularMoveB * implusePerMoveB / R2); + // } + //} + + // ApplyConstrains(events, pair.Key.ParentObject, deltaTime); + + _numNarrowPhasePairs++; + } + } + + contacts.Sort((a, b) => + { + return -a.Depth.CompareTo(b.Depth); + }); + + foreach (var contact in contacts) + { + float stiffness = Math.Max(0, (contact.Source.RigidBody.Restitution + contact.Target.RigidBody.Restitution) / 2); + SolveContact_Weak(contact, stiffness,true, deltaTime); + } + for (int i = 0; i < 16; i++) + { + bool stable = true; + foreach (var contact in contacts) + { + if (!SolveContact_Weak(contact, 0, false, deltaTime)) + { + stable = false; + } + } + if (stable) + { + break; + } + } + foreach (var contact in contacts) + { + SolveFriction_Weak(contact, deltaTime); + contact.Source.RigidBody.MatchAwakeState(contact.Target.RigidBody, deltaTime); + } + + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; } + pobj.RigidBody.CheckAwake(deltaTime); } narrowPhase.Stop(); } + private void SolveFriction_Weak(CollisionEvent2D e, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); + var vb = e.Target.RigidBody.LinearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + if (e.NormalVelOld == 0) + { + return; + } + float vrel_n = Vector2.Dot(va - vb, e.Normal); + Vector2 vt = (va - vb) - vrel_n * e.Normal; + + float vel_t = vt.Length(); + float friction = Math.Max(0, (e.Source.RigidBody.Friction + e.Target.RigidBody.Friction) / 2); + + vt = vt.SafeNormalize(Vector2.Zero); + if (vt.Length() == 0) + { + return; + } + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), vt); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), vt); + double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + //* Utils.Cross(rb, e.Normal))), e.Normal); + double effectiveMass = (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass + R1 + R2); + + // (a × b) × c = (c • a)b - (c • b)a + double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); + Vector2 J = (float)J_n * vt;// + (float)J_t * va_t_unit; + + // var offset = e.Position - (_globalCentroid + ri); + e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); + } + + private bool SolveContact_Weak(CollisionEvent2D e, float restitution, bool initial, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); + var vb = e.Target.RigidBody.LinearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + float vrel_n = Vector2.Dot(va - vb, e.Normal); + + if (vrel_n >= 0) + { + return true; + } + + if (initial) + { + if (e.Source.RigidBody.MovementType == MovementType.Player) + { + e.Source.RigidBody.ContactNormals.Add(e.Normal); + } + if (e.Target.RigidBody.MovementType == MovementType.Player) + { + e.Target.RigidBody.ContactNormals.Add(-e.Normal); + } + } + + float vnew_n = restitution * Math.Max(-vrel_n, 0); + + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); + double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; + double J_n = (vnew_n - vrel_n) / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass + R1 + R2); + Vector2 J = (float)J_n * e.Normal;// + (float)J_t * va_t_unit; + + e.NormalVelOld += (float)J_n; + + // var offset = e.Position - (_globalCentroid + ri); + e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); + + return vrel_n > -0.1f; + } + + private Dictionary>> GroupCollisions(List> pairs) + { + Dictionary>> groupedPairs = new Dictionary>>(); + UnionFind uf = new UnionFind(_objects.Count); + foreach (var pair in pairs) + { + uf.Union(pair.Key.ParentObject.GUID, pair.Value.ParentObject.GUID); + } + foreach (var pair in pairs) + { + int group = uf.Find(pair.Key.ParentObject.GUID); + if (groupedPairs.ContainsKey(group)) + { + groupedPairs[group].Add(pair); + } + else + { + groupedPairs.Add(group, new List>() { pair }); + } + } + return groupedPairs; + } + private void ApplyConstrains(List events, PhysicsObject pobj, float deltaTime) { if (deltaTime == 0) @@ -219,12 +448,65 @@ private void ApplyConstrains(List events, PhysicsObject pobj, } pobj.RigidBody.RespondToEvents(events, deltaTime); } - private void CleanUp() + private void CleanUpThisFrame(float deltaTime) + { + foreach (PhysicsObject pobj in _objects) + { + pobj.CleanThisFrame(deltaTime); + } + } + private void CleanUpThisSubstep(float deltaTime) + { + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + pobj.CleanThisSubstep(deltaTime); + } + } + + + public void RemoveObject(PhysicsObject obj) + { + _objectFreeList.AddLast(obj.GUID); + obj.IsActive = false; + } + + private void RemoveObject(int GUID) + { + Debug.Assert(_objects[GUID].IsActive); + RemoveObject(_objects[GUID]); + } + + private List AllocateGUIDs(int size) + { + List results = new List(); + while (size > 0) + { + if (_objectFreeList.Count > 0) + { + results.Add(_objectFreeList.First()); + _objectFreeList.RemoveFirst(); + } + else + { + results.Add(_maximumAllocatedId++); + } + } + return results; + } + + private int AllocateGUID() { - foreach (PhysicsObject pobj in _dynamicPhysObjects) + if (_objectFreeList.Count == 0) { - pobj.CleanUp(); + return _maximumAllocatedId++; } + int id = _objectFreeList.First(); + _objectFreeList.RemoveFirst(); + return id; } } } diff --git a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs index 57929b5c5..69cf3c581 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs @@ -297,6 +297,57 @@ public static PhysicsSimulation SphereAndBox() world.AddPhysicsObject(dynamicBox); } + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation Box2() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 700); + dynamicBox.RigidBody.Restitution = 0.3f; + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(512, 734); + dynamicBox1.RigidBody.Restitution = 0.3f; + world.AddPhysicsObject(dynamicBox1); + + + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation BoxStack() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 5; i++) + { + + for (int j = 1; j <= i + 1; j++) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (j - 2) * 38 - 16 * i, 500 - i * 34); + dynamicBox.RigidBody.Restitution = 0.3f; + world.AddPhysicsObject(dynamicBox); + } + } + + var staticPlane1 = new PhysicsObject( new BoxCollider(600, 32), null); staticPlane1.Position = new Vector2(512, 200); diff --git a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs index 559ce04f7..59f37705f 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs +++ b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs @@ -7,69 +7,70 @@ using System.Threading.Tasks; using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Utilities; using Terraria; namespace Everglow.Commons.Physics.PBEngine { - /// - /// 2D刚体物理组件,用于实际的刚体运动模拟和响应的模块 - /// - public class RigidBody2D + /// + /// 2D刚体物理组件,用于实际的刚体运动模拟和响应的模块 + /// + public class RigidBody2D { - /// - /// 指向物理对象的指针 - /// + /// + /// 指向物理对象的指针 + /// public PhysicsObject ParentObject { get => _bindObject; set => _bindObject = value; } - /// - /// 世界坐标下的质心坐标(指物理世界 - /// + /// + /// 世界坐标下的质心坐标(指物理世界 + /// public Vector2 CentroidWorldSpace { get => _globalCentroid; set => _globalCentroid = value; } - /// - /// 质量 - /// + /// + /// 质量 + /// public float Mass { get => _mass; set => _mass = value; } - /// - /// 质量的倒数 - /// + /// + /// 质量的倒数 + /// public float InvMass { get => (MovementType == MovementType.Dynamic || MovementType == MovementType.Player) ? 1 / _mass : 0; } - /// - /// 世界坐标下惯性张量的倒数 - /// + /// + /// 世界坐标下惯性张量的倒数 + /// public double GlobalInverseInertiaTensor { get => MovementType == MovementType.Dynamic ? _globalInverseInertiaTensor : 0; } - /// - /// 是否启用重力 - /// + /// + /// 是否启用重力 + /// public bool UseGravity { get; set; } - /// - /// 线速度 - /// + /// + /// 线速度 + /// public Vector2 LinearVelocity { get => MovementType == MovementType.Static ? Vector2.Zero : _linearVelocity; @@ -77,63 +78,98 @@ public Vector2 LinearVelocity } - /// - /// 角速度 - /// + /// + /// 角速度 + /// public float AngularVelocity { get => MovementType == MovementType.Static ? 0 : _angularVelocity; set => _angularVelocity = value; } - /// - /// 移动模式 - /// + /// + /// 移动模式 + /// public MovementType MovementType { - get; set; + get; set; } - /// - /// 线速度的阻尼 - /// + /// + /// 线速度的阻尼 + /// public float Drag { - get => _drag; + get => _drag; set => _drag = value; } - /// - /// 角速度的阻尼 - /// + /// + /// 角速度的阻尼 + /// public float AngularDrag { get => _angularDrag; set => _angularDrag = value; } - /// - /// 碰撞弹出的系数 - /// - public float Stiffness + /// + /// 碰撞弹出的系数 + /// + public float Restitution { - get => _stiffness; - set => _stiffness = value; + get => _restitution; + set => _restitution = value; } - /// - /// 摩擦力系数 - /// + /// + /// 摩擦力系数 + /// public float Friction { get => _friction; set => _friction = value; } - public Vector2 TangetRelativeVelocity - { - get => _tangentRelativeVelocity; - } + public List TangetRelativeVelocity + { + get => _contactTangentVels; + } + + public List ContactNormals + { + get => _contactNormals; + } + + /// + /// 物体是否是唤醒状态,如果唤醒那么可以进行运动模拟,注意唤醒时会重置运动值 + /// + public bool IsAwake + { + get => _isAwake; + set + { + if (value && !_isAwake) + { + _motion = SleepEPS * 2; + } + else if (!value && _isAwake) + { + _linearVelocity = Vector2.Zero; + _angularVelocity = 0; + } + _isAwake = value; + } + } + + /// + /// 物体是否可以进入沉睡状态,用户能控制的物体应该设为false + /// + public bool CanSleep + { + get => _canSleep; + set => _canSleep = value; + } private double _localInverseInertiaTensor; private double _globalInverseInertiaTensor; @@ -148,33 +184,41 @@ public Vector2 TangetRelativeVelocity private float _mass; private float _drag; private float _angularDrag; - private float _stiffness; + private float _restitution; private float _friction; - private Vector2 _tangentRelativeVelocity; + private List _contactTangentVels; + private List _contactNormals; private List _impluses; private bool _isAwake; + private bool _canSleep; + private double _motion; private PhysicsObject _bindObject; + private const double SleepEPS = 1e-2; + public RigidBody2D(float mass) { _mass = mass; MovementType = MovementType.Dynamic; UseGravity = true; _isAwake = true; + _canSleep = true; _linearVelocity = Vector2.Zero; _angularVelocity = 0; _drag = 0.06f; _angularDrag = 0.06f; _impluses = new List(); - _stiffness = 0.5f; + _restitution = 0.5f; _friction = 0.5f; - _tangentRelativeVelocity = Vector2.Zero; + _contactTangentVels = new List(); + _contactNormals = new List(); - } + _motion = SleepEPS * 2; + } private void CalculateMassCentroidAndMoI() @@ -187,12 +231,6 @@ public void Initialize() CalculateMassCentroidAndMoI(); } - private void UpdateGlobalInertiaTensor() - { - // var R = Matrix2x2.CreateRotationMatrix(_rotation); - // _globalInertiaTensor = R.Multiply(_localInverseInertiaTensor).Multiply(R.Transpose()); - _globalInverseInertiaTensor = _localInverseInertiaTensor; - } /// /// 进行一次运动积分 @@ -200,7 +238,7 @@ private void UpdateGlobalInertiaTensor() /// public void Update(float deltaTime) { - if (MovementType == MovementType.Static) + if (!_isAwake || MovementType == MovementType.Static) { return; } @@ -226,10 +264,13 @@ public void Update(float deltaTime) private void StabilizeBody() { - if (Math.Abs(_linearVelocity.X) < PhysicsSimulation.EPS - && Math.Abs(_linearVelocity.Y) < PhysicsSimulation.EPS) + if (Math.Abs(_linearVelocity.X) < PhysicsSimulation.EPS) + { + _linearVelocity.X = 0; + } + if (Math.Abs(_linearVelocity.Y) < PhysicsSimulation.EPS) { - _linearVelocity = Vector2.Zero; + _linearVelocity.Y = 0; } if(Math.Abs(_angularVelocity) < PhysicsSimulation.EPS) @@ -240,71 +281,92 @@ private void StabilizeBody() public void ApplyForce(Vector2 force) { + if (MovementType == MovementType.Static) + { + return; + } _force += force; } - public void CleanUp() + public void CleanInformationSubstep(float deltaTime) { _torque = 0; _force = Vector2.Zero; _impluses.Clear(); // Main.NewText((_linearVelocity.LengthSquared() * _mass * 0.5f + _mass * 9.8f * _globalCentroid.Y).ToString("F1")); + } + public void CleanInformationThisFrame(float deltaTime) + { _linearVelocity *= 1 - _drag * _drag; _angularVelocity *= 1 - _angularDrag * _angularDrag; - _tangentRelativeVelocity = Vector2.Zero; - } + _contactTangentVels.Clear(); + _contactNormals.Clear(); + } - //public void RespondToEvents(List events, float deltaTime) - //{ - // var ri0 = events[0].LocalOffsetSrc; - // var rb0 = events[0].LocalOffsetTarget; - // var n0 = events[0].Normal; - // var va0 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); - // var vb0 = events[0].Target.RigidBody._linearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); - // float relv0_n = Vector2.Dot(va0 - vb0, n0); - - // var ri1 = events[1].LocalOffsetSrc; - // var rb1 = events[1].LocalOffsetTarget; - // var n1 = events[1].Normal; - // var va1 = _linearVelocity + Utils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); - // var vb1 = events[1].Target.RigidBody._linearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); - // float relv1_n = Vector2.Dot(va1 - vb1, n1); - // Matrix2x2 matrix = new Matrix2x2() - // { - // [0, 0] = InvMass + events[0].Target.RigidBody.InvMass - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri0, n0))), n0) - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor - // * Utils.Cross(rb0, n0))), n0), - // [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri1, n1))), n0), - - // [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri0, n0))), n1), - // [1, 1] = InvMass + events[1].Target.RigidBody.InvMass - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri1, n1))), n1) - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor - // * Utils.Cross(rb1, n1))), n1), - // }; - // float C = 0.4f; - // var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); - // if (!J.HasNaNs()) - // { - // AddImpluse(J.X * events[0].Normal, ri0, events[0].Time, events[0].Normal); - // AddImpluse(J.Y * events[1].Normal, ri1, events[1].Time, events[1].Normal); - // } - // var t = matrix.Multiply(J); - // if (true) - // ; - //} - public void ApplyAngularVelocity(float w) + public bool TryRespondTo2Events(List events, float deltaTime) + { + Debug.Assert(events.Count == 2); + Debug.Assert(events[0].Target == events[1].Target); + var target = events[0].Target; + + var ri0 = events[0].LocalOffsetSrc; + var rb0 = events[0].LocalOffsetTarget; + var n0 = events[0].Normal; + var va0 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); + var vb0 = events[0].Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); + float relv0_n = Vector2.Dot(va0 - vb0, n0); + if (relv0_n >= 0) + { + return false; + } + + var ri1 = events[1].LocalOffsetSrc; + var rb1 = events[1].LocalOffsetTarget; + var n1 = events[1].Normal; + var va1 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); + var vb1 = events[1].Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); + float relv1_n = Vector2.Dot(va1 - vb1, n1); + if (relv1_n >= 0) + { + return false; + } + Matrix2x2 matrix = new Matrix2x2() + { + [0, 0] = InvMass + events[0].Target.RigidBody.InvMass + + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri0, n0))), n0) + + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor + * GeometryUtils.Cross(rb0, n0))), n0), + [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri1, n1))), n0), + + [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri0, n0))), n1), + [1, 1] = InvMass + events[1].Target.RigidBody.InvMass + + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri1, n1))), n1) + + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor + * GeometryUtils.Cross(rb1, n1))), n1), + }; + float C = 0.4f; + var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); + if (!J.HasNaNs()) + { + AddImpluse(J.X * events[0].Normal, ri0, target.RigidBody, - J.X * events[0].Normal, rb0); + AddImpluse(J.Y * events[1].Normal, ri1, target.RigidBody, - J.Y * events[1].Normal, rb1); + } + return true; + } + public void ApplyAngularVelocity(float w) { _angularVelocity += w; } + + + private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) { var ri = e.LocalOffsetSrc; @@ -318,12 +380,20 @@ private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) float va_n = Vector2.Dot(va - vb, e.Normal); + if (MovementType == MovementType.Player) + { + _contactNormals.Add(e.Normal); + } + if(e.Target.RigidBody.MovementType == MovementType.Player) + { + e.Target.RigidBody._contactNormals.Add(-e.Normal); + } if (va_n >= 0) { e.NormalVelOld = 0; return; } - float stiffness = Math.Max(0, (_stiffness + e.Target.RigidBody.Stiffness) / 2); + float stiffness = Math.Max(0, (_restitution + e.Target.RigidBody.Restitution) / 2); float vnew_n = stiffness * Math.Max(-va_n - 20 * deltaTime, 0); // (a × b) × c = (c • a)b - (c • b)a @@ -339,6 +409,7 @@ private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) // var offset = e.Position - (_globalCentroid + ri); AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); } private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime) @@ -358,24 +429,38 @@ private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime float vel_n = Vector2.Dot(va - vb, e.Normal); Vector2 vt = (va - vb) - vel_n * e.Normal; - _tangentRelativeVelocity -= vb / count; - e.Target.RigidBody._tangentRelativeVelocity -= va / count; + float vel_t = vt.Length(); float friction = Math.Max(0, (_friction + e.Target.RigidBody.Friction) / 2); + vt = vt.SafeNormalize(Vector2.Zero); + if (vt.Length() == 0) + { + return; + } - double effectiveMass = (InvMass + e.Target.RigidBody.InvMass - + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri, vt))), vt) - + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor - * GeometryUtils.Cross(rb, vt))), vt)); + _contactTangentVels.Add(-Vector2.Dot(vt, vb) * vt); + e.Target.RigidBody._contactTangentVels.Add(-Vector2.Dot(vt, va) * vt); + if (friction == 0) + { + return; + } + + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), vt); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), vt); + double R1 = rAdotN * rAdotN * GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + //* Utils.Cross(rb, e.Normal))), e.Normal); + double effectiveMass = (InvMass + e.Target.RigidBody.InvMass + R1 + R2); // (a × b) × c = (c • a)b - (c • b)a double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); Vector2 J = (float)J_n * vt / count;// + (float)J_t * va_t_unit; AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); } /// @@ -393,6 +478,7 @@ public void RespondToEvents(List events, float deltaTime) foreach (var e in events) { SolveFrictionImpluse(e, events.Count, deltaTime); + MatchAwakeState(e.Target.RigidBody, deltaTime); } ApplyImpluses(); } @@ -575,6 +661,9 @@ public void ApplyImpluses() } } + + Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); + Debug.Assert(!float.IsNaN(_angularVelocity)); StabilizeBody(); _impluses.Clear(); } @@ -584,13 +673,23 @@ public void ApplyImpluses() /// /// /// - public void AddImpluseImmediate(Vector2 J, Vector2 relativePos) + public void AddImpluseImmediate(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) { - _linearVelocity += 1.0f / _mass * J; - Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); - _angularVelocity += (float)(GeometryUtils.Cross(relativePos, - J * (float)_globalInverseInertiaTensor)); - Debug.Assert(!float.IsNaN(_angularVelocity)); + if (MovementType != MovementType.Static && MovementType != MovementType.Kinematic) + { + _linearVelocity += 1.0f / _mass * J; + Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); + _angularVelocity += (float)(GeometryUtils.Cross(relativePos, + J * (float)GlobalInverseInertiaTensor)); + Debug.Assert(!float.IsNaN(_angularVelocity)); + } + + if (other.MovementType != MovementType.Static && MovementType != MovementType.Kinematic) + { + other._linearVelocity += other.InvMass * J2; + other._angularVelocity += (float)(GeometryUtils.Cross(relativePos2, + J2 * (float)other.GlobalInverseInertiaTensor)); + } } public void MoveBody(Vector2 dir, float deltaTime) @@ -598,5 +697,44 @@ public void MoveBody(Vector2 dir, float deltaTime) _globalCentroid += dir; // _linearVelocity = (_globalCentroid - ParentObject.OldPosition) / deltaTime; } + + public void MatchAwakeState(RigidBody2D other, float deltaTime) + { + if (other.MovementType == MovementType.Static) + { + return; + } + if (!_isAwake && !other._isAwake) + { + return; + } + if (_isAwake && !other._isAwake) + { + other.IsAwake = true; + } + else if (!_isAwake && other._isAwake) + { + IsAwake = true; + } + } + + public void CheckAwake(float deltaTime) + { + if (MovementType == MovementType.Static || MovementType == MovementType.Kinematic) + { + return; + } + double bias = Math.Pow(0.6, deltaTime); + _motion = bias * _motion + (1 - bias) * (_linearVelocity.LengthSquared() + + 3 * _angularVelocity * _angularVelocity); + + if (_motion > 16 * SleepEPS) + _motion = 16 * SleepEPS; + + if (_motion < SleepEPS) + { + IsAwake = false; + } + } } } diff --git a/Sources/Modules/Example/PhysicsDemo.cs b/Sources/Modules/Example/PhysicsDemo.cs index 51b78d573..54c1d7fbb 100644 --- a/Sources/Modules/Example/PhysicsDemo.cs +++ b/Sources/Modules/Example/PhysicsDemo.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Vertex; using ReLogic.Content; using Terraria.GameContent; using Microsoft.Xna.Framework.Input; @@ -13,46 +12,166 @@ using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Constrains; +using Terraria; +using ChatGPT.Core; +using static Terraria.ModLoader.PlayerDrawLayer; +using System.Diagnostics.Contracts; +using ChatGPT.Core.PBEngine.Collision.Colliders; +using ReLogic.Graphics; +using Everglow.Commons.Vertex; namespace Everglow.Example; internal class PhysicsPlayer : ModPlayer { private PhysicsObject _movingPanel; - private Vector2 _prevPos; + private Vector2 _prevVelocity; + private float _simulationDt = 0.1f; public Vector2 _extraVelocity; + public bool _shouldStand; public override void OnEnterWorld() { } public override void PostUpdate() { + + Main.LocalPlayer.velocity += _extraVelocity; - _extraVelocity = PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity * 0.1f; - _extraVelocity = Vector2.Dot(_extraVelocity, new Vector2(1, 0)) * new Vector2(1, 0); + _extraVelocity = Vector2.Zero; + if (Main.LocalPlayer.velocity.Y == 0 + && PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count > 0) + { + Vector2 tangent = Vector2.Zero; + foreach (var v in PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity) + { + tangent += v; + } + tangent /= PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count; + _extraVelocity = tangent * _simulationDt; + _extraVelocity = Vector2.Dot(_extraVelocity, new Vector2(1, 0)) * new Vector2(1, 0); + } } public override void PreUpdate() { - + _shouldStand = false; + _prevVelocity = Main.LocalPlayer.velocity; base.PreUpdate(); } public override void PreUpdateMovement() { - Main.LocalPlayer.velocity -= _extraVelocity; - _prevPos = Main.LocalPlayer.Center; + + //// Main.LocalPlayer.Center = Physics.Utils.ConvertToPhysicsSpace(Display.Instance._dummyPlayer.Position); + //Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity) * dt; + // + //var t = PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity; + //Main.NewText(Main.LocalPlayer.velocity, Color.GreenYellow); + + Player.velocity -= _extraVelocity; + float dt = 0.1f; var oldPos = Main.LocalPlayer.Center; var preVelY = Main.LocalPlayer.velocity.Y; + var preVel = Main.LocalPlayer.velocity; + + var broadPhase = PhysicsDemo.Instance._realSimulation.BroadPhaseCollisionDetector; + + + List temporaryObjects = new List(); + + foreach (var proj in Main.projectile) + { + if (proj.active && proj.tileCollide) + { + + if (broadPhase.TestSingleCollision(proj.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() + { + "Default" + })) + { + var obj = new PhysicsObject(new BoxCollider(proj.width, proj.height), new RigidBody2D(64)); + obj.Position = GeometryUtils.ConvertToPhysicsSpace(proj.Center); + obj.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(proj.velocity / dt); + obj.RigidBody.MovementType = MovementType.Player; + obj.Tag = "Player"; + obj.RigidBody.Drag = 0; + temporaryObjects.Add(obj); + } + } + } + + foreach (var npc in Main.npc) + { + if (npc.active && !npc.noTileCollide) + { + + if (broadPhase.TestSingleCollision(npc.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() + { + "Default" + })) + { + var obj = new PhysicsObject(new BoxCollider(npc.width, npc.height), new RigidBody2D(512)); + obj.Position = GeometryUtils.ConvertToPhysicsSpace(npc.Center); + obj.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(npc.velocity / dt); + obj.RigidBody.MovementType = MovementType.Player; + obj.RigidBody.Drag = 0; + obj.Tag = "Player"; + temporaryObjects.Add(obj); + } + } + } + + foreach (var obj in temporaryObjects) + { + PhysicsDemo.Instance._realSimulation.AddPhysicsObject(obj); + } + + // Main.NewText(Main.LocalPlayer.velocity, Color.Lime); PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.velocity / dt); - PhysicsDemo.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(_prevPos); - PhysicsDemo.Instance._realSimulation.Update(dt); + PhysicsDemo.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(oldPos); + if (!PhysicsDemo.EnableRigidBodyTestPlayground) + { + PhysicsDemo.Instance._realSimulation.Update(dt); + } - // Main.LocalPlayer.Center = Physics.Utils.ConvertToPhysicsSpace(Display.Instance._dummyPlayer.Position); - Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity * dt); - if (preVelY > 0 && Math.Abs(Main.LocalPlayer.velocity.Y) < 0.04f) + foreach (var obj in temporaryObjects) { - Main.LocalPlayer.velocity.Y = 0; + PhysicsDemo.Instance._realSimulation.RemoveObject(obj); } + + + //Main.LocalPlayer.Center = GeometryUtils.ConvertToPhysicsSpace(PhysicsDemo.Instance._dummyPlayer.RigidBody.CentroidWorldSpace) - player.; + // Main.NewText(Main.LocalPlayer.velocity,Color.Cyan); + + // Find any valid, standable slope + if (PhysicsDemo.Instance._dummyPlayer.RigidBody.ContactNormals.Count > 0) + { + foreach (var v in PhysicsDemo.Instance._dummyPlayer.RigidBody.ContactNormals) + { + if (v.Y > 0.9f) + { + _shouldStand = true; + // Omit tangent velocity due to gravity + float mg = -PhysicsDemo.Instance._realSimulation.Gravity; + Vector2 velDueG = new Vector2(0, -Player.gravDir * Player.gravity / dt) * dt; + Vector2 normalDir = Vector2.Dot(velDueG, v) * v; + Vector2 tanDir = velDueG - normalDir; + Player.velocity.Y = 0; + break; + } + } + } + + //if (_shouldStand) + //{ + // Main.LocalPlayer.velocity = Main.LocalPlayer.velocity - _prevVelocity; + + //} + + // if (t.Length() > 0 && Math.Abs(GeometryUtils.Cross(new Vector2(1, 0), t)) < 0.3f) + //{ + // Main.LocalPlayer.velocity.Y = 0; + //} // Main.LocalPlayer.velocity -= _extraVelocity; if (_movingPanel != null) @@ -60,11 +179,11 @@ public override void PreUpdateMovement() if (Main.time % 1200 < 600) { - _movingPanel.RigidBody.LinearVelocity = new Vector2(-3, 0); + _movingPanel.RigidBody.LinearVelocity = new Vector2(-4, 0); } else if (Main.time % 1200 >= 600) { - _movingPanel.RigidBody.LinearVelocity = new Vector2(3, 0); + _movingPanel.RigidBody.LinearVelocity = new Vector2(4, 0); } } @@ -78,30 +197,45 @@ public override void UpdateEquips() public override void SetControls() { base.SetControls(); - if(Main.mouseLeft && Main.mouseLeftRelease) + if (Main.mouseLeft && Main.mouseLeftRelease) { - var ball = new PhysicsObject(new SphereCollider(32), - new RigidBody2D(256)); - PhysicsDemo.Instance._realSimulation.AddPhysicsObject(ball); - ball.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); - ball.RigidBody.LinearVelocity = new Vector2(0, 0); + for (int i = 0; i < 2; i++) + { + var ball = new PhysicsObject(new BoxCollider(32, 32), + new RigidBody2D(256)); + PhysicsDemo.Instance._realSimulation.AddPhysicsObject(ball); + ball.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld) + new Vector2(i, 0); + ball.RigidBody.LinearVelocity = new Vector2(0, 0); + } } if (Main.keyState[Keys.R] == KeyState.Down && Main.oldKeyState[Keys.R] == KeyState.Up) { - // RigidBodyDisplay.Instance.ReInitPhysWorld(); + if (PhysicsDemo.EnableRigidBodyTestPlayground) + { + RigidBodyDisplay.Instance.ReInitPhysWorld(); + } PhysicsDemo.Instance.ReStart(); } if (Main.keyState[Keys.T] == KeyState.Down && Main.oldKeyState[Keys.T] == KeyState.Up) { var dynamicBox = new PhysicsObject( - new BoxCollider(256, 64), new RigidBody2D(1000)); - dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center - new Vector2(0, 200)); + new BoxCollider(128, 32), new RigidBody2D(256)); + //_movingPanel = dynamicBox; + + dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -200)); dynamicBox.Rotation = 0.0f; - dynamicBox.RigidBody.MovementType = MovementType.Kinematic; - dynamicBox.RigidBody.UseGravity = false; + dynamicBox.RigidBody.MovementType = MovementType.Dynamic; + dynamicBox.RigidBody.UseGravity = true; + dynamicBox.RigidBody.Drag = 0.1f; PhysicsDemo.Instance._realSimulation.AddPhysicsObject(dynamicBox); - _movingPanel = dynamicBox; + + + var staticPlane = new PhysicsObject( + new BoxCollider(256, 128), null); + staticPlane.Rotation = 0.3f; + staticPlane.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(200, -200)); + PhysicsDemo.Instance._realSimulation.AddPhysicsObject(staticPlane); //var ball = new PhysicsObject(new BoxCollider(32, 32), // new RigidBody2D(256)); @@ -109,16 +243,23 @@ public override void SetControls() //ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -300)); var staticPlane1 = new PhysicsObject( - new BoxCollider(300, 32), null); - staticPlane1.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center - new Vector2(0, 400)); + new BoxCollider(128, 32), null); + staticPlane1.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(-200, -200)); staticPlane1.Rotation = 0.0f; + staticPlane1.RigidBody.MovementType = MovementType.Static; + staticPlane1.RigidBody.UseGravity = false; PhysicsDemo.Instance._realSimulation.AddPhysicsObject(staticPlane1); + // _movingPanel = staticPlane1; - //var joint = new SpringConstrain(staticPlane1, dynamicBox, 40f, 200f, new Vector2(100, 0), new Vector2(100, 0)); - //PhysicsDemo.Instance._realSimulation.AddConstrain(joint); + var joint = new SpringConstrain(staticPlane1, dynamicBox, 100f, 144f, new Vector2(64, -16), new Vector2(64, 16)); + PhysicsDemo.Instance._realSimulation.AddConstrain(joint); + var joint11 = new SpringConstrain(staticPlane1, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(-64, 16)); + PhysicsDemo.Instance._realSimulation.AddConstrain(joint11); - //var joint1 = new SpringConstrain(staticPlane1, dynamicBox, 100f, 200f, new Vector2(-100, 0), new Vector2(-100, 0)); - //PhysicsDemo.Instance._realSimulation.AddConstrain(joint1); + //var joint2 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, 16), new Vector2(64, -16)); + //PhysicsDemo.Instance._realSimulation.AddConstrain(joint2); + //var joint22 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(64, 16)); + //PhysicsDemo.Instance._realSimulation.AddConstrain(joint22); //if (Main.rand.NextBool(2)) //{ @@ -151,58 +292,202 @@ internal class PhysicsDemo : ModSystem public static PhysicsDemo Instance; public PhysicsObject _dummyPlayer; + public const bool EnableRigidBodyTestPlayground = true; + public const float Simulation_DeltaTime = 0.1f; + public override void PostUpdateEverything() { - //rigidBodyDisplay.Update(); + if (EnableRigidBodyTestPlayground) + { + rigidBodyDisplay.Update(); + } } public override void PostDrawTiles() { - //rigidBodyDisplay.Draw(Main.spriteBatch); + if (EnableRigidBodyTestPlayground) + { + rigidBodyDisplay.Draw(Main.spriteBatch); + } List vertices = new List(); - foreach (var p in _realSimulation.GetCurrentWireFrames()) + foreach (var (p, c) in _realSimulation.GetCurrentWireFrames()) { - vertices.Add(new Vertex2D(GeometryUtils.ConvertToPhysicsSpace(p) - Main.screenPosition, Color.White, Vector3.Zero)); + vertices.Add(new Vertex2D(GeometryUtils.ConvertToPhysicsSpace(p) - Main.screenPosition, c, Vector3.Zero)); } if (vertices.Count > 0) { - _renderShader.Value.Parameters["uColor"].SetValue(Color.White.ToVector4()); + // _renderShader.Value.Parameters["uColor"].SetValue(Color.White.ToVector4()); _renderShader.Value.Parameters["uTransform"].SetValue(Matrix.CreateOrthographicOffCenter(0, Main.screenWidth, Main.screenHeight, 0, 0, 1)); _renderShader.Value.CurrentTechnique.Passes[0].Apply(); Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); } + + _realSimulation.BroadPhaseCollisionDetector.DrawDebugInfo(Main.spriteBatch); base.PostDrawTiles(); } public override void Load() { - //rigidBodyDisplay = new RigidBodyDisplay(); - //rigidBodyDisplay.Initialize(); + if (EnableRigidBodyTestPlayground) + { + rigidBodyDisplay = new RigidBodyDisplay(); + rigidBodyDisplay.Initialize(); + } Instance = this; ReStart(); - _renderShader = ModContent.Request("Everglow/Example/VFX/PureColor"); + _renderShader = ModContent.Request("Everglow/Example/VFX/Default"); On_Player.PlayerFrame += On_Player_PlayerFrame; + On_Collision.TileCollision += On_Collision_TileCollision; base.Load(); } + + private Vector2 On_Collision_TileCollision(On_Collision.orig_TileCollision orig, Vector2 Position, Vector2 Velocity, int Width, int Height, bool fallThrough, bool fall2, int gravDir) + { + Position = GeometryUtils.ConvertToPhysicsSpace(Position); + Velocity = GeometryUtils.ConvertToPhysicsSpace(Velocity); + var rect = new AABB(Position - new Vector2(0, Height), Position + new Vector2(Width, 0)); + var oldCenter = rect.Center; + + rect = rect.Move(Velocity); + + var collisions = _realSimulation.BroadPhaseCollisionDetector.GetSingleCollision(rect, Vector2.Zero, 0, new List() + { + "Default" + }); + + if (collisions.Count > 0) + { + + var dummyCollider = new BoxCollider(Width, Height); + var dummyPObject = new PhysicsObject(dummyCollider, new RigidBody2D(64)); + dummyPObject.OldPosition = rect.Center; + dummyPObject.RigidBody.CentroidWorldSpace = rect.Center; + + foreach (var collider in collisions) + { + CollisionInfo info; + if (dummyCollider.TestCollisionCondition(collider, Simulation_DeltaTime, out info)) + { + float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + //if (weightA != 0 && weightB != 0) + // weightA = weightB = 0.5f; + + //Vector2 impluseVel = Vector2.Zero; + // List events; + // dummyCollider.GetContactInfo(info, Simulation_DeltaTime, out events); + //foreach(var e in events) + //{ + // impluseVel += SolveContactImpluse(e, result + velChange, events.Count, Simulation_DeltaTime); + // } + // dummyPObject.RigidBody.MoveBody(info.Normal * weightA * info.Depth, Simulation_DeltaTime); + if (Math.Abs(info.Normal.Y) < 0.77f) + { + dummyPObject.RigidBody.MoveBody(info.Normal * weightA * info.Depth, Simulation_DeltaTime); + } + else + { + dummyPObject.RigidBody.MoveBody(info.Depth / info.Normal.Y * new Vector2(0, 1), Simulation_DeltaTime); + } + //if (float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.X) || float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.Y)) + //{ + // if (true) + // ; + //} + // extraVel = collider.ParentObject.RigidBody.LinearVelocity * Simulation_DeltaTime; + //var n = Vector2.Dot(result, info.Normal) * info.Normal; + //result = result - n; + // velChange += impluseVel; + } + } + + // Main.NewText(result + dummyCollider.ParentObject.Position - oldCenter, Color.Red); + // var newDir = result + new Vector2(0, d); + Velocity = (dummyCollider.ParentObject.Position - oldCenter); + } + + Vector2 result = orig(GeometryUtils.ConvertToPhysicsSpace(Position), GeometryUtils.ConvertToPhysicsSpace(Velocity), Width, Height, fallThrough, fall2, gravDir); + return result; + } + + /// + /// 返回接触响应后的速度的变化量 + /// + /// + /// + /// + /// + private Vector2 SolveContactImpluse(CollisionEvent2D e, Vector2 sourceVel, int count, float deltaTime) + { + float mass = 64; + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var va = sourceVel; + var vb = e.Target.RigidBody.LinearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + float va_n = Vector2.Dot(va - vb, e.Normal); + if (va_n >= 0) + { + e.NormalVelOld = 0; + return Vector2.Zero; + } + float stiffness = 0; + float vnew_n = stiffness * Math.Max(-va_n - 20 * deltaTime, 0); + + // (a × b) × c = (c • a)b - (c • b)a + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); + double R1 = 0; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + //* Utils.Cross(rb, e.Normal))), e.Normal); + double J_n = (vnew_n - va_n) / (1.0 / mass + e.Target.RigidBody.InvMass + R1 + R2); + Vector2 J = (float)J_n * e.Normal / count;// + (float)J_t * va_t_unit; + + return J / mass; + } + private void On_Player_PlayerFrame(On_Player.orig_PlayerFrame orig, Player self) { + Main.LocalPlayer.velocity += self.GetModPlayer()._extraVelocity; + + if (self.GetModPlayer()._shouldStand) + { + Main.LocalPlayer.velocity.Y = 0; + + } orig(self); Main.LocalPlayer.velocity -= self.GetModPlayer()._extraVelocity; + } + public override void PostDrawInterface(SpriteBatch sb) + { + var start = Main.LocalPlayer.Center - new Vector2(1024 / 2, 1024 / 2) - Main.screenPosition; + sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Red); + sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_realSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Red); + sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_realSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Red); + sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_realSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Red); + + double percent = _realSimulation.NumOfBroadPhasePairs == 0 ? 1 : (double)_realSimulation.NumOfNarrowPhasePairs / _realSimulation.NumOfBroadPhasePairs; + sb.DrawString(FontAssets.MouseText.Value, $"Detected Pairs: {_realSimulation.NumOfNarrowPhasePairs} / {_realSimulation.NumOfBroadPhasePairs}, {percent:F2}% of accuracy", start + new Vector2(0, 108), Color.Red); + } public void ReStart() { var terrariaCollisionGroup = new CollisionGraph(); _realSimulation = new PhysicsSimulation(terrariaCollisionGroup); + terrariaCollisionGroup.AddSingleEdge("Default", "Default"); terrariaCollisionGroup.AddSingleEdge("Default", "Terrain"); terrariaCollisionGroup.AddDoubleEdge("Player", "Default"); @@ -218,8 +503,8 @@ public void ReStart() rigidb.UseGravity = false; rigidb.AngularDrag = 0; rigidb.Drag = 0; - rigidb.Stiffness = -1; - rigidb.Friction = 0; + rigidb.Restitution = -1; + rigidb.Friction = -1; _realSimulation.AddPhysicsObject(_dummyPlayer); _realSimulation.Initialize(); } diff --git a/Sources/Modules/Example/RigidBodyDisplay.cs b/Sources/Modules/Example/RigidBodyDisplay.cs index fc86db526..1ca44a269 100644 --- a/Sources/Modules/Example/RigidBodyDisplay.cs +++ b/Sources/Modules/Example/RigidBodyDisplay.cs @@ -1,3 +1,4 @@ +using ChatGPT.Core; using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.PlayGround.Constrain; @@ -58,9 +59,9 @@ private void RenderPhysWorld() { List vertices = new List(); - foreach (var p in _physicsSimulation.GetCurrentWireFrames()) + foreach (var (p, c) in _physicsSimulation.GetCurrentWireFrames()) { - vertices.Add(new Vertex2D(p, Color.White, Vector3.Zero)); + vertices.Add(new Vertex2D(p, c, Vector3.Zero)); } Main.graphics.GraphicsDevice.SetRenderTarget(_canvasTarget); @@ -73,8 +74,7 @@ private void RenderPhysWorld() public void ReInitPhysWorld() { - _physicsSimulation = JointPlayground.SpringTriganluar(); - + _physicsSimulation = ContactPlayGround1.DoubleSlope(); //var staticPlane3 = new PhysicsObject( // new AABBCollider(1, 40), null); //staticPlane3.Position = new Vector2(200, 300); @@ -91,7 +91,7 @@ public void Initialize() { _canvasTarget = new RenderTarget2D(Main.graphics.GraphicsDevice, 1024, 1024); }); - _renderShader = ModContent.Request("ChatGPT/Core/Physics/Shaders/Default"); + _renderShader = ModContent.Request("Everglow/Example/VFX/Default"); } } } From 0ad36c1bba06fa86dfff450b57274c2ffc5b9dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=83=E7=B4=A0=C2=B7=E4=B8=87=E8=B1=A1?= <877183629@qq.com> Date: Fri, 17 Oct 2025 23:19:09 +0800 Subject: [PATCH 07/13] Change the path of PBEngine. --- .../Collision/BroadPhase/CollisionGraph.cs | 63 -- .../Collision/Colliders/BoxCollider.cs | 516 ------------ .../Collision/Colliders/CapsuleCollider.cs | 267 ------- .../Collision/Colliders/Collider2D.cs | 37 - .../Collision/Colliders/SphereCollider.cs | 244 ------ .../PBEngine/GameInteraction/TileCollider.cs | 55 -- .../GameInteraction/TileCollisionUtils.cs | 414 ---------- .../Physics/PBEngine/PhysicsObject.cs | 191 ----- .../Physics/PBEngine/PhysicsSimulation.cs | 512 ------------ .../PlayGround/Constrain/JointPlayground.cs | 229 ------ .../PlayGround/Contact/ContactPlayGround1.cs | 359 --------- .../Physics/PBEngine/RigidBody2D.cs | 740 ------------------ .../Physics/PBEngine/Collision/AABB.cs | 0 .../Collision/BroadPhase/BVHDetect.cs | 0 .../Collision/BroadPhase/BruteForceDetect.cs | 0 .../Collision/BroadPhase/CollisionGraph.cs | 60 ++ .../Collision/BroadPhase/HashGridMethod.cs | 0 .../Collision/BroadPhase/IBroadPhase.cs | 0 .../Collision/BroadPhase/Structure/BVH.cs | 0 .../Structure/MultiLevelHashGrid.cs | 0 .../Collision/BroadPhase/SweepAndPrune.cs | 0 .../Collision/Colliders/BoxCollider.cs | 505 ++++++++++++ .../Collision/Colliders/CapsuleCollider.cs | 261 ++++++ .../Collision/Colliders/Collider2D.cs | 33 + .../Collision/Colliders/SphereCollider.cs | 241 ++++++ .../PBEngine/Collision/CollisionEvent2D.cs | 0 .../PBEngine/Collision/CollisionInfo.cs | 0 .../Physics/PBEngine/Collision/Edge2D.cs | 0 .../PBEngine/Collision/ImpluseEntry.cs | 0 .../PBEngine/Collision/MovementType.cs | 25 +- .../Physics/PBEngine/Constrains/Constrain.cs | 0 .../PBEngine/Constrains/JointConstrain.cs | 0 .../PBEngine/Constrains/SpringConstrain.cs | 0 .../Physics/PBEngine/CustomPhysicsObject.cs | 40 + .../PBEngine/GameInteraction/TileCollider.cs | 48 ++ .../GameInteraction/TileCollisionUtils.cs | 409 ++++++++++ .../Physics/PBEngine/GeometryUtils.cs | 8 +- .../Physics/PBEngine/PhysicsObject.cs | 200 +++++ .../Physics/PBEngine/PhysicsSimulation.cs | 548 +++++++++++++ .../PlayGround/Constrain/JointPlayground.cs | 221 ++++++ .../PlayGround/Contact/ContactPlayGround1.cs | 341 ++++++++ .../PlayGround/Contact/ContactPlayGround2.cs | 0 .../Physics/PBEngine/RigidBody2D.cs | 736 +++++++++++++++++ ...25\346\223\216\346\226\207\346\241\243.md" | 0 .../Physics/PhysicWorldSystem.cs} | 251 +++--- .../Physics/RigidBodyRenderer.cs | 85 ++ Sources/Modules/Example/RigidBodyDisplay.cs | 97 --- .../Example/Test/PhysicsBody_Square.cs | 41 + .../Example/Test/PhysicsBody_Square.png | Bin 0 -> 375 bytes .../Example/Test/PhysicsBody_TestSystem.cs | 22 + .../Example/Test/PhysicsBody_TestSystem.png | Bin 0 -> 407 bytes .../Modules/Example/Test/Square_Physics.cs | 35 + .../Modules/Example/Test/Square_Physics.png | Bin 0 -> 366 bytes 53 files changed, 3954 insertions(+), 3880 deletions(-) delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs delete mode 100644 Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/AABB.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs (100%) create mode 100644 Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs (100%) create mode 100644 Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/CollisionEvent2D.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/CollisionInfo.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/Edge2D.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/ImpluseEntry.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Collision/MovementType.cs (71%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Constrains/Constrain.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Constrains/JointConstrain.cs (100%) rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/Constrains/SpringConstrain.cs (100%) create mode 100644 Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollider.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/GeometryUtils.cs (98%) create mode 100644 Sources/Everglow.Function/Physics/PBEngine/PhysicsObject.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs create mode 100644 Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs rename Sources/{Everglow.Core => Everglow.Function}/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs (100%) create mode 100644 Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs rename "Sources/Everglow.Core/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" => "Sources/Everglow.Function/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" (100%) rename Sources/{Modules/Example/PhysicsDemo.cs => Everglow.Function/Physics/PhysicWorldSystem.cs} (67%) create mode 100644 Sources/Everglow.Function/Physics/RigidBodyRenderer.cs delete mode 100644 Sources/Modules/Example/RigidBodyDisplay.cs create mode 100644 Sources/Modules/Example/Test/PhysicsBody_Square.cs create mode 100644 Sources/Modules/Example/Test/PhysicsBody_Square.png create mode 100644 Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs create mode 100644 Sources/Modules/Example/Test/PhysicsBody_TestSystem.png create mode 100644 Sources/Modules/Example/Test/Square_Physics.cs create mode 100644 Sources/Modules/Example/Test/Square_Physics.png diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs deleted file mode 100644 index 757313d91..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase -{ - /// - /// 存储碰撞组的碰撞关系的图结构 - /// - public class CollisionGraph - { - public static CollisionGraph DefualtGraph - = new CollisionGraph(new Dictionary>() { { "Default", new List() { "Default"} } }); - public Dictionary> Graph - { - get; - set; - } - - public CollisionGraph() - { - Graph = new Dictionary>(); - } - - public CollisionGraph(Dictionary> graph) - { - Graph = graph; - } - - public void AddDoubleEdge(string groupA, string groupB) - { - if (groupA == groupB) - return; - // Ensure only compare once - if (String.CompareOrdinal(groupA, groupB) < 0) - { - AddSingleEdge(groupA, groupB); - } - else - { - AddSingleEdge(groupB, groupA); - } - } - - public void AddSingleEdge(string groupA, string groupB) - { - if (Graph.ContainsKey(groupA)) - { - Graph[groupA].Add(groupB); - } - else - { - Graph.Add(groupA, new List() - { - groupB - }); - } - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs deleted file mode 100644 index db22fb884..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/BoxCollider.cs +++ /dev/null @@ -1,516 +0,0 @@ -using ChatGPT.Core.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.GameInteraction; -using Everglow.Commons.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization.Formatters; -using System.Text; -using System.Threading.Tasks; -using Terraria; -using static Terraria.ModLoader.PlayerDrawLayer; - -namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders -{ - /// - /// 四边形、盒子碰撞体 - /// - public class BoxCollider : Collider2D - { - public Vector2 Size - { - get => new Vector2(_width, _height); - } - private float _width; - private float _height; - public BoxCollider(float width, float height) - { - _width = width; - _height = height; - } - - public override double InertiaTensor(float mass) - { - return mass / 12.0 * (_width * _width + _height * _height); - } - - public override List GetWireFrameWires() - { - List wires = new List - { - new Vector2(-_width / 2, -_height / 2), - new Vector2(_width / 2, -_height / 2), - new Vector2(_width / 2, -_height / 2), - new Vector2(_width / 2, _height / 2), - new Vector2(_width / 2, _height / 2), - new Vector2(-_width / 2, _height / 2), - new Vector2(-_width / 2, _height / 2), - new Vector2(-_width / 2, -_height / 2) - }; - return wires; - } - - public List GetEdges(float dt) - { - List cornerWorldSpace = GetCornerPoints(dt); - List edges = new List(); - for (int i = 0; i < 4; i++) - { - edges.Add(new Edge2D(cornerWorldSpace[i], cornerWorldSpace[(i + 1) % cornerWorldSpace.Count])); - } - return edges; - - } - - //public override List GetCollisionEvents(Collider2D other, float deltaTime) - //{ - // List events = new List(); - - // Vector2[] localPoints = new Vector2[4] - // { - // new Vector2(-_width / 2, -_height / 2), - // new Vector2(_width / 2, -_height / 2), - // new Vector2(_width / 2, _height / 2), - // new Vector2(-_width / 2, _height / 2) - // }; - // if (other is AABBCollider) - // { - // AABBCollider aabb_other = (AABBCollider)other; - // for (int i = 0; i < 4; i++) - // { - // var curPos = _bindObject.Position + Matrix2x2.CreateRotationMatrix(_bindObject.Rotation).Multiply(localPoints[i]); - // var oldPos = _bindObject.OldPosition + Matrix2x2.CreateRotationMatrix(_bindObject.OldRotation).Multiply(localPoints[i]); - - // foreach (var edge in aabb_other.GetEdges()) - // { - // CollisionEvent2D collision; - // if (edge.Segment_Segment_Collision(oldPos, curPos, out collision)) - // { - // collision.Source = this.ParentObject; - // collision.Target = other.ParentObject; - // collision.LocalOffsetSrc = Matrix2x2.CreateRotationMatrix(_bindObject.Rotation).Multiply(localPoints[i]); - - // events.Add(collision); - // } - // } - // } - // } - // if (events.Count > 1) - // { - // Vector2 averagedLocalPoint = new Vector2(0, 0); - // Vector2 averagedNormal = new Vector2(0, 0); - // float minTime = 1.0f; - // for (int i = 0; i < events.Count; i++) - // { - // averagedLocalPoint += events[i].LocalOffsetSrc; - // averagedNormal += events[i].Normal; - // minTime = Math.Min(minTime, events[i].Time); - // } - // averagedLocalPoint /= events.Count; - // averagedNormal /= events.Count; - // CollisionEvent2D collision = new CollisionEvent2D(); - // collision.Source = this.ParentObject; - // collision.Target = other.ParentObject; - // collision.LocalOffsetSrc = averagedLocalPoint; - // collision.Normal = Vector2.Normalize(averagedNormal); - // collision.Time = minTime; - - // events.Clear(); - // events.Add(collision); - // } - // return events; - //} - - public List GetCornerPoints(float dt) - { - List cornerPoints = new List(); - Vector2[] localPoints = new Vector2[4] - { - new Vector2(-_width / 2, -_height / 2), - new Vector2(_width / 2, -_height / 2), - new Vector2(_width / 2, _height / 2), - new Vector2(-_width / 2, _height / 2) - }; - - var M = ParentObject.CachedRotationalMatrix; - for (int i = 0; i < 4; i++) - { - var curPos = ParentObject.Position + M.Multiply(localPoints[i]); - cornerPoints.Add(curPos); - } - return cornerPoints; - } - - public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) - { - info = new CollisionInfo(); - if (other is BoxCollider) - { - BoxCollider b = (BoxCollider)other; - List edgesA = GetEdges(deltaTime); - List edgesB = b.GetEdges(deltaTime); - List cornersA = GetCornerPoints(deltaTime); - List cornersB = b.GetCornerPoints(deltaTime); - float depth; - Vector2 normal; - if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edgesA, cornersA, edgesB, cornersB, out depth, out normal)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; - return true; - } - } - else if (other is SphereCollider) - { - SphereCollider b = (SphereCollider)other; - float depth; - Vector2 normal; - bool collided = GeometryUtils.SphereConvexPolygonCollisionInfo(b.ParentObject.Position, b.Radius, GetEdges(deltaTime), - GetCornerPoints(deltaTime), - out depth, out normal); - if (collided) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; - } - return collided; - } - else if (other is TileCollider) - { - float depth; - Vector2 normal; - if (TileCollisionUtils.GetPolygonTileCollisionInfo(GetAABB(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, - GetEdges(deltaTime), GetCornerPoints(deltaTime), out depth, out normal)) - { - info.Source = this.ParentObject; - info.Target = other.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = normal; - return true; - } - } - else if(other is CapsuleCollider) - { - CapsuleCollider b = (CapsuleCollider)other; - b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); - if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(A, B, b.Radius, GetCornerPoints(deltaTime), - GetEdges(deltaTime), - out float depth, out Vector2 normal)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; - return true; - } - } - return false; - } - - public override void GetContactInfo(in CollisionInfo info, float deltaTime, - out List collisionEvents) - { - collisionEvents = new List(); - if (info.Target.Collider is BoxCollider) - { - BoxCollider b = (BoxCollider)info.Target.Collider; - List edgesA = GetEdges(deltaTime); - List edgesB = b.GetEdges(deltaTime); - List cornersA = GetCornerPoints(deltaTime); - List cornersB = b.GetCornerPoints(deltaTime); - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = info.Source, - Target = info.Target, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - List> contacts = new List>(); - GeometryUtils.ConvexPolygonPolygonContactInfo(edgesA, cornersA, edgesB, cornersB, - ParentObject.RigidBody.CentroidWorldSpace, b.ParentObject.RigidBody.CentroidWorldSpace, - contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - else if (info.Target.Collider is SphereCollider) - { - SphereCollider b = (SphereCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = 0, - Source = info.Source, - Target = info.Target, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - - List> contacts = new List>(); - GeometryUtils.SphereConvexPolygonContactInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, GetEdges(deltaTime), - GetCornerPoints(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Value, - LocalOffsetTarget = c.Key, - }); - } - - } - else if (info.Target.Collider is CapsuleCollider) - { - CapsuleCollider b = (CapsuleCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = this.ParentObject, - Target = b.ParentObject, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); - List> contacts = new List>(); - GeometryUtils.CapsuleConvexPolygonContactInfo(A, B, b.Radius, - GetCornerPoints(deltaTime), GetEdges(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Value, - LocalOffsetTarget = c.Key - }); - } - } - else if (info.Target.Collider is TileCollider) - { - TileCollisionUtils.GetPolygonTileContactInfo(GetAABB(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, - GetEdges(deltaTime), GetCornerPoints(deltaTime), ParentObject, info.Target, deltaTime, collisionEvents); - } - } - - public override AABB GetAABB(float deltaTime) - { - var points = GetCornerPoints(deltaTime); - Vector2 minPoint = points[0]; - Vector2 maxPoint = points[0]; - for (int i = 1; i < points.Count; i++) - { - minPoint.X = Math.Min(points[i].X, minPoint.X); - minPoint.Y = Math.Min(points[i].Y, minPoint.Y); - maxPoint.X = Math.Max(points[i].X, maxPoint.X); - maxPoint.Y = Math.Max(points[i].Y, maxPoint.Y); - } - return new AABB() { MaxPoint = maxPoint, MinPoint = minPoint }; - } - } -} - - -//float maxDepth = -1; -//if (edgeBelongsToA) -//{ -// for (int i = 0; i < cornersB.Count; i++) -// { -// if (IsInsideEdge(cornersB[i], keyEdge)) -// { -// float d = Utils.PointDistance2ToSegment(cornersB[i], keyEdge._pA, keyEdge._pB); -// if (d > maxDepth) -// { -// maxDepth = d; -// } -// } -// } -// Debug.Assert(maxDepth >= 0); -// for (int i = 0; i < cornersB.Count; i++) -// { -// if (IsInsideEdge(cornersB[i], keyEdge)) -// { -// float d = Utils.PointDistance2ToSegment(cornersB[i], keyEdge._pA, keyEdge._pB); -// if (d == maxDepth) -// { -// collisionEvents.Add(new CollisionEvent2D() -// { -// Time = 0, -// Source = this.ParentObject, -// Target = b.ParentObject, -// LocalOffsetSrc = Utils.ProjectPointOnSegment(cornersB[i], keyEdge._pA, keyEdge._pB) -// - ParentObject.RigidBody.CentroidWorldSpace, -// LocalOffsetTarget = cornersB[i] - b.ParentObject.RigidBody.CentroidWorldSpace, -// Normal = normal, -// Position = Vector2.Zero, -// Depth = depth -// }); -// } -// } -// } - -//} -//else -//{ -// for (int i = 0; i < cornersA.Count; i++) -// { -// if (IsInsideEdge(cornersA[i], keyEdge)) -// { -// float d = Utils.PointDistance2ToSegment(cornersA[i], keyEdge._pA, keyEdge._pB); -// if (d > maxDepth) -// { -// maxDepth = d; -// } -// } -// } -// Debug.Assert(maxDepth >= 0); -// for (int i = 0; i < cornersA.Count; i++) -// { -// if (IsInsideEdge(cornersA[i], keyEdge)) -// { -// float d = Utils.PointDistance2ToSegment(cornersA[i], keyEdge._pA, keyEdge._pB); -// if (d == maxDepth) -// { -// collisionEvents.Add(new CollisionEvent2D() -// { -// Time = 0, -// Source = b.ParentObject, -// Target = this.ParentObject, -// LocalOffsetSrc = Utils.ProjectPointOnSegment(cornersA[i], keyEdge._pA, keyEdge._pB) -// - b.ParentObject.RigidBody.CentroidWorldSpace, -// LocalOffsetTarget = cornersA[i] - ParentObject.RigidBody.CentroidWorldSpace, -// Normal = -normal, -// Position = Vector2.Zero, -// Depth = depth -// }); -// } -// } -// } -//} -//collisionEvents.Add(new CollisionEvent2D() -//{ -// Time = 0, -// Source = this.ParentObject, -// Target = b.ParentObject, -// LocalOffsetSrc = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), -// LocalOffsetTarget = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), -// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), -// Position = curPoint, -//}); - -//AABBCollider b = (AABBCollider)other; -//double l = 0, r = deltaTime; -//double ans = r; -//while (r - l > deltaTime * 1e-2) -//{ -// double midTime = (r + l) / 2.0; -// if (IsCollideWith(b, (float)midTime)) -// { -// ans = Math.Min(ans, midTime); -// r = midTime; -// } -// else -// { -// l = midTime; -// } -//} - -//double t = Math.Max(0, ans); -//var curPointsA = GetCornerPoints((float)ans); -//var curEdgesA = GetEdges((float)ans); -//var curPointsB = b.GetCornerPoints((float)ans); -//var curEdgesB = b.GetEdges((float)ans); - -//// Vertex/Edge collision, A to B -//foreach (var curPoint in curPointsA) -//{ -// bool isInside = true; -// Edge2D closestEdge = null; -// float closestDistance = float.PositiveInfinity; -// foreach(var curEdge in curEdgesB) -// { -// if (!IsInsideEdge(curPoint, curEdge)) -// { -// isInside = false; -// break; -// } -// float dist = Utils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); -// if (dist < closestDistance) -// { -// closestDistance = dist; -// closestEdge = curEdge; -// } -// } - -// if(isInside) -// { -// collisionEvents.Add(new CollisionEvent2D() -// { -// Time = (float)(t), -// Source = this.ParentObject, -// Target = b.ParentObject, -// LocalOffsetSrc = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), -// LocalOffsetTarget = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), -// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), -// Position = curPoint, - -// }); -// } - -//} - -//// Vertex/Edge collision, B to A -//foreach (var curPoint in curPointsB) -//{ -// bool isInside = true; -// Edge2D closestEdge = null; -// float closestDistance = float.PositiveInfinity; -// foreach (var curEdge in curEdgesA) -// { -// if (!IsInsideEdge(curPoint, curEdge)) -// { -// isInside = false; -// break; -// } -// float dist = Utils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); -// if (dist < closestDistance) -// { -// closestDistance = dist; -// closestEdge = curEdge; -// } -// } - -// if (isInside) -// { -// collisionEvents.Add(new CollisionEvent2D() -// { -// Time = (float)(t), -// Source = b.ParentObject, -// Target = this.ParentObject, -// LocalOffsetSrc = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), -// LocalOffsetTarget = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), -// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), -// Position = curPoint, -// }); -// } - -//} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs deleted file mode 100644 index d9f641060..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs +++ /dev/null @@ -1,267 +0,0 @@ -using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.GameInteraction; -using Everglow.Commons.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Terraria; -using static Humanizer.In; - -namespace ChatGPT.Core.PBEngine.Collision.Colliders -{ - internal class CapsuleCollider : Collider2D - { - private float _length; - private float _radius; - - public float Length - { - get => _length; - set => _length = value; - } - public float Radius - { - get => _radius; - set => _radius = value; - } - public CapsuleCollider(float length, float radius) - { - _length = length; - _radius = radius; - } - - public override AABB GetAABB(float deltaTime) - { - var M = Matrix2x2.CreateRotationMatrix(ParentObject.OldRotation + ParentObject.RigidBody.AngularVelocity * deltaTime); - Vector2 P1 = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime - + M.Multiply(new Vector2(_length / 2, 0)); - Vector2 P2 = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime - + M.Multiply(new Vector2(-_length / 2, 0)); - - if(P1.X > P2.X) - { - (P2.X, P1.X) = (P1.X, P2.X); - } - if (P1.Y > P2.Y) - { - (P2.Y, P1.Y) = (P1.Y, P2.Y); - } - return new AABB() - { - MinPoint = P1 - new Vector2(_radius, _radius), - MaxPoint = P2 + new Vector2(_radius, _radius), - }; - } - - public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) - { - collisionEvents = new List(); - if (info.Target.Collider is SphereCollider) - { - SphereCollider b = (SphereCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = this.ParentObject, - Target = b.ParentObject, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - List> contacts = new List>(); - GeometryUtils.SphereCapsuleContactInfo(b.ParentObject.RigidBody.CentroidWorldSpace, - b.Radius, A, B, _radius, contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Value, - LocalOffsetTarget = c.Key, - }); - } - } - else if (info.Target.Collider is CapsuleCollider) - { - CapsuleCollider b = (CapsuleCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = this.ParentObject, - Target = b.ParentObject, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - b.GetSegment(deltaTime, out Vector2 A2, out Vector2 B2); - List> contacts = new List>(); - GeometryUtils.CapsuleCapsuleContactInfo(A, B, _radius, A2, B2, b._radius, contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - else if (info.Target.Collider is BoxCollider) - { - BoxCollider b = (BoxCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = this.ParentObject, - Target = b.ParentObject, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - List> contacts = new List>(); - GeometryUtils.CapsuleConvexPolygonContactInfo(A, B, _radius, - b.GetCornerPoints(deltaTime), b.GetEdges(deltaTime), b.ParentObject.RigidBody.CentroidWorldSpace, contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - else if (info.Target.Collider is TileCollider) - { - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - TileCollisionUtils.GetCapsuleTileContactInfo(GetAABB(deltaTime), A, B,_radius, ParentObject, info.Target, deltaTime, collisionEvents); - } - } - - public override List GetWireFrameWires() - { - List lines = new List(); - lines.Add(new Vector2(_length / 2, _radius)); - lines.Add(new Vector2(-_length / 2, _radius)); - - float da = MathHelper.Pi / 32; - var leftCenter = new Vector2(-_length / 2, 0); - for (int i = 0; i < 32; i++) - { - lines.Add(leftCenter + (i * da + MathHelper.PiOver2).ToRotationVector2() * _radius); - lines.Add(leftCenter + ((i + 1) * da + MathHelper.PiOver2).ToRotationVector2() * _radius); - } - lines.Add(new Vector2(-_length / 2, -_radius)); - lines.Add(new Vector2(_length / 2, -_radius)); - var rightCenter = new Vector2(_length / 2, 0); - for (int i = 0; i < 32; i++) - { - lines.Add(rightCenter + (i * da - MathHelper.PiOver2).ToRotationVector2() * _radius); - lines.Add(rightCenter + ((i + 1) * da - MathHelper.PiOver2).ToRotationVector2() * _radius); - } - lines.Add(Vector2.Zero); - lines.Add(new Vector2(_length / 2 + _radius, 0)); - return lines; - } - - public override double InertiaTensor(float mass) - { - float massOfBox = mass * 2 * _radius * _length / (2 * _radius * _length + MathHelper.Pi * _radius * _radius); - float massOfSphere = mass - massOfBox; - float moi_box = massOfBox / 12.0f * (_length * _length + 4 * _radius * _radius); - float moi_sphere = 0.5f * massOfSphere * _radius * _radius; - float d1 = 3.0f / 8.0f * _radius; - float moi_hsphere = 0.5f * moi_sphere - 0.5f * massOfSphere * d1 * d1; - float d2 = _length / 2 + d1; - moi_hsphere += 0.5f * massOfSphere * d2 * d2; - return moi_box + 2 * moi_hsphere; - } - - public void GetSegment(float dt, out Vector2 segA, out Vector2 segB) - { - var M = ParentObject.CachedRotationalMatrix; - var pos = ParentObject.Position; - segA = pos + M.Multiply(new Vector2(_length / 2, 0)); - segB = pos + M.Multiply(new Vector2(-_length / 2, 0)); - } - - public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) - { - info = new CollisionInfo(); - if (other is SphereCollider) - { - SphereCollider b = (SphereCollider)other; - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - if (GeometryUtils.SphereCapsuleCollisionInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, - A, B, _radius, out float depth, out Vector2 normal)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = -normal; - return true; - } - } - else if (other is CapsuleCollider) - { - CapsuleCollider b = (CapsuleCollider)other; - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - b.GetSegment(deltaTime, out Vector2 A2, out Vector2 B2); - if (GeometryUtils.CapsuleCapsuleCollisionInfo(A, B, _radius, A2, B2, b._radius, - out float depth, out Vector2 normal)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; - return true; - } - } - else if (other is BoxCollider) - { - BoxCollider b = (BoxCollider)other; - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(A, B, _radius, b.GetCornerPoints(deltaTime), - b.GetEdges(deltaTime), - out float depth, out Vector2 normal)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; - return true; - } - } - else if (other is TileCollider) - { - float depth; - Vector2 normal; - GetSegment(deltaTime, out Vector2 A, out Vector2 B); - if (TileCollisionUtils.GetCapsuleTileCollisionInfo(GetAABB(deltaTime), A, B, _radius, out depth, out normal)) - { - info.Source = this.ParentObject; - info.Target = other.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = normal; - return true; - } - } - - return false; - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs deleted file mode 100644 index c3c3381de..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/Collider2D.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders -{ - /// - /// 碰撞组件的基类 - /// - public abstract class Collider2D - { - public PhysicsObject ParentObject - { - get => _bindObject; - set => _bindObject = value; - } - - protected PhysicsObject _bindObject; - public abstract double InertiaTensor(float mass); - public abstract List GetWireFrameWires(); - // public abstract List GetCollisionEvents(Collider2D other, float deltaTime); - - public abstract bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info); - - public abstract void GetContactInfo(in CollisionInfo info, float deltaTime, - out List collisionEvents); - - //public abstract List GetContactInfo(CollisionEvent2D e, float deltaTime); - - public abstract AABB GetAABB(float deltaTime); - public Collider2D() - { - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs deleted file mode 100644 index aaa78fb2d..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/Colliders/SphereCollider.cs +++ /dev/null @@ -1,244 +0,0 @@ -using ChatGPT.Core.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.GameInteraction; -using Everglow.Commons.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Terraria; - -namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders -{ - /// - /// 圆形碰撞体 - /// - public class SphereCollider : Collider2D - { - public float Radius - { - get => _radius; set => _radius = value; - } - private float _radius; - public SphereCollider(float radius) - { - _radius = radius; - } - public override AABB GetAABB(float deltaTime) - { - var center = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime; - return new AABB() { - MinPoint = center - new Vector2(_radius, _radius), - MaxPoint = center + new Vector2(_radius, _radius), - }; - } - - public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) - { - collisionEvents = new List(); - if (info.Target.Collider is SphereCollider) - { - SphereCollider b = (SphereCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = this.ParentObject, - Target = b.ParentObject, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - - var unit = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.Zero); - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = -unit * _radius, - LocalOffsetTarget = unit * b.Radius, - }); - } - else if (info.Target.Collider is BoxCollider) - { - BoxCollider b = (BoxCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = info.Source, - Target = info.Target, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = Vector2.Dot(info.Normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -info.Normal : info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - - List> contacts = new List>(); - GeometryUtils.SphereConvexPolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, b.GetEdges(deltaTime), - b.GetCornerPoints(deltaTime), b.ParentObject.RigidBody.CentroidWorldSpace, contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - else if (info.Target.Collider is TileCollider) - { - TileCollisionUtils.GetSphereTileContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, - ParentObject, info.Target, deltaTime, collisionEvents); - } - else if (info.Target.Collider is CapsuleCollider) - { - CapsuleCollider b = (CapsuleCollider)info.Target.Collider; - var e = new CollisionEvent2D() - { - Time = info.Time, - Source = this.ParentObject, - Target = b.ParentObject, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = info.Normal, - Position = Vector2.Zero, - Depth = info.Depth, - }; - b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); - List> contacts = new List>(); - GeometryUtils.SphereCapsuleContactInfo(ParentObject.RigidBody.CentroidWorldSpace, - _radius, A, B, b.Radius, contacts); - foreach (var c in contacts) - { - collisionEvents.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - } - - //public override List GetContactInfo(CollisionEvent2D e, float deltaTime) - //{ - // List events = new List(); - // if (e.Target.Collider is SphereCollider) - // { - // SphereCollider b = (SphereCollider)e.Target.Collider; - // var unit = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.Zero); - // events.Add(new CollisionEvent2D(e) - // { - // LocalOffsetSrc = -unit * _radius, - // LocalOffsetTarget = unit * b.Radius, - // }); - // } - // else if (e.Target.Collider is BoxCollider) - // { - // BoxCollider b = (BoxCollider)e.Target.Collider; - // List edgesB = b.GetEdges(deltaTime); - // List cornersB = b.GetCornerPoints(deltaTime); - // List> contacts = new List>(); - // Utils.CirclePolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, edgesB, - // cornersB, b.ParentObject.RigidBody.CentroidWorldSpace, contacts); - // foreach (var c in contacts) - // { - // events.Add(new CollisionEvent2D(e) - // { - // LocalOffsetSrc = c.Key, - // LocalOffsetTarget = c.Value, - // }); - // } - // } - // else if (e.Target.Collider is TileCollider) - // { - // TileCollisionUtils.GetSphereTileContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, e, events); - // } - // return events; - //} - - public override List GetWireFrameWires() - { - List lines = new List(); - float da = MathHelper.TwoPi / 64; - for (int i = 0; i < 64; i++) - { - lines.Add((i * da).ToRotationVector2() * _radius); - lines.Add(((i + 1) * da).ToRotationVector2() * _radius); - } - lines.Add(Vector2.Zero); - lines.Add(ParentObject.CachedRotationalMatrix.Multiply(new Vector2(_radius, 0))); - return lines; - } - - public override double InertiaTensor(float mass) - { - return 0.5 * mass * _radius * _radius; - } - - public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) - { - info = new CollisionInfo(); - if (other is SphereCollider) - { - SphereCollider b = (SphereCollider)other; - float d = Vector2.DistanceSquared(ParentObject.RigidBody.CentroidWorldSpace, b.ParentObject.RigidBody.CentroidWorldSpace); - if (d < (_radius + b._radius) * (_radius + b._radius)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = _radius + b._radius - (float)Math.Sqrt(d); - info.Normal = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.One); - return true; - } - } - else if (other is BoxCollider) - { - BoxCollider b = (BoxCollider)other; - float depth; - Vector2 normal; - if (GeometryUtils.SphereConvexPolygonCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, - b.GetEdges(deltaTime), b.GetCornerPoints(deltaTime), out depth, out normal)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; - return true; - } - } - else if (other is TileCollider) - { - float depth; - Vector2 normal; - if (TileCollisionUtils.GetSphereTileCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, - ParentObject, info.Target, out depth, out normal)) - { - info.Source = this.ParentObject; - info.Target = other.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = normal; - return true; - } - } - else if (other is CapsuleCollider) - { - CapsuleCollider b = (CapsuleCollider)other; - b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); - if (GeometryUtils.SphereCapsuleCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, Radius, - A, B, b.Radius, out float depth, out Vector2 normal)) - { - info.Source = this.ParentObject; - info.Target = b.ParentObject; - info.Time = deltaTime; - info.Depth = depth; - info.Normal = normal; - return true; - } - } - return false; - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs deleted file mode 100644 index 2cf5b8b38..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollider.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Everglow.Commons.Physics.PBEngine.GameInteraction -{ - /// - /// 与TR世界地形碰撞的碰撞体 - /// - public class TileCollider : Collider2D - { - public override AABB GetAABB(float deltaTime) - { - return new AABB() - { - MinPoint = new Vector2(float.NegativeInfinity, float.NegativeInfinity), - MaxPoint = new Vector2(float.PositiveInfinity, 0) - }; - } - - public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) - { - // Leave empty since static object will not serve as collision source - - collisionEvents = new List(); - } - - //public override List GetContactInfo(CollisionEvent2D e, float deltaTime) - //{ - // return new List(); - // // Leave empty since static object will not serve as collision source - //} - - public override List GetWireFrameWires() - { - return new List(); - } - - public override double InertiaTensor(float mass) - { - return 1; - } - - public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) - { - // Leave empty since static object will not serve as collision source - info = new CollisionInfo(); - return false; - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs b/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs deleted file mode 100644 index 0e40538ab..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs +++ /dev/null @@ -1,414 +0,0 @@ -using ChatGPT.Core.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Terraria; -using static Humanizer.In; - -namespace Everglow.Commons.Physics.PBEngine.GameInteraction -{ - /// - /// 与TR地形进行碰撞检测的函数 - /// - public class TileCollisionUtils - { - public static List GenerateHalfBrick(int x, int y) - { - var slope = Main.tile[x, y].Slope; - if (slope == Terraria.ID.SlopeType.Solid) - { - if (Main.tile[x, y].IsHalfBlock) - { - return new List() - { - new Vector2(x * 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 - 8 ), - new Vector2(x * 16, -y * 16 - 8) - }; - } - else - { - return new List() - { - new Vector2(x * 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 ), - new Vector2(x * 16, -y * 16) - }; - } - } - else if (slope == Terraria.ID.SlopeType.SlopeDownLeft) - { - return new List() - { - new Vector2(x * 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 - 16), - new Vector2(x * 16, -y * 16) - }; - } - else if (slope == Terraria.ID.SlopeType.SlopeDownRight) - { - return new List() - { - new Vector2(x * 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 ) - }; - } - else if (slope == Terraria.ID.SlopeType.SlopeUpLeft) - { - return new List() - { - new Vector2(x * 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 ), - new Vector2(x * 16, -y * 16) - }; - } - else if (slope == Terraria.ID.SlopeType.SlopeUpRight) - { - return new List() - { - new Vector2(x * 16 + 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 ), - new Vector2(x * 16, -y * 16) - }; - } - return new List() - { - new Vector2(x * 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 - 16), - new Vector2(x * 16 + 16, -y * 16 ), - new Vector2(x * 16, -y * 16) - }; - } - - public static bool GetPolygonTileCollisionInfo(AABB boundingBox, Vector2 polyCenter, List edges, List corners, - out float depth1, out Vector2 normal1) - { - bool collided = false; - float maxDepth = 0; - Vector2 maxNormal = Vector2.Zero; - int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); - int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); - int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); - int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); - - for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) - { - for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) - { - if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) - { - var localPoints = GenerateHalfBrick(x, y); - List edgesTile = new List(); - for (int i = 0; i < localPoints.Count; i++) - { - edgesTile.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); - } - var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); - float depth; - Vector2 normal; - if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edges, corners, edgesTile, localPoints, - out depth, out normal)) - { - depth1 = 0; - normal1 = Vector2.Zero; - return true; - - if (depth > maxDepth) - { - maxDepth = depth; - maxNormal = Vector2.Dot(maxNormal, polyCenter - centerTile) < 0 ? -normal : normal; - } - } - } - } - } - depth1 = 0; - normal1 = maxNormal; - return collided; - } - public static void GetPolygonTileContactInfo(AABB boundingBox, Vector2 polyCenter, List edges, - List corners, - PhysicsObject A, PhysicsObject B, - float deltaTime, - List outputList) - { - - int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); - int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); - int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); - int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); - - for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) - { - for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) - { - if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) - { - var localPoints = GenerateHalfBrick(x, y); - List edgesTile = new List(); - for (int i = 0; i < localPoints.Count; i++) - { - edgesTile.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); - } - var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); - - var boxCollider = (A.Collider as BoxCollider); - float depth; - Vector2 normal; - if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edges, - corners, edgesTile, localPoints, - out depth, out normal)) - { - var e = new CollisionEvent2D() - { - Time = 0, - Source = A, - Target = B, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = Vector2.Dot(normal, polyCenter - centerTile) < 0 ? -normal : normal, - Position = Vector2.Zero, - Depth = depth, - }; - float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); - float weightB = e.Target.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); - e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); - e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); - - List> contacts = new List>(); - GeometryUtils.ConvexPolygonPolygonContactInfo(boxCollider.GetEdges(deltaTime), - boxCollider.GetCornerPoints(deltaTime), edgesTile, localPoints.ToList(), polyCenter, - centerTile, contacts); - foreach (var c in contacts) - { - outputList.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - } - } - } - } - - public static bool GetSphereTileCollisionInfo(Vector2 center, float radius, PhysicsObject A, PhysicsObject B, - out float depth1, out Vector2 normal1) - { - bool collided = false; - float maxDepth = 0; - Vector2 maxNormal = Vector2.Zero; - int minX = (int)Math.Floor((center.X - radius) / 16); - int maxX = (int)Math.Ceiling((center.X + radius) / 16); - int minY = (int)Math.Floor((-center.Y - radius) / 16); - int maxY = (int)Math.Ceiling((-center.Y + radius) / 16); - - for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) - { - for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) - { - if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) - { - var localPoints = GenerateHalfBrick(x, y); - List edges = new List(); - for (int i = 0; i < localPoints.Count; i++) - { - edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); - } - var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); - float depth; - Vector2 normal; - if (GeometryUtils.SphereConvexPolygonCollisionInfo(center, radius, edges, localPoints.ToList(), out depth, out normal)) - { - collided = true; - if (depth > maxDepth) - { - maxDepth = depth; - maxNormal = Vector2.Dot(maxNormal, center - centerTile) < 0 ? -normal : normal; - } - } - } - } - } - depth1 = 0; - normal1 = maxNormal; - return collided; - } - public static void GetSphereTileContactInfo(Vector2 center, float radius, PhysicsObject A, PhysicsObject B, - float deltaTime, - List outputList) - { - int minX = (int)Math.Floor((center.X - radius) / 16); - int maxX = (int)Math.Ceiling((center.X + radius) / 16); - int minY = (int)Math.Floor((-center.Y - radius) / 16); - int maxY = (int)Math.Ceiling((-center.Y + radius) / 16); - - for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) - { - for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) - { - if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) - { - var localPoints = GenerateHalfBrick(x, y); - List edges = new List(); - for (int i = 0; i < localPoints.Count; i++) - { - edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); - } - var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); - float depth; - Vector2 normal; - if (GeometryUtils.SphereConvexPolygonCollisionInfo(A.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), out depth, out normal)) - { - var e = new CollisionEvent2D() - { - Time = 0, - Source = A, - Target = B, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = Vector2.Dot(normal, center - centerTile) < 0 ? -normal : normal, - Position = Vector2.Zero, - Depth = depth, - }; - float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); - float weightB = e.Target.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); - e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); - e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); - - List> contacts = new List>(); - GeometryUtils.SphereConvexPolygonContactInfo(e.Source.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), centerTile, contacts); - foreach (var c in contacts) - { - outputList.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - } - } - } - } - - - public static bool GetCapsuleTileCollisionInfo(AABB boundingBox, Vector2 segA, Vector2 segB, float radius, - out float depth1, out Vector2 normal1) - { - bool collided = false; - float maxDepth = 0; - Vector2 maxNormal = Vector2.Zero; - int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); - int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); - int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); - int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); - - for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) - { - for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) - { - if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) - { - var localPoints = GenerateHalfBrick(x, y); - List edges = new List(); - for (int i = 0; i < localPoints.Count; i++) - { - edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); - } - var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); - float depth; - Vector2 normal; - if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(segA, segB, radius, localPoints.ToList(), edges, - out depth, out normal)) - { - collided = true; - - if (depth > maxDepth) - { - maxDepth = depth; - maxNormal = Vector2.Dot(maxNormal, boundingBox.Center - centerTile) < 0 ? -normal : normal; - } - } - } - } - } - depth1 = 0; - normal1 = maxNormal; - return collided; - } - - public static void GetCapsuleTileContactInfo(AABB boundingBox, Vector2 segA, Vector2 segB, float radius, - PhysicsObject A, PhysicsObject B, - float deltaTime, - List outputList) - { - int minX = (int)Math.Floor((boundingBox.MinPoint.X) / 16); - int maxX = (int)Math.Ceiling((boundingBox.MaxPoint.X) / 16); - int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); - int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); - - var capsule = A.Collider as CapsuleCollider; - - for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) - { - for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) - { - if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) - { - var localPoints = GenerateHalfBrick(x, y); - List edges = new List(); - for (int i = 0; i < localPoints.Count; i++) - { - edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); - } - var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); - float depth; - Vector2 normal; - - if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(segA, segB, radius, localPoints.ToList(), edges, - out depth, out normal)) - { - var e = new CollisionEvent2D() - { - Time = 0, - Source = A, - Target = B, - LocalOffsetSrc = Vector2.Zero, - LocalOffsetTarget = Vector2.Zero, - Normal = Vector2.Dot(normal, boundingBox.Center - centerTile) < 0 ? -normal : normal, - Position = Vector2.Zero, - Depth = depth, - }; - float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); - float weightB = 1 - weightA; - e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); - e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); - - List> contacts = new List>(); - capsule.GetSegment(deltaTime, out Vector2 segAt, out Vector2 segBt); - GeometryUtils.CapsuleConvexPolygonContactInfo(segAt, segBt, radius, localPoints.ToList(), edges, - centerTile, contacts); - foreach (var c in contacts) - { - outputList.Add(new CollisionEvent2D(e) - { - LocalOffsetSrc = c.Key, - LocalOffsetTarget = c.Value, - }); - } - } - } - } - } - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs deleted file mode 100644 index 629db3984..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/PhysicsObject.cs +++ /dev/null @@ -1,191 +0,0 @@ -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Everglow.Commons.Physics.PBEngine -{ - /// - /// 物理对象的容器,可以容纳Rigidbody,Collider等组件 - /// 此外,它还是物理模拟的主体 - /// - public class PhysicsObject - { - /// - /// 物理对象的几何组件,用于碰撞检测和质心计算等 - /// - public Collider2D Collider - { - get => _collider; - } - - /// - /// 物理对象的刚体物理组件,用于存储和模拟刚体数据 - /// - public RigidBody2D RigidBody - { - get => _rigidBody; - } - - /// - /// 物体的质心位置 - /// - public Vector2 Position - { - get => _rigidBody.CentroidWorldSpace; - set => _rigidBody.CentroidWorldSpace = value; - } - - /// - /// 物体的旋转,绕着质心 - /// - public float Rotation - { - get => _rotation; - set - { - _rotation = value; - _cachedRotationalMatrix = Matrix2x2.CreateRotationMatrix(_rotation); - } - } - internal Matrix2x2 CachedRotationalMatrix - { - get => _cachedRotationalMatrix; - } - private Matrix2x2 _cachedRotationalMatrix; - - /// - /// 物体在迭代开始的时候的位置 - /// - public Vector2 OldPosition - { - get => _oldPos; - set => _oldPos = value; - } - - /// - /// 物体在迭代开始的时候的旋转 - /// - public float OldRotation - { - get => _oldRot; - set => _oldRot = value; - } - - /// - /// 物体的GUID - /// - public int GUID - { - get => _guid; - set => _guid = value; - } - - /// - /// 物体的碰撞组名字 - /// - public string Tag - { - get => _tag; - set => _tag = value; - } - - public bool IsActive - { - get => _isActive; - set => _isActive = value; - } - - private Collider2D _collider; - private RigidBody2D _rigidBody; - private Vector2 _oldPos; - private float _rotation; - private float _oldRot; - private int _guid; - private string _tag; - private bool _isActive; - - public PhysicsObject(Collider2D collider, RigidBody2D rigidBody) - { - _collider = collider; - _rigidBody = rigidBody; - _tag = "Default"; - _isActive = true; - - - if (_rigidBody == null) - { - _rigidBody = new RigidBody2D(1) - { - MovementType = MovementType.Static, - UseGravity = false, - }; - } - - Rotation = 0; - Position = Vector2.Zero; - - _collider.ParentObject = this; - _rigidBody.ParentObject = this; - } - - public void Initialize() - { - _rigidBody?.Initialize(); - } - - public void RecordOldState() - { - _oldPos = Position; - _oldRot = _rotation; - } - - public List<(Vector2, Color)> GetWireFrameWires() - { - List<(Vector2, Color)> wires_color = new List<(Vector2, Color)>(); - List wires = _collider.GetWireFrameWires(); - for (int i = 0; i < wires.Count; i++) - { - wires_color.Add((_cachedRotationalMatrix.Multiply(wires[i]) + Position, RigidBody.IsAwake ? Color.White : Color.Gray)); - } - return wires_color; - } - public void CleanThisFrame(float deltaTime) - { - _rigidBody?.CleanInformationThisFrame(deltaTime); - } - public void CleanThisSubstep(float deltaTime) - { - _rigidBody?.CleanInformationSubstep(deltaTime); - Rotation = MathHelper.WrapAngle(Rotation); - } - public void Update(float deltaTime) - { - _rigidBody?.Update(deltaTime); - } - - public void ApplyGravity(Vector2 gravity) - { - if (!_isActive) - { - return; - } - if(_rigidBody != null) - { - if (_rigidBody.UseGravity) - { - _rigidBody.ApplyForce(gravity * _rigidBody.Mass); - } - } - } - - public Vector2 LocalToWorldPos(Vector2 localPos) - { - return _rigidBody.CentroidWorldSpace + _cachedRotationalMatrix.Multiply(localPos); - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs deleted file mode 100644 index 45379b4b2..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/PhysicsSimulation.cs +++ /dev/null @@ -1,512 +0,0 @@ -using Everglow.Commons.DataStructures; -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.Constrains; -using Everglow.Commons.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Terraria; - -namespace Everglow.Commons.Physics.PBEngine -{ - /// - /// 用于物理模拟的世界,可以设置各种世界属性,添加和管理物理对象 - /// 其中最重要的模拟部分也是由这个类完成 - /// - public class PhysicsSimulation - { - public const double EPS = 1e-6; - public int GAUSS_SEIDEL_ITERS - { - get => 8; - } - - private List _objects; - private LinkedList _objectFreeList; - private int _maximumAllocatedId; - private List _constrains; - private IBroadPhase _broadPhase; - private float _gravity; - - private Stopwatch _stopwatchPreIntegration; - private Stopwatch _stopwatchBroadPhase; - private Stopwatch _stopwatchNarrowPhase; - private readonly double _ticksPerMillisecond; - - private long _numBroadPhasePairs; - private long _numNarrowPhasePairs; - - /// - /// 用于性能检测,对各个阶段的耗时进行统计 - /// - public double MeasuredPreIntegrationTimeInMs - { - get => _stopwatchPreIntegration.ElapsedTicks / _ticksPerMillisecond; - } - public double MeasuredBroadPhaseTimeInMs - { - get => _stopwatchBroadPhase.ElapsedTicks / _ticksPerMillisecond; - } - public double MeasuredNarrowPhaseTimeInMs - { - get => _stopwatchNarrowPhase.ElapsedTicks / _ticksPerMillisecond; - } - - /// - /// 粗碰撞检测得到的碰撞对数量 - /// - public long NumOfBroadPhasePairs - { - get => _numBroadPhasePairs; - } - - /// - /// 细碰撞检测实际碰撞出现的物体对数量 - /// - public long NumOfNarrowPhasePairs - { - get => _numNarrowPhasePairs; - } - public float Gravity - { - get => _gravity; - } - - public IBroadPhase BroadPhaseCollisionDetector - { - get => _broadPhase; - } - - public PhysicsSimulation() - : this(CollisionGraph.DefualtGraph) - { - - } - - public PhysicsSimulation(CollisionGraph graph) - { - _objectFreeList = new LinkedList(); - _objects = new List(); - _maximumAllocatedId = 0; - _constrains = new List(); - _broadPhase = new HashGridMethod(graph); - - _gravity = 9.8f; - - _stopwatchPreIntegration = new Stopwatch(); - _stopwatchBroadPhase = new Stopwatch(); - _stopwatchNarrowPhase = new Stopwatch(); - - _ticksPerMillisecond = Stopwatch.Frequency / 1000.0; - - _numBroadPhasePairs = 0; - _numNarrowPhasePairs = 0; - } - - /// - /// 向世界添加物理对象,如果对象没有刚体部件,那么认为是静态物体 - /// - /// - public void AddPhysicsObject(PhysicsObject pobj) - { - pobj.GUID = AllocateGUID(); - if (pobj.GUID == _objects.Count) - { - _objects.Add(pobj); - } - else - { - _objects[pobj.GUID] = pobj; - } - pobj.Initialize(); - } - - /// - /// 向物理世界添加约束对象 - /// - /// - public void AddConstrain(Constrain constrain) - { - _constrains.Add(constrain); - } - - /// - /// Initialize physical object properties - /// - public void Initialize() - { - foreach (PhysicsObject pobj in _objects) - { - pobj.Initialize(); - } - } - - /// - /// 模拟一个完整步长,内部会分为多个子步长 - /// - /// - public void Update(float deltaTime) - { - _stopwatchPreIntegration.Reset(); - _stopwatchBroadPhase.Reset(); - _stopwatchNarrowPhase.Reset(); - CleanUpThisFrame(deltaTime); - float dt = deltaTime / GAUSS_SEIDEL_ITERS; - for (int i = 0; i < GAUSS_SEIDEL_ITERS; i++) - { - _stopwatchPreIntegration.Start(); - PreIntegration(dt); - _stopwatchPreIntegration.Stop(); - - Resolve(dt, _stopwatchBroadPhase, _stopwatchNarrowPhase); - } - //foreach (var pobj in _objects) - //{ - // Main.NewText(pobj.RigidBody.LinearVelocity); - //} - - } - - public List<(Vector2, Color)> GetCurrentWireFrames() - { - List<(Vector2, Color)> result = new List<(Vector2, Color)>(); - foreach (PhysicsObject pobj in _objects) - { - if(!pobj.IsActive) continue; - result.AddRange(pobj.GetWireFrameWires()); - } - foreach (Constrain joint in _constrains) - { - result.AddRange(joint.GetDrawMesh()); - } - return result; - } - - /// - /// 进行预积分,先模拟一个步长 - /// - /// - private void PreIntegration(float deltaTime) - { - CleanUpThisSubstep(deltaTime); - foreach (PhysicsObject pobj in _objects) - { - if (!pobj.IsActive) - { - continue; - } - pobj.ApplyGravity(new Vector2(0, -_gravity)); - } - foreach (var constrain in _constrains) - { - constrain.ApplyForce(deltaTime); - } - foreach (PhysicsObject pobj in _objects) - { - if (!pobj.IsActive) - { - continue; - } - pobj.RecordOldState(); - pobj.Update(deltaTime); - } - foreach (var constrain in _constrains) - { - constrain.Apply(deltaTime); - } - } - - /// - /// 解算所有物体的约束条件,并且做出响应 - /// - /// - /// - /// - private void Resolve(float deltaTime, Stopwatch broadPhase, Stopwatch narrowPhase) - { - broadPhase.Start(); - List activeObjects = _objects.Where(obj => obj.IsActive).ToList(); - _broadPhase.Prepare(activeObjects, deltaTime); - var pairs = _broadPhase.GetCollisionPairs(deltaTime); - _numBroadPhasePairs = pairs.Count; - broadPhase.Stop(); - - narrowPhase.Start(); - _numNarrowPhasePairs = 0; - List contacts = new List(); - foreach (var pair in pairs) - { - if (pair.Key.TestCollisionCondition(pair.Value, deltaTime, out CollisionInfo info)) - { - // 非线性投影:预先移开以获得contact points - float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); - float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); - - info.Source.RigidBody.MoveBody(info.Normal * weightA * info.Depth, deltaTime); - info.Target.RigidBody.MoveBody(-info.Normal * weightB * info.Depth, deltaTime); - - List events; - pair.Key.GetContactInfo(info, deltaTime, out events); - contacts.AddRange(events); - - //// 非线性投影:回溯,计算实际投影后姿态 - //foreach (var e in events) - //{ - // double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(e.LocalOffsetSrc), e.Normal); - // double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(e.LocalOffsetTarget), e.Normal); - // double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; - // double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; - // double effectiveMass = R1 + R2 + e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass; - - // double linearMoveA = info.Depth * e.Source.RigidBody.InvMass / effectiveMass; - // double linearMoveB = -info.Depth * e.Target.RigidBody.InvMass / effectiveMass; - // double angularMoveA = info.Depth * R1 / effectiveMass; - // double angularMoveB = -info.Depth * R2 / effectiveMass; - - // info.Source.RigidBody.MoveBody(info.Normal * (float)linearMoveA, deltaTime); - // info.Target.RigidBody.MoveBody(info.Normal * (float)linearMoveB, deltaTime); - - // double implusePerMoveA = GeometryUtils.Cross(e.LocalOffsetSrc, e.Normal) * e.Source.RigidBody.GlobalInverseInertiaTensor; - // double implusePerMoveB = GeometryUtils.Cross(e.LocalOffsetTarget, e.Normal) * e.Target.RigidBody.GlobalInverseInertiaTensor; - // if (R1 > 0) - // { - // info.Source.Rotation += (float)(angularMoveA * implusePerMoveA / R1); - // } - // if (R2 > 0) - // { - // info.Target.Rotation += (float)(angularMoveB * implusePerMoveB / R2); - // } - //} - - // ApplyConstrains(events, pair.Key.ParentObject, deltaTime); - - _numNarrowPhasePairs++; - } - } - - contacts.Sort((a, b) => - { - return -a.Depth.CompareTo(b.Depth); - }); - - foreach (var contact in contacts) - { - float stiffness = Math.Max(0, (contact.Source.RigidBody.Restitution + contact.Target.RigidBody.Restitution) / 2); - SolveContact_Weak(contact, stiffness,true, deltaTime); - } - for (int i = 0; i < 16; i++) - { - bool stable = true; - foreach (var contact in contacts) - { - if (!SolveContact_Weak(contact, 0, false, deltaTime)) - { - stable = false; - } - } - if (stable) - { - break; - } - } - foreach (var contact in contacts) - { - SolveFriction_Weak(contact, deltaTime); - contact.Source.RigidBody.MatchAwakeState(contact.Target.RigidBody, deltaTime); - } - - foreach (PhysicsObject pobj in _objects) - { - if (!pobj.IsActive) - { - continue; - } - pobj.RigidBody.CheckAwake(deltaTime); - } - narrowPhase.Stop(); - } - - private void SolveFriction_Weak(CollisionEvent2D e, float deltaTime) - { - var ri = e.LocalOffsetSrc; - var rb = e.LocalOffsetTarget; - - var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); - var vb = e.Target.RigidBody.LinearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); - - if (e.NormalVelOld == 0) - { - return; - } - float vrel_n = Vector2.Dot(va - vb, e.Normal); - Vector2 vt = (va - vb) - vrel_n * e.Normal; - - float vel_t = vt.Length(); - float friction = Math.Max(0, (e.Source.RigidBody.Friction + e.Target.RigidBody.Friction) / 2); - - vt = vt.SafeNormalize(Vector2.Zero); - if (vt.Length() == 0) - { - return; - } - double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), vt); - double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), vt); - double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri, e.Normal))), e.Normal); - double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor - //* Utils.Cross(rb, e.Normal))), e.Normal); - double effectiveMass = (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass + R1 + R2); - - // (a × b) × c = (c • a)b - (c • b)a - double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); - Vector2 J = (float)J_n * vt;// + (float)J_t * va_t_unit; - - // var offset = e.Position - (_globalCentroid + ri); - e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); - Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); - } - - private bool SolveContact_Weak(CollisionEvent2D e, float restitution, bool initial, float deltaTime) - { - var ri = e.LocalOffsetSrc; - var rb = e.LocalOffsetTarget; - - var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); - var vb = e.Target.RigidBody.LinearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); - - float vrel_n = Vector2.Dot(va - vb, e.Normal); - - if (vrel_n >= 0) - { - return true; - } - - if (initial) - { - if (e.Source.RigidBody.MovementType == MovementType.Player) - { - e.Source.RigidBody.ContactNormals.Add(e.Normal); - } - if (e.Target.RigidBody.MovementType == MovementType.Player) - { - e.Target.RigidBody.ContactNormals.Add(-e.Normal); - } - } - - float vnew_n = restitution * Math.Max(-vrel_n, 0); - - double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); - double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); - double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; - double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; - double J_n = (vnew_n - vrel_n) / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass + R1 + R2); - Vector2 J = (float)J_n * e.Normal;// + (float)J_t * va_t_unit; - - e.NormalVelOld += (float)J_n; - - // var offset = e.Position - (_globalCentroid + ri); - e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); - Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); - - return vrel_n > -0.1f; - } - - private Dictionary>> GroupCollisions(List> pairs) - { - Dictionary>> groupedPairs = new Dictionary>>(); - UnionFind uf = new UnionFind(_objects.Count); - foreach (var pair in pairs) - { - uf.Union(pair.Key.ParentObject.GUID, pair.Value.ParentObject.GUID); - } - foreach (var pair in pairs) - { - int group = uf.Find(pair.Key.ParentObject.GUID); - if (groupedPairs.ContainsKey(group)) - { - groupedPairs[group].Add(pair); - } - else - { - groupedPairs.Add(group, new List>() { pair }); - } - } - return groupedPairs; - } - - private void ApplyConstrains(List events, PhysicsObject pobj, float deltaTime) - { - if (deltaTime == 0) - { - return; - } - pobj.RigidBody.RespondToEvents(events, deltaTime); - } - private void CleanUpThisFrame(float deltaTime) - { - foreach (PhysicsObject pobj in _objects) - { - pobj.CleanThisFrame(deltaTime); - } - } - private void CleanUpThisSubstep(float deltaTime) - { - foreach (PhysicsObject pobj in _objects) - { - if (!pobj.IsActive) - { - continue; - } - pobj.CleanThisSubstep(deltaTime); - } - } - - - public void RemoveObject(PhysicsObject obj) - { - _objectFreeList.AddLast(obj.GUID); - obj.IsActive = false; - } - - private void RemoveObject(int GUID) - { - Debug.Assert(_objects[GUID].IsActive); - RemoveObject(_objects[GUID]); - } - - private List AllocateGUIDs(int size) - { - List results = new List(); - while (size > 0) - { - if (_objectFreeList.Count > 0) - { - results.Add(_objectFreeList.First()); - _objectFreeList.RemoveFirst(); - } - else - { - results.Add(_maximumAllocatedId++); - } - } - return results; - } - - private int AllocateGUID() - { - if (_objectFreeList.Count == 0) - { - return _maximumAllocatedId++; - } - int id = _objectFreeList.First(); - _objectFreeList.RemoveFirst(); - return id; - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs deleted file mode 100644 index d4054426c..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs +++ /dev/null @@ -1,229 +0,0 @@ -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.Constrains; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Everglow.Commons.Physics.PBEngine.PlayGround.Constrain -{ - /// - /// 储存了一些测试约束的场景 - /// - public class JointPlayground - { - public static PhysicsSimulation SimpleJoint() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(500, 660); - dynamicBox.Rotation = 0.0f; - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(300, 32), null); - staticPlane1.Position = new Vector2(512, 700); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - - var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -40), new Vector2(0, 64)); - world.AddConstrain(joint); - - return world; - } - - public static PhysicsSimulation SimpleJointMultiple() - { - var world = new PhysicsSimulation(); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(32, 32), null); - staticPlane1.Position = new Vector2(512, 700); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(500, 600); - dynamicBox.Rotation = 0.0f; - world.AddPhysicsObject(dynamicBox); - - var dynamicBox1 = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox1.Position = new Vector2(500, 500); - dynamicBox1.Rotation = 0.0f; - world.AddPhysicsObject(dynamicBox1); - - var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -50), new Vector2(0, 50)); - world.AddConstrain(joint); - - var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -70), new Vector2(0, 70)); - world.AddConstrain(joint2); - - //var kPlane = new PhysicsObject( - // new BoxCollider(200, 32), null); - //kPlane.Position = new Vector2(512, 400); - //kPlane.Rotation = 0; - //kPlane.RigidBody.ApplyAngularVelocity(0.9f); - //kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; - //world.AddPhysicsObject(kPlane); - return world; - } - - public static PhysicsSimulation DynamicJoints() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(500, 500); - dynamicBox.Rotation = 0.2f; - world.AddPhysicsObject(dynamicBox); - - var dynamicBox1 = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox1.Position = new Vector2(500, 440); - dynamicBox1.Rotation = 0.3f; - world.AddPhysicsObject(dynamicBox1); - - var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -50), new Vector2(0, 50)); - world.AddConstrain(joint2); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(700, 32), null); - staticPlane1.Position = new Vector2(512, 300); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - - return world; - } - - public static PhysicsSimulation NewtonPendulum() - { - var world = new PhysicsSimulation(); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(512, 32), null); - staticPlane1.Position = new Vector2(512, 800); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - - for (int i = 0; i < 5; i++) - { - var dynamicBox = new PhysicsObject( - new SphereCollider(32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512 + 64 * (i - 2), 600); - dynamicBox.Rotation = 0.0f; - world.AddPhysicsObject(dynamicBox); - - if (i == 0) - { - dynamicBox.RigidBody.LinearVelocity = new Vector2(-50, 0); - } - - var joint1 = new JointConstrain(staticPlane1, dynamicBox, new Vector2(64 * (i - 2), -16), new Vector2(0, 200)); - world.AddConstrain(joint1); - } - - - //var dynamicBox1 = new PhysicsObject( - // new SphereCollider(32), new RigidBody2D(256)); - //dynamicBox1.Position = new Vector2(512, 584); - //dynamicBox1.Rotation = 0.0f; - //world.AddPhysicsObject(dynamicBox1); - - //var dynamicBox2 = new PhysicsObject( - // new SphereCollider(32), new RigidBody2D(256)); - //dynamicBox2.Position = new Vector2(576, 584); - //dynamicBox2.Rotation = 0.0f; - //world.AddPhysicsObject(dynamicBox2); - - - - - - //var joint2 = new Joint(staticPlane1, dynamicBox1, new Vector2(0, -16), new Vector2(0, 200)); - //world.AddConstrain(joint2); - - //var joint3 = new Joint(staticPlane1, dynamicBox2, new Vector2(64, -16), new Vector2(0, 200)); - //world.AddConstrain(joint3); - - - return world; - } - - public static PhysicsSimulation SimpleSpring() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(300, 100); - dynamicBox.Rotation = 0.0f; - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(300, 32), null); - staticPlane1.Position = new Vector2(512, 700); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - - var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); - world.AddConstrain(joint); - - var dynamicBox1 = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox1.Position = new Vector2(400, 10); - dynamicBox1.Rotation = 0.0f; - world.AddPhysicsObject(dynamicBox1); - - var joint1 = new SpringConstrain(dynamicBox, dynamicBox1, 29f, 200); - world.AddConstrain(joint1); - - return world; - } - - public static PhysicsSimulation SpringTriganluar() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 64), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(300, 100); - dynamicBox.Rotation = 0.0f; - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(32, 32), null); - staticPlane1.Position = new Vector2(512, 700); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - - var staticPlane2 = new PhysicsObject( - new BoxCollider(32, 32), null); - staticPlane2.Position = new Vector2(212, 300); - staticPlane2.Rotation = 0f; - world.AddPhysicsObject(staticPlane2); - - var staticPlane3 = new PhysicsObject( -new BoxCollider(32, 32), null); - staticPlane3.Position = new Vector2(812, 300); - staticPlane3.Rotation = 0f; - world.AddPhysicsObject(staticPlane3); - - var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); - world.AddConstrain(joint); - var joint1 = new SpringConstrain(staticPlane2, dynamicBox, 29f, 200); - world.AddConstrain(joint1); - - var joint2 = new SpringConstrain(staticPlane3, dynamicBox, 29f, 200); - world.AddConstrain(joint2); - - - return world; - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs b/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs deleted file mode 100644 index 69cf3c581..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs +++ /dev/null @@ -1,359 +0,0 @@ -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Everglow.Commons.Physics.PBEngine.PlayGround.Contact -{ - /// - /// 储存了一些物理引擎接触测试的场景 - /// - public class ContactPlayGround1 - { - public static PhysicsSimulation SimpleGround1() - { - var world = new PhysicsSimulation(); - - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512, 800); - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(300, 32), null); - staticPlane1.Position = new Vector2(512, 200); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - - return world; - } - - public static PhysicsSimulation InclinedGround() - { - var world = new PhysicsSimulation(); - - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512, 800); - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(600, 32), null); - staticPlane1.Position = new Vector2(512, 200); - staticPlane1.Rotation = 0.22f; - world.AddPhysicsObject(staticPlane1); - - return world; - } - - public static PhysicsSimulation MultipleInclinedGround() - { - var world = new PhysicsSimulation(); - - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512, 900); - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(500, 32), null); - staticPlane1.Position = new Vector2(300, 700); - staticPlane1.Rotation = -0.32f; - world.AddPhysicsObject(staticPlane1); - - var staticPlane2 = new PhysicsObject( - new BoxCollider(500, 32), null); - staticPlane2.Position = new Vector2(680, 200); - staticPlane2.Rotation = 0.32f; - world.AddPhysicsObject(staticPlane2); - - return world; - } - - public static PhysicsSimulation MultipleInclinedGround2() - { - var world = new PhysicsSimulation(); - - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512, 900); - world.AddPhysicsObject(dynamicBox); - - for (int i = 0; i < 5; i++) - { - - var staticPlane1 = new PhysicsObject( - new BoxCollider(500, 32), null); - staticPlane1.Position = new Vector2(300, 700 - i * 200); - staticPlane1.Rotation = -0.32f; - world.AddPhysicsObject(staticPlane1); - - var staticPlane2 = new PhysicsObject( - new BoxCollider(500, 32), null); - staticPlane2.Position = new Vector2(680, 700 - i * 200 - 100); - staticPlane2.Rotation = 0.32f; - world.AddPhysicsObject(staticPlane2); - } - return world; - } - - public static PhysicsSimulation MultipleInclinedGround3() - { - var world = new PhysicsSimulation(); - - for (int i = 0; i < 20; i++) - { - var dynamicBox = new PhysicsObject( - new SphereCollider(16), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512 + (i % 10) * 40, 900 + i / 10 * 40); - world.AddPhysicsObject(dynamicBox); - } - - for (int i = 0; i < 5; i++) - { - - var staticPlane1 = new PhysicsObject( - new BoxCollider(500, 32), null); - staticPlane1.Position = new Vector2(300, 700 - i * 250); - staticPlane1.Rotation = -0.38f; - world.AddPhysicsObject(staticPlane1); - - var staticPlane2 = new PhysicsObject( - new BoxCollider(500, 32), null); - staticPlane2.Position = new Vector2(680, 700 - i * 250 - 125); - staticPlane2.Rotation = 0.38f; - world.AddPhysicsObject(staticPlane2); - } - return world; - } - - public static PhysicsSimulation MultipleInclinedGroundSphere() - { - var world = new PhysicsSimulation(); - - - for (int i = 0; i < 20; i++) - { - var dynamicBall = new PhysicsObject( - new SphereCollider(16), new RigidBody2D(256)); - dynamicBall.Position = new Vector2(512, 800 + i * 40); - world.AddPhysicsObject(dynamicBall); - } - - var staticPlane1 = new PhysicsObject( - new BoxCollider(400, 32), null); - staticPlane1.Position = new Vector2(200, 400); - staticPlane1.Rotation = -0.78f; - world.AddPhysicsObject(staticPlane1); - - - var staticPlane2 = new PhysicsObject( - new BoxCollider(400, 32), null); - staticPlane2.Position = new Vector2(400, 400); - staticPlane2.Rotation = 0.78f; - world.AddPhysicsObject(staticPlane2); - - return world; - } - - public static PhysicsSimulation DoubleSlope() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new BoxCollider(162, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(300, 600); - world.AddPhysicsObject(dynamicBox); - - var dynamicBox1 = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox1.Position = new Vector2(300, 760); - dynamicBox1.RigidBody.ApplyForce(new Vector2(100, 0)); - world.AddPhysicsObject(dynamicBox1); - - - for (int i = 0; i < 100; i++) - { - var dynamicBox2 = new PhysicsObject( - new BoxCollider(55, 32), new RigidBody2D(256)); - dynamicBox2.Position = new Vector2(350, 700 + 36 * i); - dynamicBox2.RigidBody.ApplyForce(new Vector2(100, 0)); - world.AddPhysicsObject(dynamicBox2); - } - - var staticPlane1 = new PhysicsObject( - new BoxCollider(400, 32), null); - staticPlane1.Position = new Vector2(200, 400); - staticPlane1.Rotation = -0.78f; - world.AddPhysicsObject(staticPlane1); - - - var staticPlane2 = new PhysicsObject( - new BoxCollider(400, 32), null); - staticPlane2.Position = new Vector2(400, 400); - staticPlane2.Rotation = 0.78f; - world.AddPhysicsObject(staticPlane2); - - return world; - } - - public static PhysicsSimulation SimpleKinematic() - { - var world = new PhysicsSimulation(); - - for (int i = 0; i < 200; i++) - { - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512 + (i % 10) * 35, 900 + i / 10 * 35); - world.AddPhysicsObject(dynamicBox); - } - - var kPlane = new PhysicsObject( - new BoxCollider(500, 32), null); - kPlane.Position = new Vector2(512, 512); - kPlane.Rotation = 0; - kPlane.RigidBody.ApplyAngularVelocity(0.9f); - kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; - world.AddPhysicsObject(kPlane); - - var sPlane = new PhysicsObject( - new BoxCollider(32, 600), null); - sPlane.Position = new Vector2(0, 512); - sPlane.Rotation = 0; - world.AddPhysicsObject(sPlane); - - var sPlane2 = new PhysicsObject( - new BoxCollider(32, 600), null); - sPlane2.Position = new Vector2(1024, 512); - sPlane2.Rotation = 0; - world.AddPhysicsObject(sPlane2); - - var sPlane1 = new PhysicsObject( - new BoxCollider(1024, 32), null); - sPlane1.Position = new Vector2(512, 0); - sPlane1.Rotation = 0; - world.AddPhysicsObject(sPlane1); - return world; - } - - public static PhysicsSimulation SimpleSphere() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new SphereCollider(32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512, 800); - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(300, 32), null); - staticPlane1.Position = new Vector2(512, 200); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - return world; - } - - public static PhysicsSimulation SlopeSphere() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new SphereCollider(32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512, 800); - world.AddPhysicsObject(dynamicBox); - - var staticPlane1 = new PhysicsObject( - new BoxCollider(300, 32), null); - staticPlane1.Position = new Vector2(512, 200); - staticPlane1.Rotation = 0.3f; - world.AddPhysicsObject(staticPlane1); - return world; - } - - public static PhysicsSimulation SphereAndBox() - { - var world = new PhysicsSimulation(); - - for (int i = 0; i < 10; i++) - { - var dynamicBox = new PhysicsObject( - new SphereCollider(32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512 + (i - 5) * 50, 800); - world.AddPhysicsObject(dynamicBox); - } - - for (int i = 0; i < 10; i++) - { - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512 + (i - 5) * 50, 700); - world.AddPhysicsObject(dynamicBox); - } - - var staticPlane1 = new PhysicsObject( - new BoxCollider(600, 32), null); - staticPlane1.Position = new Vector2(512, 200); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - return world; - } - - public static PhysicsSimulation Box2() - { - var world = new PhysicsSimulation(); - - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512, 700); - dynamicBox.RigidBody.Restitution = 0.3f; - world.AddPhysicsObject(dynamicBox); - - var dynamicBox1 = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox1.Position = new Vector2(512, 734); - dynamicBox1.RigidBody.Restitution = 0.3f; - world.AddPhysicsObject(dynamicBox1); - - - var staticPlane1 = new PhysicsObject( - new BoxCollider(600, 32), null); - staticPlane1.Position = new Vector2(512, 200); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - return world; - } - - public static PhysicsSimulation BoxStack() - { - var world = new PhysicsSimulation(); - - for (int i = 0; i < 5; i++) - { - - for (int j = 1; j <= i + 1; j++) - { - var dynamicBox = new PhysicsObject( - new BoxCollider(32, 32), new RigidBody2D(256)); - dynamicBox.Position = new Vector2(512 + (j - 2) * 38 - 16 * i, 500 - i * 34); - dynamicBox.RigidBody.Restitution = 0.3f; - world.AddPhysicsObject(dynamicBox); - } - } - - - var staticPlane1 = new PhysicsObject( - new BoxCollider(600, 32), null); - staticPlane1.Position = new Vector2(512, 200); - staticPlane1.Rotation = 0f; - world.AddPhysicsObject(staticPlane1); - return world; - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs deleted file mode 100644 index 59f37705f..000000000 --- a/Sources/Everglow.Core/Physics/PBEngine/RigidBody2D.cs +++ /dev/null @@ -1,740 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing.Printing; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Utilities; -using Terraria; - -namespace Everglow.Commons.Physics.PBEngine -{ - /// - /// 2D刚体物理组件,用于实际的刚体运动模拟和响应的模块 - /// - public class RigidBody2D - { - /// - /// 指向物理对象的指针 - /// - public PhysicsObject ParentObject - { - get => _bindObject; - set => _bindObject = value; - } - - /// - /// 世界坐标下的质心坐标(指物理世界 - /// - public Vector2 CentroidWorldSpace - { - get => _globalCentroid; - set => _globalCentroid = value; - } - - /// - /// 质量 - /// - public float Mass - { - get => _mass; - set => _mass = value; - } - - /// - /// 质量的倒数 - /// - public float InvMass - { - get => (MovementType == MovementType.Dynamic || MovementType == MovementType.Player) ? 1 / _mass : 0; - } - - /// - /// 世界坐标下惯性张量的倒数 - /// - public double GlobalInverseInertiaTensor - { - get => MovementType == MovementType.Dynamic ? _globalInverseInertiaTensor : 0; - } - - /// - /// 是否启用重力 - /// - public bool UseGravity - { - get; set; - } - - /// - /// 线速度 - /// - public Vector2 LinearVelocity - { - get => MovementType == MovementType.Static ? Vector2.Zero : _linearVelocity; - set => _linearVelocity = value; - } - - - /// - /// 角速度 - /// - public float AngularVelocity - { - get => MovementType == MovementType.Static ? 0 : _angularVelocity; - set => _angularVelocity = value; - } - - /// - /// 移动模式 - /// - public MovementType MovementType - { - get; set; - } - - /// - /// 线速度的阻尼 - /// - public float Drag - { - get => _drag; - set => _drag = value; - } - - /// - /// 角速度的阻尼 - /// - public float AngularDrag - { - get => _angularDrag; - set => _angularDrag = value; - } - - /// - /// 碰撞弹出的系数 - /// - public float Restitution - { - get => _restitution; - set => _restitution = value; - } - - /// - /// 摩擦力系数 - /// - public float Friction - { - get => _friction; - set => _friction = value; - } - - public List TangetRelativeVelocity - { - get => _contactTangentVels; - } - - public List ContactNormals - { - get => _contactNormals; - } - - /// - /// 物体是否是唤醒状态,如果唤醒那么可以进行运动模拟,注意唤醒时会重置运动值 - /// - public bool IsAwake - { - get => _isAwake; - set - { - if (value && !_isAwake) - { - _motion = SleepEPS * 2; - } - else if (!value && _isAwake) - { - _linearVelocity = Vector2.Zero; - _angularVelocity = 0; - } - _isAwake = value; - } - } - - /// - /// 物体是否可以进入沉睡状态,用户能控制的物体应该设为false - /// - public bool CanSleep - { - get => _canSleep; - set => _canSleep = value; - } - - private double _localInverseInertiaTensor; - private double _globalInverseInertiaTensor; - - private Vector2 _globalCentroid; - - private Vector2 _linearVelocity; - private float _angularVelocity; - - private float _torque; - private Vector2 _force; - private float _mass; - private float _drag; - private float _angularDrag; - private float _restitution; - private float _friction; - - private List _contactTangentVels; - private List _contactNormals; - - private List _impluses; - - private bool _isAwake; - private bool _canSleep; - private double _motion; - - private PhysicsObject _bindObject; - - private const double SleepEPS = 1e-2; - - public RigidBody2D(float mass) - { - _mass = mass; - MovementType = MovementType.Dynamic; - UseGravity = true; - _isAwake = true; - _canSleep = true; - _linearVelocity = Vector2.Zero; - _angularVelocity = 0; - _drag = 0.06f; - _angularDrag = 0.06f; - _impluses = new List(); - _restitution = 0.5f; - _friction = 0.5f; - _contactTangentVels = new List(); - _contactNormals = new List(); - - _motion = SleepEPS * 2; - } - - - private void CalculateMassCentroidAndMoI() - { - _globalInverseInertiaTensor = _localInverseInertiaTensor = 1 / _bindObject.Collider.InertiaTensor(_mass); - } - - public void Initialize() - { - CalculateMassCentroidAndMoI(); - } - - - /// - /// 进行一次运动积分 - /// - /// - public void Update(float deltaTime) - { - if (!_isAwake || MovementType == MovementType.Static) - { - return; - } - - _angularVelocity += (float)(_globalInverseInertiaTensor * deltaTime * _torque); - _linearVelocity += deltaTime * _force / _mass; - // _oldRot = _rotation; - - // _oldPos = _globalCentroid; - _globalCentroid += deltaTime * _linearVelocity; - _bindObject.Rotation += deltaTime * _angularVelocity; - - - - Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); - Debug.Assert(!float.IsNaN(_angularVelocity)); - - StabilizeBody(); - - // CollisionDetection(deltaTime); - // CollisionResponse(deltaTime); - } - - private void StabilizeBody() - { - if (Math.Abs(_linearVelocity.X) < PhysicsSimulation.EPS) - { - _linearVelocity.X = 0; - } - if (Math.Abs(_linearVelocity.Y) < PhysicsSimulation.EPS) - { - _linearVelocity.Y = 0; - } - - if(Math.Abs(_angularVelocity) < PhysicsSimulation.EPS) - { - _angularVelocity = 0; - } - } - - public void ApplyForce(Vector2 force) - { - if (MovementType == MovementType.Static) - { - return; - } - _force += force; - } - - public void CleanInformationSubstep(float deltaTime) - { - _torque = 0; - _force = Vector2.Zero; - _impluses.Clear(); - // Main.NewText((_linearVelocity.LengthSquared() * _mass * 0.5f + _mass * 9.8f * _globalCentroid.Y).ToString("F1")); - } - - public void CleanInformationThisFrame(float deltaTime) - { - _linearVelocity *= 1 - _drag * _drag; - _angularVelocity *= 1 - _angularDrag * _angularDrag; - _contactTangentVels.Clear(); - _contactNormals.Clear(); - } - - public bool TryRespondTo2Events(List events, float deltaTime) - { - Debug.Assert(events.Count == 2); - Debug.Assert(events[0].Target == events[1].Target); - var target = events[0].Target; - - var ri0 = events[0].LocalOffsetSrc; - var rb0 = events[0].LocalOffsetTarget; - var n0 = events[0].Normal; - var va0 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); - var vb0 = events[0].Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); - float relv0_n = Vector2.Dot(va0 - vb0, n0); - if (relv0_n >= 0) - { - return false; - } - - var ri1 = events[1].LocalOffsetSrc; - var rb1 = events[1].LocalOffsetTarget; - var n1 = events[1].Normal; - var va1 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); - var vb1 = events[1].Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); - float relv1_n = Vector2.Dot(va1 - vb1, n1); - if (relv1_n >= 0) - { - return false; - } - Matrix2x2 matrix = new Matrix2x2() - { - [0, 0] = InvMass + events[0].Target.RigidBody.InvMass - + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri0, n0))), n0) - + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor - * GeometryUtils.Cross(rb0, n0))), n0), - [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri1, n1))), n0), - - [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri0, n0))), n1), - [1, 1] = InvMass + events[1].Target.RigidBody.InvMass - + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri1, n1))), n1) - + Vector2.Dot(GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor - * GeometryUtils.Cross(rb1, n1))), n1), - }; - float C = 0.4f; - var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); - if (!J.HasNaNs()) - { - AddImpluse(J.X * events[0].Normal, ri0, target.RigidBody, - J.X * events[0].Normal, rb0); - AddImpluse(J.Y * events[1].Normal, ri1, target.RigidBody, - J.Y * events[1].Normal, rb1); - } - return true; - } - public void ApplyAngularVelocity(float w) - { - _angularVelocity += w; - } - - - - private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) - { - var ri = e.LocalOffsetSrc; - var rb = e.LocalOffsetTarget; - - var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); - var va = _linearVelocity + v2; - var vb = e.Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); - var bias = 0.1f * Math.Max(e.Depth - 0.02f, 0) / deltaTime; - - - float va_n = Vector2.Dot(va - vb, e.Normal); - if (MovementType == MovementType.Player) - { - _contactNormals.Add(e.Normal); - } - if(e.Target.RigidBody.MovementType == MovementType.Player) - { - e.Target.RigidBody._contactNormals.Add(-e.Normal); - } - if (va_n >= 0) - { - e.NormalVelOld = 0; - return; - } - float stiffness = Math.Max(0, (_restitution + e.Target.RigidBody.Restitution) / 2); - float vnew_n = stiffness * Math.Max(-va_n - 20 * deltaTime, 0); - - // (a × b) × c = (c • a)b - (c • b)a - double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); - double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); - double R1 = rAdotN * rAdotN * GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri, e.Normal))), e.Normal); - double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor - //* Utils.Cross(rb, e.Normal))), e.Normal); - double J_n = (vnew_n - va_n) / (InvMass + e.Target.RigidBody.InvMass + R1 + R2); - Vector2 J = (float)J_n * e.Normal / count;// + (float)J_t * va_t_unit; - e.NormalVelOld = (float)J_n; - - // var offset = e.Position - (_globalCentroid + ri); - AddImpluse(J, ri, e.Target.RigidBody, -J, rb); - Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); - } - - private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime) - { - var ri = e.LocalOffsetSrc; - var rb = e.LocalOffsetTarget; - - var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); - var va = _linearVelocity + v2; - var vb = e.Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); - - if (e.NormalVelOld == 0) - { - return; - } - - float vel_n = Vector2.Dot(va - vb, e.Normal); - Vector2 vt = (va - vb) - vel_n * e.Normal; - - float vel_t = vt.Length(); - float friction = Math.Max(0, (_friction + e.Target.RigidBody.Friction) / 2); - - - vt = vt.SafeNormalize(Vector2.Zero); - if (vt.Length() == 0) - { - return; - } - - _contactTangentVels.Add(-Vector2.Dot(vt, vb) * vt); - e.Target.RigidBody._contactTangentVels.Add(-Vector2.Dot(vt, va) * vt); - if (friction == 0) - { - return; - } - - double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), vt); - double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), vt); - double R1 = rAdotN * rAdotN * GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri, e.Normal))), e.Normal); - double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor - //* Utils.Cross(rb, e.Normal))), e.Normal); - double effectiveMass = (InvMass + e.Target.RigidBody.InvMass + R1 + R2); - - // (a × b) × c = (c • a)b - (c • b)a - double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); - Vector2 J = (float)J_n * vt / count;// + (float)J_t * va_t_unit; - - AddImpluse(J, ri, e.Target.RigidBody, -J, rb); - Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); - } - - /// - /// 对接触事件进行响应 - /// - /// - /// - public void RespondToEvents(List events, float deltaTime) - { - foreach (var e in events) - { - SolveContactImpluse(e, events.Count, deltaTime); - } - ApplyImpluses(); - foreach (var e in events) - { - SolveFrictionImpluse(e, events.Count, deltaTime); - MatchAwakeState(e.Target.RigidBody, deltaTime); - } - ApplyImpluses(); - } - - public void Integrate(float deltaTime) - { - if (MovementType == MovementType.Static) - { - return; - } - _bindObject.Rotation += deltaTime * _angularVelocity; - _globalCentroid += deltaTime * _linearVelocity; - _bindObject.Position = _globalCentroid; - } - - //public void ResolveImpluse(float deltaTime) - //{ - // if (_impluses.Count == 0) - // { - // return; - // } - // //_bindObject.Rotation = _bindObject.OldRotation; - // //_globalCentroid = _bindObject.OldPosition; - // _impluses.Sort((a, b) => - // { - // return a.Time.CompareTo(b.Time); - // }); - // float lastTime = 0; - // int sameTimeCount = 0; - // for (int i = 0; i < _impluses.Count; i++) - // { - // sameTimeCount++; - // if (i == _impluses.Count - 1 || _impluses[i].Time != _impluses[i + 1].Time) - // { - // float dt = (_impluses[i].Time - lastTime); - - // Vector2 linearVelocityChange = Vector2.Zero; - // float angularVelocityChange = 0; - // Vector2 normalAvg = Vector2.Zero; - - // //linearVelocityChange /= sameTimeCount; - // //angularVelocityChange /= sameTimeCount; - // //normalAvg /= sameTimeCount; - // //normalAvg = normalAvg.SafeNormalize(Vector2.Zero); - - - - // var resImpluses = ResolveConstrains(); - // for (int j = i - sameTimeCount + 1; j <= i; j++) - // { - // linearVelocityChange += 1.0f / _mass * resImpluses[j] * _impluses[j].Normal; - // angularVelocityChange += (float)(Utils.Cross(_impluses[j].RelativePosition, - // resImpluses[j] *_impluses[j].Normal * (float)_globalInverseInertiaTensor)); - // } - - - // //var penetration = Math.Max(0, -Vector2.Dot(_linearVelocity * deltaTime, normalAvg)); - // //_linearVelocity += normalAvg * penetration * 0.7f; - - // deltaTime -= dt; - // lastTime = _impluses[i].Time; - // sameTimeCount = 0; - // } - // } - // _impluses.Clear(); - //} - - //private List ResolveConstrains() - //{ - // List impluseArray = new List(); - // List impluseArrayTemp = new List(); - // for (int k = 0; k < _impluses.Count; k++) - // { - // impluseArray.Add(_impluses[k].Impluse.Length()); - // impluseArrayTemp.Add(0f); - // } - // for (int iter = 0; iter < 8; iter++) - // { - // int sameSourceCount = 0; - // for (int k = 0; k < _impluses.Count; k++) - // { - // var ri0 = _impluses[k].CollisionEvent.LocalOffsetSrc; - // var rb0 = _impluses[k].CollisionEvent.LocalOffsetTarget; - // var n0 = _impluses[k].CollisionEvent.Normal; - // var va0 = _impluses[k].CollisionEvent.Source.RigidBody.LinearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(ri0, _impluses[k].CollisionEvent.Source.RigidBody.AngularVelocity); - // var vb0 = _impluses[k].CollisionEvent.Target.RigidBody.LinearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(rb0, _impluses[k].CollisionEvent.Target.RigidBody.AngularVelocity); - // float relv0_n = Vector2.Dot(va0 - vb0, n0); - // float relv0_plus = -(1 + 0.4f) * Vector2.Dot(va0 - vb0, n0); - - // float Kj = _impluses[k].CollisionEvent.Source.RigidBody.InvMass + _impluses[k].CollisionEvent.Target.RigidBody.InvMass - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor - // * Utils.Cross(ri0, n0))), n0) - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(_impluses[k].CollisionEvent.Target.RigidBody.GlobalInverseInertiaTensor - // * Utils.Cross(rb0, n0))), n0); - - // float KLU = 0f; - // for (int l = 0; l < _impluses.Count; l++) - // { - // if (l == k) - // { - // continue; - // } - // var ri1 = _impluses[l].CollisionEvent.LocalOffsetSrc; - // var rb1 = _impluses[l].CollisionEvent.LocalOffsetTarget; - // var n1 = _impluses[l].CollisionEvent.Normal; - - // KLU += (_impluses[k].CollisionEvent.Source.RigidBody.InvMass - // * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, - // (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor - // * Utils.Cross(ri1, n1))), n0)) * impluseArray[l]; - // } - // impluseArrayTemp[k] = 1 / Kj * (relv0_plus - KLU); - // } - // sameSourceCount = 0; - // // Deep copy - // for (int k = 0; k < impluseArray.Count; k++) - // { - // impluseArray[k] = impluseArrayTemp[k]; - // } - // } - - // return impluseArray; - //} - - public void AddForce(Vector2 force, Vector2 relPos) - { - _force += force; - _torque += GeometryUtils.Cross(relPos, force); - } - - /// - /// 把冲量数据累加到这个刚体上 - /// - /// - /// - /// - /// - /// - public void AddImpluse(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) - { - var entry = new ImpluseEntry() - { - Source = this, - Target = other, - ImpluseSource = J, - RelativePositionSource = relativePos, - ImpluseTarget = J2, - RelativePositionTarget = relativePos2 - }; - _impluses.Add(entry); - } - - /// - /// 对累加的冲量进行应用,更新各种速度 - /// - public void ApplyImpluses() - { - if (_impluses.Count > 0) - { - foreach (var imp in _impluses) - { - if (imp.Source.MovementType == MovementType.Dynamic || imp.Source.MovementType == MovementType.Player) - { - _linearVelocity += 1.0f / _mass * imp.ImpluseSource; - if (imp.Source.MovementType != MovementType.Player) - { - _angularVelocity += GeometryUtils.Cross(imp.RelativePositionSource, imp.ImpluseSource * (float)_globalInverseInertiaTensor); - } - } - if (imp.Target.MovementType == MovementType.Dynamic || imp.Target.MovementType == MovementType.Player) - { - imp.Target._linearVelocity += imp.Target.InvMass * imp.ImpluseTarget; - if (imp.Target.MovementType != MovementType.Player) - { - imp.Target._angularVelocity += GeometryUtils.Cross(imp.RelativePositionTarget, imp.ImpluseTarget * (float)imp.Target.GlobalInverseInertiaTensor); - } - } - - } - } - - Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); - Debug.Assert(!float.IsNaN(_angularVelocity)); - StabilizeBody(); - _impluses.Clear(); - } - - /// - /// 立即施加一个冲量 - /// - /// - /// - public void AddImpluseImmediate(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) - { - if (MovementType != MovementType.Static && MovementType != MovementType.Kinematic) - { - _linearVelocity += 1.0f / _mass * J; - Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); - _angularVelocity += (float)(GeometryUtils.Cross(relativePos, - J * (float)GlobalInverseInertiaTensor)); - Debug.Assert(!float.IsNaN(_angularVelocity)); - } - - if (other.MovementType != MovementType.Static && MovementType != MovementType.Kinematic) - { - other._linearVelocity += other.InvMass * J2; - other._angularVelocity += (float)(GeometryUtils.Cross(relativePos2, - J2 * (float)other.GlobalInverseInertiaTensor)); - } - } - - public void MoveBody(Vector2 dir, float deltaTime) - { - _globalCentroid += dir; - // _linearVelocity = (_globalCentroid - ParentObject.OldPosition) / deltaTime; - } - - public void MatchAwakeState(RigidBody2D other, float deltaTime) - { - if (other.MovementType == MovementType.Static) - { - return; - } - if (!_isAwake && !other._isAwake) - { - return; - } - if (_isAwake && !other._isAwake) - { - other.IsAwake = true; - } - else if (!_isAwake && other._isAwake) - { - IsAwake = true; - } - } - - public void CheckAwake(float deltaTime) - { - if (MovementType == MovementType.Static || MovementType == MovementType.Kinematic) - { - return; - } - double bias = Math.Pow(0.6, deltaTime); - _motion = bias * _motion + (1 - bias) * (_linearVelocity.LengthSquared() + - 3 * _angularVelocity * _angularVelocity); - - if (_motion > 16 * SleepEPS) - _motion = 16 * SleepEPS; - - if (_motion < SleepEPS) - { - IsAwake = false; - } - } - } -} diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/AABB.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/AABB.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/AABB.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs new file mode 100644 index 000000000..c1a34ce69 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs @@ -0,0 +1,60 @@ +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +{ + /// + /// 存储碰撞组的碰撞关系的图结构 + /// + public class CollisionGraph + { + public static CollisionGraph DefualtGraph + = new CollisionGraph(new Dictionary>() { { "Default", new List() { "Default" } } }); + + public Dictionary> Graph + { + get; + set; + } + + public CollisionGraph() + { + Graph = new Dictionary>(); + } + + public CollisionGraph(Dictionary> graph) + { + Graph = graph; + } + + public void AddDoubleEdge(string groupA, string groupB) + { + if (groupA == groupB) + { + return; + } + + // Ensure only compare once + if (string.CompareOrdinal(groupA, groupB) < 0) + { + AddSingleEdge(groupA, groupB); + } + else + { + AddSingleEdge(groupB, groupA); + } + } + + public void AddSingleEdge(string groupA, string groupB) + { + if (Graph.ContainsKey(groupA)) + { + Graph[groupA].Add(groupB); + } + else + { + Graph.Add(groupA, new List() + { + groupB, + }); + } + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/SweepAndPrune.cs diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs new file mode 100644 index 000000000..fd15abded --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs @@ -0,0 +1,505 @@ +using Everglow.Commons.Physics.PBEngine.GameInteraction; + +namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders +{ + /// + /// 四边形、盒子碰撞体 + /// + public class BoxCollider : Collider2D + { + public Vector2 Size + { + get => new Vector2(_width, _height); + } + + private float _width; + private float _height; + + public BoxCollider(float width, float height) + { + _width = width; + _height = height; + } + + public override double InertiaTensor(float mass) + { + return mass / 12.0 * (_width * _width + _height * _height); + } + + public override List GetWireFrameWires() + { + List wires = new List + { + new Vector2(-_width / 2, -_height / 2), + new Vector2(_width / 2, -_height / 2), + new Vector2(_width / 2, -_height / 2), + new Vector2(_width / 2, _height / 2), + new Vector2(_width / 2, _height / 2), + new Vector2(-_width / 2, _height / 2), + new Vector2(-_width / 2, _height / 2), + new Vector2(-_width / 2, -_height / 2), + }; + return wires; + } + + public List GetEdges(float dt) + { + List cornerWorldSpace = GetCornerPoints(dt); + List edges = new List(); + for (int i = 0; i < 4; i++) + { + edges.Add(new Edge2D(cornerWorldSpace[i], cornerWorldSpace[(i + 1) % cornerWorldSpace.Count])); + } + return edges; + } + + // public override List GetCollisionEvents(Collider2D other, float deltaTime) + // { + // List events = new List(); + + // Vector2[] localPoints = new Vector2[4] + // { + // new Vector2(-_width / 2, -_height / 2), + // new Vector2(_width / 2, -_height / 2), + // new Vector2(_width / 2, _height / 2), + // new Vector2(-_width / 2, _height / 2) + // }; + // if (other is AABBCollider) + // { + // AABBCollider aabb_other = (AABBCollider)other; + // for (int i = 0; i < 4; i++) + // { + // var curPos = _bindObject.Position + Matrix2x2.CreateRotationMatrix(_bindObject.Rotation).Multiply(localPoints[i]); + // var oldPos = _bindObject.OldPosition + Matrix2x2.CreateRotationMatrix(_bindObject.OldRotation).Multiply(localPoints[i]); + + // foreach (var edge in aabb_other.GetEdges()) + // { + // CollisionEvent2D collision; + // if (edge.Segment_Segment_Collision(oldPos, curPos, out collision)) + // { + // collision.Source = this.ParentObject; + // collision.Target = other.ParentObject; + // collision.LocalOffsetSrc = Matrix2x2.CreateRotationMatrix(_bindObject.Rotation).Multiply(localPoints[i]); + + // events.Add(collision); + // } + // } + // } + // } + // if (events.Count > 1) + // { + // Vector2 averagedLocalPoint = new Vector2(0, 0); + // Vector2 averagedNormal = new Vector2(0, 0); + // float minTime = 1.0f; + // for (int i = 0; i < events.Count; i++) + // { + // averagedLocalPoint += events[i].LocalOffsetSrc; + // averagedNormal += events[i].Normal; + // minTime = Math.Min(minTime, events[i].Time); + // } + // averagedLocalPoint /= events.Count; + // averagedNormal /= events.Count; + // CollisionEvent2D collision = new CollisionEvent2D(); + // collision.Source = this.ParentObject; + // collision.Target = other.ParentObject; + // collision.LocalOffsetSrc = averagedLocalPoint; + // collision.Normal = Vector2.Normalize(averagedNormal); + // collision.Time = minTime; + + // events.Clear(); + // events.Add(collision); + // } + // return events; + // } + public List GetCornerPoints(float dt) + { + List cornerPoints = new List(); + Vector2[] localPoints = new Vector2[4] + { + new Vector2(-_width / 2, -_height / 2), + new Vector2(_width / 2, -_height / 2), + new Vector2(_width / 2, _height / 2), + new Vector2(-_width / 2, _height / 2), + }; + + var M = ParentObject.CachedRotationalMatrix; + for (int i = 0; i < 4; i++) + { + var curPos = ParentObject.Position + M.Multiply(localPoints[i]); + cornerPoints.Add(curPos); + } + return cornerPoints; + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + info = default(CollisionInfo); + if (other is BoxCollider) + { + BoxCollider b = (BoxCollider)other; + List edgesA = GetEdges(deltaTime); + List edgesB = b.GetEdges(deltaTime); + List cornersA = GetCornerPoints(deltaTime); + List cornersB = b.GetCornerPoints(deltaTime); + float depth; + Vector2 normal; + if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edgesA, cornersA, edgesB, cornersB, out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is SphereCollider) + { + SphereCollider b = (SphereCollider)other; + float depth; + Vector2 normal; + bool collided = GeometryUtils.SphereConvexPolygonCollisionInfo(b.ParentObject.Position, b.Radius, GetEdges(deltaTime), + GetCornerPoints(deltaTime), + out depth, out normal); + if (collided) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + } + return collided; + } + else if (other is TileCollider) + { + float depth; + Vector2 normal; + if (TileCollisionUtils.GetPolygonTileCollisionInfo(GetAABB(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, + GetEdges(deltaTime), GetCornerPoints(deltaTime), out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = other.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = normal; + return true; + } + } + else if (other is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)other; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(A, B, b.Radius, GetCornerPoints(deltaTime), + GetEdges(deltaTime), + out float depth, out Vector2 normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + return false; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, + out List collisionEvents) + { + collisionEvents = new List(); + if (info.Target.Collider is BoxCollider) + { + BoxCollider b = (BoxCollider)info.Target.Collider; + List edgesA = GetEdges(deltaTime); + List edgesB = b.GetEdges(deltaTime); + List cornersA = GetCornerPoints(deltaTime); + List cornersB = b.GetCornerPoints(deltaTime); + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = info.Source, + Target = info.Target, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + List> contacts = new List>(); + GeometryUtils.ConvexPolygonPolygonContactInfo(edgesA, cornersA, edgesB, cornersB, + ParentObject.RigidBody.CentroidWorldSpace, b.ParentObject.RigidBody.CentroidWorldSpace, + contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is SphereCollider) + { + SphereCollider b = (SphereCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = 0, + Source = info.Source, + Target = info.Target, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + + List> contacts = new List>(); + GeometryUtils.SphereConvexPolygonContactInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, GetEdges(deltaTime), + GetCornerPoints(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Value, + LocalOffsetTarget = c.Key, + }); + } + } + else if (info.Target.Collider is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + List> contacts = new List>(); + GeometryUtils.CapsuleConvexPolygonContactInfo(A, B, b.Radius, + GetCornerPoints(deltaTime), GetEdges(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Value, + LocalOffsetTarget = c.Key, + }); + } + } + else if (info.Target.Collider is TileCollider) + { + TileCollisionUtils.GetPolygonTileContactInfo(GetAABB(deltaTime), ParentObject.RigidBody.CentroidWorldSpace, + GetEdges(deltaTime), GetCornerPoints(deltaTime), ParentObject, info.Target, deltaTime, collisionEvents); + } + } + + public override AABB GetAABB(float deltaTime) + { + var points = GetCornerPoints(deltaTime); + Vector2 minPoint = points[0]; + Vector2 maxPoint = points[0]; + for (int i = 1; i < points.Count; i++) + { + minPoint.X = Math.Min(points[i].X, minPoint.X); + minPoint.Y = Math.Min(points[i].Y, minPoint.Y); + maxPoint.X = Math.Max(points[i].X, maxPoint.X); + maxPoint.Y = Math.Max(points[i].Y, maxPoint.Y); + } + return new AABB() { MaxPoint = maxPoint, MinPoint = minPoint }; + } + } +} + + +// float maxDepth = -1; +// if (edgeBelongsToA) +// { +// for (int i = 0; i < cornersB.Count; i++) +// { +// if (IsInsideEdge(cornersB[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersB[i], keyEdge._pA, keyEdge._pB); +// if (d > maxDepth) +// { +// maxDepth = d; +// } +// } +// } +// Debug.Assert(maxDepth >= 0); +// for (int i = 0; i < cornersB.Count; i++) +// { +// if (IsInsideEdge(cornersB[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersB[i], keyEdge._pA, keyEdge._pB); +// if (d == maxDepth) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = 0, +// Source = this.ParentObject, +// Target = b.ParentObject, +// LocalOffsetSrc = Utils.ProjectPointOnSegment(cornersB[i], keyEdge._pA, keyEdge._pB) +// - ParentObject.RigidBody.CentroidWorldSpace, +// LocalOffsetTarget = cornersB[i] - b.ParentObject.RigidBody.CentroidWorldSpace, +// Normal = normal, +// Position = Vector2.Zero, +// Depth = depth +// }); +// } +// } +// } + +// } +// else +// { +// for (int i = 0; i < cornersA.Count; i++) +// { +// if (IsInsideEdge(cornersA[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersA[i], keyEdge._pA, keyEdge._pB); +// if (d > maxDepth) +// { +// maxDepth = d; +// } +// } +// } +// Debug.Assert(maxDepth >= 0); +// for (int i = 0; i < cornersA.Count; i++) +// { +// if (IsInsideEdge(cornersA[i], keyEdge)) +// { +// float d = Utils.PointDistance2ToSegment(cornersA[i], keyEdge._pA, keyEdge._pB); +// if (d == maxDepth) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = 0, +// Source = b.ParentObject, +// Target = this.ParentObject, +// LocalOffsetSrc = Utils.ProjectPointOnSegment(cornersA[i], keyEdge._pA, keyEdge._pB) +// - b.ParentObject.RigidBody.CentroidWorldSpace, +// LocalOffsetTarget = cornersA[i] - ParentObject.RigidBody.CentroidWorldSpace, +// Normal = -normal, +// Position = Vector2.Zero, +// Depth = depth +// }); +// } +// } +// } +// } +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = 0, +// Source = this.ParentObject, +// Target = b.ParentObject, +// LocalOffsetSrc = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), +// LocalOffsetTarget = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), +// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), +// Position = curPoint, +// }); + +// AABBCollider b = (AABBCollider)other; +// double l = 0, r = deltaTime; +// double ans = r; +// while (r - l > deltaTime * 1e-2) +// { +// double midTime = (r + l) / 2.0; +// if (IsCollideWith(b, (float)midTime)) +// { +// ans = Math.Min(ans, midTime); +// r = midTime; +// } +// else +// { +// l = midTime; +// } +// } + +// double t = Math.Max(0, ans); +// var curPointsA = GetCornerPoints((float)ans); +// var curEdgesA = GetEdges((float)ans); +// var curPointsB = b.GetCornerPoints((float)ans); +// var curEdgesB = b.GetEdges((float)ans); + +//// Vertex/Edge collision, A to B +// foreach (var curPoint in curPointsA) +// { +// bool isInside = true; +// Edge2D closestEdge = null; +// float closestDistance = float.PositiveInfinity; +// foreach(var curEdge in curEdgesB) +// { +// if (!IsInsideEdge(curPoint, curEdge)) +// { +// isInside = false; +// break; +// } +// float dist = Utils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); +// if (dist < closestDistance) +// { +// closestDistance = dist; +// closestEdge = curEdge; +// } +// } + +// if(isInside) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = (float)(t), +// Source = this.ParentObject, +// Target = b.ParentObject, +// LocalOffsetSrc = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), +// LocalOffsetTarget = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), +// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), +// Position = curPoint, + +// }); +// } + +// } + +//// Vertex/Edge collision, B to A +// foreach (var curPoint in curPointsB) +// { +// bool isInside = true; +// Edge2D closestEdge = null; +// float closestDistance = float.PositiveInfinity; +// foreach (var curEdge in curEdgesA) +// { +// if (!IsInsideEdge(curPoint, curEdge)) +// { +// isInside = false; +// break; +// } +// float dist = Utils.PointDistance2ToSegment(curPoint, curEdge._pA, curEdge._pB); +// if (dist < closestDistance) +// { +// closestDistance = dist; +// closestEdge = curEdge; +// } +// } + +// if (isInside) +// { +// collisionEvents.Add(new CollisionEvent2D() +// { +// Time = (float)(t), +// Source = b.ParentObject, +// Target = this.ParentObject, +// LocalOffsetSrc = curPoint - (b.ParentObject.RigidBody.CentroidWorldSpace + b.ParentObject.RigidBody.LinearVelocity * (float)ans), +// LocalOffsetTarget = curPoint - (ParentObject.RigidBody.CentroidWorldSpace + ParentObject.RigidBody.LinearVelocity * (float)ans), +// Normal = -Vector2.Normalize(Utils.Rotate90(closestEdge._pB - closestEdge._pA)), +// Position = curPoint, +// }); +// } + +// } \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs new file mode 100644 index 000000000..4635ece58 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs @@ -0,0 +1,261 @@ +using Everglow.Commons.Physics.PBEngine.GameInteraction; +using Everglow.Commons.Utilities; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders +{ + internal class CapsuleCollider : Collider2D + { + private float _length; + private float _radius; + + public float Length + { + get => _length; + set => _length = value; + } + + public float Radius + { + get => _radius; + set => _radius = value; + } + + public CapsuleCollider(float length, float radius) + { + _length = length; + _radius = radius; + } + + public override AABB GetAABB(float deltaTime) + { + var M = Matrix2x2.CreateRotationMatrix(ParentObject.OldRotation + ParentObject.RigidBody.AngularVelocity * deltaTime); + Vector2 P1 = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime + + M.Multiply(new Vector2(_length / 2, 0)); + Vector2 P2 = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime + + M.Multiply(new Vector2(-_length / 2, 0)); + + if (P1.X > P2.X) + { + (P2.X, P1.X) = (P1.X, P2.X); + } + if (P1.Y > P2.Y) + { + (P2.Y, P1.Y) = (P1.Y, P2.Y); + } + return new AABB() + { + MinPoint = P1 - new Vector2(_radius, _radius), + MaxPoint = P2 + new Vector2(_radius, _radius), + }; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) + { + collisionEvents = new List(); + if (info.Target.Collider is SphereCollider) + { + var b = (SphereCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + var contacts = new List>(); + GeometryUtils.SphereCapsuleContactInfo( + b.ParentObject.RigidBody.CentroidWorldSpace, + b.Radius, A, B, _radius, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Value, + LocalOffsetTarget = c.Key, + }); + } + } + else if (info.Target.Collider is CapsuleCollider) + { + var b = (CapsuleCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + b.GetSegment(deltaTime, out Vector2 A2, out Vector2 B2); + var contacts = new List>(); + GeometryUtils.CapsuleCapsuleContactInfo(A, B, _radius, A2, B2, b._radius, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is BoxCollider) + { + var b = (BoxCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + var contacts = new List>(); + GeometryUtils.CapsuleConvexPolygonContactInfo(A, B, _radius, + b.GetCornerPoints(deltaTime), b.GetEdges(deltaTime), b.ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is TileCollider) + { + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + GameInteraction.TileCollisionUtils.GetCapsuleTileContactInfo(GetAABB(deltaTime), A, B, _radius, ParentObject, info.Target, deltaTime, collisionEvents); + } + } + + public override List GetWireFrameWires() + { + var lines = new List(); + lines.Add(new Vector2(_length / 2, _radius)); + lines.Add(new Vector2(-_length / 2, _radius)); + + float da = MathHelper.Pi / 32; + var leftCenter = new Vector2(-_length / 2, 0); + for (int i = 0; i < 32; i++) + { + lines.Add(leftCenter + (i * da + MathHelper.PiOver2).ToRotationVector2() * _radius); + lines.Add(leftCenter + ((i + 1) * da + MathHelper.PiOver2).ToRotationVector2() * _radius); + } + lines.Add(new Vector2(-_length / 2, -_radius)); + lines.Add(new Vector2(_length / 2, -_radius)); + var rightCenter = new Vector2(_length / 2, 0); + for (int i = 0; i < 32; i++) + { + lines.Add(rightCenter + (i * da - MathHelper.PiOver2).ToRotationVector2() * _radius); + lines.Add(rightCenter + ((i + 1) * da - MathHelper.PiOver2).ToRotationVector2() * _radius); + } + lines.Add(Vector2.Zero); + lines.Add(new Vector2(_length / 2 + _radius, 0)); + return lines; + } + + public override double InertiaTensor(float mass) + { + float massOfBox = mass * 2 * _radius * _length / (2 * _radius * _length + MathHelper.Pi * _radius * _radius); + float massOfSphere = mass - massOfBox; + float moi_box = massOfBox / 12.0f * (_length * _length + 4 * _radius * _radius); + float moi_sphere = 0.5f * massOfSphere * _radius * _radius; + float d1 = 3.0f / 8.0f * _radius; + float moi_hsphere = 0.5f * moi_sphere - 0.5f * massOfSphere * d1 * d1; + float d2 = _length / 2 + d1; + moi_hsphere += 0.5f * massOfSphere * d2 * d2; + return moi_box + 2 * moi_hsphere; + } + + public void GetSegment(float dt, out Vector2 segA, out Vector2 segB) + { + var M = ParentObject.CachedRotationalMatrix; + var pos = ParentObject.Position; + segA = pos + M.Multiply(new Vector2(_length / 2, 0)); + segB = pos + M.Multiply(new Vector2(-_length / 2, 0)); + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + info = default; + if (other is SphereCollider) + { + var b = (SphereCollider)other; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.SphereCapsuleCollisionInfo(b.ParentObject.RigidBody.CentroidWorldSpace, b.Radius, + A, B, _radius, out float depth, out Vector2 normal)) + { + info.Source = ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = -normal; + return true; + } + } + else if (other is CapsuleCollider) + { + var b = (CapsuleCollider)other; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + b.GetSegment(deltaTime, out Vector2 A2, out Vector2 B2); + if (GeometryUtils.CapsuleCapsuleCollisionInfo(A, B, _radius, A2, B2, b._radius, + out float depth, out Vector2 normal)) + { + info.Source = ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is BoxCollider) + { + var b = (BoxCollider)other; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(A, B, _radius, b.GetCornerPoints(deltaTime), + b.GetEdges(deltaTime), + out float depth, out Vector2 normal)) + { + info.Source = ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is TileCollider) + { + float depth; + Vector2 normal; + GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GameInteraction.TileCollisionUtils.GetCapsuleTileCollisionInfo(GetAABB(deltaTime), A, B, _radius, out depth, out normal)) + { + info.Source = ParentObject; + info.Target = other.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = normal; + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs new file mode 100644 index 000000000..80a376afe --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs @@ -0,0 +1,33 @@ +namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders +{ + /// + /// 碰撞组件的基类 + /// + public abstract class Collider2D + { + public PhysicsObject ParentObject + { + get => _bindObject; + set => _bindObject = value; + } + + protected PhysicsObject _bindObject; + + public abstract double InertiaTensor(float mass); + + public abstract List GetWireFrameWires(); + + // public abstract List GetCollisionEvents(Collider2D other, float deltaTime); + public abstract bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info); + + public abstract void GetContactInfo(in CollisionInfo info, float deltaTime, + out List collisionEvents); + + // public abstract List GetContactInfo(CollisionEvent2D e, float deltaTime); + public abstract AABB GetAABB(float deltaTime); + + public Collider2D() + { + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs new file mode 100644 index 000000000..6f98e3b25 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs @@ -0,0 +1,241 @@ +using Everglow.Commons.Physics.PBEngine.GameInteraction; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders +{ + /// + /// 圆形碰撞体 + /// + public class SphereCollider : Collider2D + { + public float Radius + { + get => _radius; set => _radius = value; + } + + private float _radius; + + public SphereCollider(float radius) + { + _radius = radius; + } + + public override AABB GetAABB(float deltaTime) + { + var center = ParentObject.OldPosition + ParentObject.RigidBody.LinearVelocity * deltaTime; + return new AABB() + { + MinPoint = center - new Vector2(_radius, _radius), + MaxPoint = center + new Vector2(_radius, _radius), + }; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) + { + collisionEvents = new List(); + if (info.Target.Collider is SphereCollider) + { + SphereCollider b = (SphereCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + + var unit = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.Zero); + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = -unit * _radius, + LocalOffsetTarget = unit * b.Radius, + }); + } + else if (info.Target.Collider is BoxCollider) + { + BoxCollider b = (BoxCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = info.Source, + Target = info.Target, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(info.Normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -info.Normal : info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + + List> contacts = new List>(); + GeometryUtils.SphereConvexPolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, b.GetEdges(deltaTime), + b.GetCornerPoints(deltaTime), b.ParentObject.RigidBody.CentroidWorldSpace, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + else if (info.Target.Collider is TileCollider) + { + TileCollisionUtils.GetSphereTileContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, + ParentObject, info.Target, deltaTime, collisionEvents); + } + else if (info.Target.Collider is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)info.Target.Collider; + var e = new CollisionEvent2D() + { + Time = info.Time, + Source = this.ParentObject, + Target = b.ParentObject, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = info.Normal, + Position = Vector2.Zero, + Depth = info.Depth, + }; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + List> contacts = new List>(); + GeometryUtils.SphereCapsuleContactInfo( + ParentObject.RigidBody.CentroidWorldSpace, + _radius, A, B, b.Radius, contacts); + foreach (var c in contacts) + { + collisionEvents.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + } + + // public override List GetContactInfo(CollisionEvent2D e, float deltaTime) + // { + // List events = new List(); + // if (e.Target.Collider is SphereCollider) + // { + // SphereCollider b = (SphereCollider)e.Target.Collider; + // var unit = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.Zero); + // events.Add(new CollisionEvent2D(e) + // { + // LocalOffsetSrc = -unit * _radius, + // LocalOffsetTarget = unit * b.Radius, + // }); + // } + // else if (e.Target.Collider is BoxCollider) + // { + // BoxCollider b = (BoxCollider)e.Target.Collider; + // List edgesB = b.GetEdges(deltaTime); + // List cornersB = b.GetCornerPoints(deltaTime); + // List> contacts = new List>(); + // Utils.CirclePolygonContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, edgesB, + // cornersB, b.ParentObject.RigidBody.CentroidWorldSpace, contacts); + // foreach (var c in contacts) + // { + // events.Add(new CollisionEvent2D(e) + // { + // LocalOffsetSrc = c.Key, + // LocalOffsetTarget = c.Value, + // }); + // } + // } + // else if (e.Target.Collider is TileCollider) + // { + // TileCollisionUtils.GetSphereTileContactInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, e, events); + // } + // return events; + // } + public override List GetWireFrameWires() + { + List lines = new List(); + float da = MathHelper.TwoPi / 64; + for (int i = 0; i < 64; i++) + { + lines.Add((i * da).ToRotationVector2() * _radius); + lines.Add(((i + 1) * da).ToRotationVector2() * _radius); + } + lines.Add(Vector2.Zero); + lines.Add(ParentObject.CachedRotationalMatrix.Multiply(new Vector2(_radius, 0))); + return lines; + } + + public override double InertiaTensor(float mass) + { + return 0.5 * mass * _radius * _radius; + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + info = default(CollisionInfo); + if (other is SphereCollider) + { + SphereCollider b = (SphereCollider)other; + float d = Vector2.DistanceSquared(ParentObject.RigidBody.CentroidWorldSpace, b.ParentObject.RigidBody.CentroidWorldSpace); + if (d < (_radius + b._radius) * (_radius + b._radius)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = _radius + b._radius - (float)Math.Sqrt(d); + info.Normal = (ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace).SafeNormalize(Vector2.One); + return true; + } + } + else if (other is BoxCollider) + { + BoxCollider b = (BoxCollider)other; + float depth; + Vector2 normal; + if (GeometryUtils.SphereConvexPolygonCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, + b.GetEdges(deltaTime), b.GetCornerPoints(deltaTime), out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = Vector2.Dot(normal, ParentObject.RigidBody.CentroidWorldSpace - b.ParentObject.RigidBody.CentroidWorldSpace) < 0 ? -normal : normal; + return true; + } + } + else if (other is TileCollider) + { + float depth; + Vector2 normal; + if (TileCollisionUtils.GetSphereTileCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, _radius, + ParentObject, info.Target, out depth, out normal)) + { + info.Source = this.ParentObject; + info.Target = other.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = normal; + return true; + } + } + else if (other is CapsuleCollider) + { + CapsuleCollider b = (CapsuleCollider)other; + b.GetSegment(deltaTime, out Vector2 A, out Vector2 B); + if (GeometryUtils.SphereCapsuleCollisionInfo(ParentObject.RigidBody.CentroidWorldSpace, Radius, + A, B, b.Radius, out float depth, out Vector2 normal)) + { + info.Source = this.ParentObject; + info.Target = b.ParentObject; + info.Time = deltaTime; + info.Depth = depth; + info.Normal = normal; + return true; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionEvent2D.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionEvent2D.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionEvent2D.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionInfo.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionInfo.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/CollisionInfo.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionInfo.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/Edge2D.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Edge2D.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/Edge2D.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/Edge2D.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/ImpluseEntry.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/ImpluseEntry.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/ImpluseEntry.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/ImpluseEntry.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Collision/MovementType.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/MovementType.cs similarity index 71% rename from Sources/Everglow.Core/Physics/PBEngine/Collision/MovementType.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/MovementType.cs index fcb1f8c5e..f249d49da 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/Collision/MovementType.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/MovementType.cs @@ -1,31 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Everglow.Commons.Physics.PBEngine.Collision { /// /// 刚体的移动模式,用于区分模拟方法以及提升性能 /// - public enum MovementType - { + public enum MovementType + { /// /// 静态物体,无法移动、转向 /// - Static, + Static, + /// /// 动态物体,但是拥有无限质量,动作无法被其他动态物体影响 /// - Kinematic, + Kinematic, + /// /// 玩家物体,动态,具有质量但是不可旋转 /// - Player, + Player, + /// /// 动态物体,可以任意移动和旋转,可以被任何物体影响 /// - Dynamic - } -} + Dynamic, + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs b/Sources/Everglow.Function/Physics/PBEngine/Constrains/Constrain.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Constrains/Constrain.cs rename to Sources/Everglow.Function/Physics/PBEngine/Constrains/Constrain.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs b/Sources/Everglow.Function/Physics/PBEngine/Constrains/JointConstrain.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Constrains/JointConstrain.cs rename to Sources/Everglow.Function/Physics/PBEngine/Constrains/JointConstrain.cs diff --git a/Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs b/Sources/Everglow.Function/Physics/PBEngine/Constrains/SpringConstrain.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/Constrains/SpringConstrain.cs rename to Sources/Everglow.Function/Physics/PBEngine/Constrains/SpringConstrain.cs diff --git a/Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs b/Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs new file mode 100644 index 000000000..df4416c9c --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs @@ -0,0 +1,40 @@ +using Everglow.Commons.Enums; +using Everglow.Commons.Interfaces; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.VFX.CommonVFXDusts; +using Everglow.Commons.VFX; +using Everglow.Commons.VFX.Pipelines; + +namespace Everglow.Commons.Physics.PBEngine; + +/// +/// Custom PhysicsObject, allow modifying Draw(). +/// +public abstract class CustomPhysicsObject : PhysicsObject, IVisual +{ + public virtual bool Active { get; set; } = true; + + public virtual bool Visible { get; set; } = true; + + public virtual CodeLayer DrawLayer { get; } + + public CustomPhysicsObject(Collider2D collider, RigidBody2D rigidBody) + : base(collider, rigidBody) + { + Collider = collider; + RigidBody = rigidBody; + } + + public virtual void Draw() + { + } + + public virtual void Kill() + { + Active = false; + } + + public virtual void Update() + { + } +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollider.cs b/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollider.cs new file mode 100644 index 000000000..d66aee6ee --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollider.cs @@ -0,0 +1,48 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; + +namespace Everglow.Commons.Physics.PBEngine.GameInteraction +{ + /// + /// 与TR世界地形碰撞的碰撞体 + /// + public class TileCollider : Collider2D + { + public override AABB GetAABB(float deltaTime) + { + return new AABB() + { + MinPoint = new Vector2(float.NegativeInfinity, float.NegativeInfinity), + MaxPoint = new Vector2(float.PositiveInfinity, 0), + }; + } + + public override void GetContactInfo(in CollisionInfo info, float deltaTime, out List collisionEvents) + { + // Leave empty since static object will not serve as collision source + collisionEvents = new List(); + } + + // public override List GetContactInfo(CollisionEvent2D e, float deltaTime) + // { + // return new List(); + // // Leave empty since static object will not serve as collision source + // } + public override List GetWireFrameWires() + { + return new List(); + } + + public override double InertiaTensor(float mass) + { + return 1; + } + + public override bool TestCollisionCondition(Collider2D other, float deltaTime, out CollisionInfo info) + { + // Leave empty since static object will not serve as collision source + info = default(CollisionInfo); + return false; + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs b/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs new file mode 100644 index 000000000..022c28b6e --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs @@ -0,0 +1,409 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine.GameInteraction +{ + /// + /// 与TR地形进行碰撞检测的函数 + /// + public class TileCollisionUtils + { + public static List GenerateHalfBrick(int x, int y) + { + var slope = Main.tile[x, y].Slope; + if (slope == Terraria.ID.SlopeType.Solid) + { + if (Main.tile[x, y].IsHalfBlock) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 8 ), + new Vector2(x * 16, -y * 16 - 8), + }; + } + else + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16), + }; + } + } + else if (slope == Terraria.ID.SlopeType.SlopeDownLeft) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16, -y * 16), + }; + } + else if (slope == Terraria.ID.SlopeType.SlopeDownRight) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + }; + } + else if (slope == Terraria.ID.SlopeType.SlopeUpLeft) + { + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16), + }; + } + else if (slope == Terraria.ID.SlopeType.SlopeUpRight) + { + return new List() + { + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16), + }; + } + return new List() + { + new Vector2(x * 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 - 16), + new Vector2(x * 16 + 16, -y * 16 ), + new Vector2(x * 16, -y * 16), + }; + } + + public static bool GetPolygonTileCollisionInfo(AABB boundingBox, Vector2 polyCenter, List edges, List corners, + out float depth1, out Vector2 normal1) + { + bool collided = false; + float maxDepth = 0; + Vector2 maxNormal = Vector2.Zero; + int minX = (int)Math.Floor(boundingBox.MinPoint.X / 16); + int maxX = (int)Math.Ceiling(boundingBox.MaxPoint.X / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edgesTile = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edgesTile.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.ConvexPolygonPolygonCollisionInfo(edges, corners, edgesTile, localPoints, + out depth, out normal)) + { + depth1 = 0; + normal1 = Vector2.Zero; + return true; + + if (depth > maxDepth) + { + maxDepth = depth; + maxNormal = Vector2.Dot(maxNormal, polyCenter - centerTile) < 0 ? -normal : normal; + } + } + } + } + } + depth1 = 0; + normal1 = maxNormal; + return collided; + } + + public static void GetPolygonTileContactInfo(AABB boundingBox, Vector2 polyCenter, List edges, + List corners, + PhysicsObject A, PhysicsObject B, + float deltaTime, + List outputList) + { + int minX = (int)Math.Floor(boundingBox.MinPoint.X / 16); + int maxX = (int)Math.Ceiling(boundingBox.MaxPoint.X / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edgesTile = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edgesTile.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + + var boxCollider = A.Collider as BoxCollider; + float depth; + Vector2 normal; + if (GeometryUtils.ConvexPolygonPolygonCollisionInfo( + edges, + corners, edgesTile, localPoints, + out depth, out normal)) + { + var e = new CollisionEvent2D() + { + Time = 0, + Source = A, + Target = B, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(normal, polyCenter - centerTile) < 0 ? -normal : normal, + Position = Vector2.Zero, + Depth = depth, + }; + float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + float weightB = e.Target.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); + e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); + + List> contacts = new List>(); + GeometryUtils.ConvexPolygonPolygonContactInfo( + boxCollider.GetEdges(deltaTime), + boxCollider.GetCornerPoints(deltaTime), edgesTile, localPoints.ToList(), polyCenter, + centerTile, contacts); + foreach (var c in contacts) + { + outputList.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + } + } + } + } + + public static bool GetSphereTileCollisionInfo(Vector2 center, float radius, PhysicsObject A, PhysicsObject B, + out float depth1, out Vector2 normal1) + { + bool collided = false; + float maxDepth = 0; + Vector2 maxNormal = Vector2.Zero; + int minX = (int)Math.Floor((center.X - radius) / 16); + int maxX = (int)Math.Ceiling((center.X + radius) / 16); + int minY = (int)Math.Floor((-center.Y - radius) / 16); + int maxY = (int)Math.Ceiling((-center.Y + radius) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.SphereConvexPolygonCollisionInfo(center, radius, edges, localPoints.ToList(), out depth, out normal)) + { + collided = true; + if (depth > maxDepth) + { + maxDepth = depth; + maxNormal = Vector2.Dot(maxNormal, center - centerTile) < 0 ? -normal : normal; + } + } + } + } + } + depth1 = 0; + normal1 = maxNormal; + return collided; + } + + public static void GetSphereTileContactInfo(Vector2 center, float radius, PhysicsObject A, PhysicsObject B, + float deltaTime, + List outputList) + { + int minX = (int)Math.Floor((center.X - radius) / 16); + int maxX = (int)Math.Ceiling((center.X + radius) / 16); + int minY = (int)Math.Floor((-center.Y - radius) / 16); + int maxY = (int)Math.Ceiling((-center.Y + radius) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.SphereConvexPolygonCollisionInfo(A.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), out depth, out normal)) + { + var e = new CollisionEvent2D() + { + Time = 0, + Source = A, + Target = B, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(normal, center - centerTile) < 0 ? -normal : normal, + Position = Vector2.Zero, + Depth = depth, + }; + float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + float weightB = e.Target.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); + e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); + + List> contacts = new List>(); + GeometryUtils.SphereConvexPolygonContactInfo(e.Source.RigidBody.CentroidWorldSpace, radius, edges, localPoints.ToList(), centerTile, contacts); + foreach (var c in contacts) + { + outputList.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + } + } + } + } + + public static bool GetCapsuleTileCollisionInfo(AABB boundingBox, Vector2 segA, Vector2 segB, float radius, + out float depth1, out Vector2 normal1) + { + bool collided = false; + float maxDepth = 0; + Vector2 maxNormal = Vector2.Zero; + int minX = (int)Math.Floor(boundingBox.MinPoint.X / 16); + int maxX = (int)Math.Ceiling(boundingBox.MaxPoint.X / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(segA, segB, radius, localPoints.ToList(), edges, + out depth, out normal)) + { + collided = true; + + if (depth > maxDepth) + { + maxDepth = depth; + maxNormal = Vector2.Dot(maxNormal, boundingBox.Center - centerTile) < 0 ? -normal : normal; + } + } + } + } + } + depth1 = 0; + normal1 = maxNormal; + return collided; + } + + public static void GetCapsuleTileContactInfo(AABB boundingBox, Vector2 segA, Vector2 segB, float radius, + PhysicsObject A, PhysicsObject B, + float deltaTime, + List outputList) + { + int minX = (int)Math.Floor(boundingBox.MinPoint.X / 16); + int maxX = (int)Math.Ceiling(boundingBox.MaxPoint.X / 16); + int minY = (int)Math.Floor((-boundingBox.MaxPoint.Y) / 16); + int maxY = (int)Math.Ceiling((-boundingBox.MinPoint.Y) / 16); + + var capsule = A.Collider as CapsuleCollider; + + for (int x = Math.Max(minX, 0); x <= Math.Min(maxX, Main.maxTilesX - 1); x++) + { + for (int y = Math.Max(minY, 0); y <= Math.Min(maxY, Main.maxTilesY - 1); y++) + { + if (Main.tile[x, y] != null && Main.tile[x, y].HasTile && Main.tileSolid[Main.tile[x, y].TileType]) + { + var localPoints = GenerateHalfBrick(x, y); + List edges = new List(); + for (int i = 0; i < localPoints.Count; i++) + { + edges.Add(new Edge2D(localPoints[i], localPoints[(i + 1) % localPoints.Count])); + } + var centerTile = new Vector2(x * 16 + 8, -y * 16 - 8); + float depth; + Vector2 normal; + + if (GeometryUtils.CapsuleConvexPolygonCollisionInfo(segA, segB, radius, localPoints.ToList(), edges, + out depth, out normal)) + { + var e = new CollisionEvent2D() + { + Time = 0, + Source = A, + Target = B, + LocalOffsetSrc = Vector2.Zero, + LocalOffsetTarget = Vector2.Zero, + Normal = Vector2.Dot(normal, boundingBox.Center - centerTile) < 0 ? -normal : normal, + Position = Vector2.Zero, + Depth = depth, + }; + float weightA = e.Source.RigidBody.InvMass / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass); + float weightB = 1 - weightA; + e.Source.RigidBody.MoveBody(e.Normal * weightA * e.Depth, deltaTime); + e.Target.RigidBody.MoveBody(-e.Normal * weightB * e.Depth, deltaTime); + + List> contacts = new List>(); + capsule.GetSegment(deltaTime, out Vector2 segAt, out Vector2 segBt); + GeometryUtils.CapsuleConvexPolygonContactInfo(segAt, segBt, radius, localPoints.ToList(), edges, + centerTile, contacts); + foreach (var c in contacts) + { + outputList.Add(new CollisionEvent2D(e) + { + LocalOffsetSrc = c.Key, + LocalOffsetTarget = c.Value, + }); + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs b/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs similarity index 98% rename from Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs rename to Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs index aa1380446..49edd0da7 100644 --- a/Sources/Everglow.Core/Physics/PBEngine/GeometryUtils.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs @@ -249,7 +249,7 @@ public static bool SphereConvexPolygonCollisionInfo(Vector2 cSphere, float radiu foreach (var edge in edges) { float d = GeometryUtils.PointDistance2ToSegmentGetNearest(cSphere, edge._pA, edge._pB, out Vector2 pointOnSeg); - // ijһ߶ε棬ô˵ڶε + // 如果点在某一个线段的外面,那么就说明点在多边形的外面 if (d > maxDepth && GeometryUtils.Cross(edge._pB - edge._pA, cSphere - edge._pA) <= 0) { maxDepth = d; @@ -475,7 +475,7 @@ public static bool CapsuleCapsuleCollisionInfo(Vector2 segA1, Vector2 segB1, depth = radius1 + radius2 - (float)Math.Sqrt(d); normal = (pointA - pointB).SafeNormalize(Vector2.UnitX); - // ߷볯Sphereײ + // 法线方向必须朝向Sphere碰撞体 if (flipSign) { normal = -normal; @@ -522,7 +522,7 @@ public static void CapsuleCapsuleContactInfo(Vector2 segA1, Vector2 segB1, )); } - // ֹĸȫȡֻܱӴ + // 防止四个点全都被取,最多只能保留两个接触点 if ((d2 = PointDistance2ToSegmentGetNearest(segA1, segA2, segB2, out p)) <= d) { d = d2; @@ -574,7 +574,7 @@ public static bool CapsuleConvexPolygonCollisionInfo(Vector2 segA1, Vector2 segB normal = Vector2.Zero; { - // ߶εķߺ߶αǹؼ + // 中轴线段的法线和中轴线段本身都是关键分离轴 Edge2D edge = new Edge2D(segA1, segB1); Vector2 axis = edge.GetNormal(); var (amin, amax) = GetProjectedInterval(new List() { segA1, segB1 }, edge._pA, axis); diff --git a/Sources/Everglow.Function/Physics/PBEngine/PhysicsObject.cs b/Sources/Everglow.Function/Physics/PBEngine/PhysicsObject.cs new file mode 100644 index 000000000..0f6d79ec9 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/PhysicsObject.cs @@ -0,0 +1,200 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Utilities; +using Everglow.Commons.Vertex; + +namespace Everglow.Commons.Physics.PBEngine +{ + /// + /// 物理对象的容器,可以容纳Rigidbody,Collider等组件 + /// 此外,它还是物理模拟的主体 + /// + public class PhysicsObject + { + /// + /// 物理对象的几何组件,用于碰撞检测和质心计算等 + /// + public Collider2D Collider + { + get => _collider; + } + + /// + /// 物理对象的刚体物理组件,用于存储和模拟刚体数据 + /// + public RigidBody2D RigidBody + { + get => _rigidBody; + } + + /// + /// 物体的质心位置 + /// + public Vector2 Position + { + get => _rigidBody.CentroidWorldSpace; + set => _rigidBody.CentroidWorldSpace = value; + } + + /// + /// 物体的旋转,绕着质心 + /// + public float Rotation + { + get => _rotation; + set + { + _rotation = value; + _cachedRotationalMatrix = Matrix2x2.CreateRotationMatrix(_rotation); + } + } + + internal Matrix2x2 CachedRotationalMatrix + { + get => _cachedRotationalMatrix; + } + + private Matrix2x2 _cachedRotationalMatrix; + + /// + /// 物体在迭代开始的时候的位置 + /// + public Vector2 OldPosition + { + get => _oldPos; + set => _oldPos = value; + } + + /// + /// 物体在迭代开始的时候的旋转 + /// + public float OldRotation + { + get => _oldRot; + set => _oldRot = value; + } + + /// + /// 物体的GUID + /// + public int GUID + { + get => _guid; + set => _guid = value; + } + + /// + /// 物体的碰撞组名字 + /// + public string Tag + { + get => _tag; + set => _tag = value; + } + + public bool IsActive + { + get => _isActive; + set => _isActive = value; + } + + private Collider2D _collider; + private RigidBody2D _rigidBody; + private Vector2 _oldPos; + private float _rotation; + private float _oldRot; + private int _guid; + private string _tag; + private bool _isActive; + + public PhysicsObject(Collider2D collider, RigidBody2D rigidBody) + { + _collider = collider; + _rigidBody = rigidBody; + _tag = "Default"; + _isActive = true; + + if (_rigidBody == null) + { + _rigidBody = new RigidBody2D(1) + { + MovementType = MovementType.Static, + UseGravity = false, + }; + } + + Rotation = 0; + Position = Vector2.Zero; + + _collider.ParentObject = this; + _rigidBody.ParentObject = this; + } + + public void Initialize() + { + _rigidBody?.Initialize(); + } + + public void RecordOldState() + { + _oldPos = Position; + _oldRot = _rotation; + } + + public List<(Vector2, Color)> GetWireFrameWires() + { + List<(Vector2, Color)> wires_color = new List<(Vector2, Color)>(); + List wires = _collider.GetWireFrameWires(); + for (int i = 0; i < wires.Count; i++) + { + wires_color.Add((_cachedRotationalMatrix.Multiply(wires[i]) + Position, RigidBody.IsAwake ? Color.White : Color.Gray)); + } + return wires_color; + } + + ///// + ///// Override to add vertex only, no sprite batch. + ///// + ///// + //public virtual List Draw() + //{ + // return new List(); + //} + + public void CleanThisFrame(float deltaTime) + { + _rigidBody?.CleanInformationThisFrame(deltaTime); + } + + public void CleanThisSubstep(float deltaTime) + { + _rigidBody?.CleanInformationSubstep(deltaTime); + Rotation = MathHelper.WrapAngle(Rotation); + } + + public void Update(float deltaTime) + { + _rigidBody?.Update(deltaTime); + } + + public void ApplyGravity(Vector2 gravity) + { + if (!_isActive) + { + return; + } + if (_rigidBody != null) + { + if (_rigidBody.UseGravity) + { + _rigidBody.ApplyForce(gravity * _rigidBody.Mass); + } + } + } + + public Vector2 LocalToWorldPos(Vector2 localPos) + { + return _rigidBody.CentroidWorldSpace + _cachedRotationalMatrix.Multiply(localPos); + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs new file mode 100644 index 000000000..446c76735 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs @@ -0,0 +1,548 @@ +using Everglow.Commons.DataStructures; +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Constrains; +using Everglow.Commons.Vertex; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine +{ + /// + /// 用于物理模拟的世界,可以设置各种世界属性,添加和管理物理对象 + /// 其中最重要的模拟部分也是由这个类完成 + /// + public class PhysicsSimulation + { + public const double EPS = 1e-6; + + public int GAUSS_SEIDEL_ITERS + { + get => 8; + } + + private List _objects; + private LinkedList _objectFreeList; + private int _maximumAllocatedId; + private List _constrains; + private IBroadPhase _broadPhase; + private float _gravity; + + private Stopwatch _stopwatchPreIntegration; + private Stopwatch _stopwatchBroadPhase; + private Stopwatch _stopwatchNarrowPhase; + private readonly double _ticksPerMillisecond; + + private long _numBroadPhasePairs; + private long _numNarrowPhasePairs; + + /// + /// 用于性能检测,对各个阶段的耗时进行统计 + /// + public double MeasuredPreIntegrationTimeInMs + { + get => _stopwatchPreIntegration.ElapsedTicks / _ticksPerMillisecond; + } + + public double MeasuredBroadPhaseTimeInMs + { + get => _stopwatchBroadPhase.ElapsedTicks / _ticksPerMillisecond; + } + + public double MeasuredNarrowPhaseTimeInMs + { + get => _stopwatchNarrowPhase.ElapsedTicks / _ticksPerMillisecond; + } + + /// + /// 粗碰撞检测得到的碰撞对数量 + /// + public long NumOfBroadPhasePairs + { + get => _numBroadPhasePairs; + } + + /// + /// 细碰撞检测实际碰撞出现的物体对数量 + /// + public long NumOfNarrowPhasePairs + { + get => _numNarrowPhasePairs; + } + + public float Gravity + { + get => _gravity; + } + + public IBroadPhase BroadPhaseCollisionDetector + { + get => _broadPhase; + } + + public PhysicsSimulation() + : this(CollisionGraph.DefualtGraph) + { + } + + public PhysicsSimulation(CollisionGraph graph) + { + _objectFreeList = new LinkedList(); + _objects = new List(); + _maximumAllocatedId = 0; + _constrains = new List(); + _broadPhase = new HashGridMethod(graph); + + _gravity = 9.8f; + + _stopwatchPreIntegration = new Stopwatch(); + _stopwatchBroadPhase = new Stopwatch(); + _stopwatchNarrowPhase = new Stopwatch(); + + _ticksPerMillisecond = Stopwatch.Frequency / 1000.0; + + _numBroadPhasePairs = 0; + _numNarrowPhasePairs = 0; + } + + /// + /// 向世界添加物理对象,如果对象没有刚体部件,那么认为是静态物体 + /// + /// + public void AddPhysicsObject(PhysicsObject pobj) + { + pobj.GUID = AllocateGUID(); + if (pobj.GUID == _objects.Count) + { + _objects.Add(pobj); + } + else + { + _objects[pobj.GUID] = pobj; + } + pobj.Initialize(); + } + + /// + /// 向物理世界添加约束对象 + /// + /// + public void AddConstrain(Constrain constrain) + { + _constrains.Add(constrain); + } + + /// + /// Initialize physical object properties + /// + public void Initialize() + { + foreach (PhysicsObject pobj in _objects) + { + pobj.Initialize(); + } + } + + /// + /// 模拟一个完整步长,内部会分为多个子步长 + /// + /// + public void Update(float deltaTime) + { + _stopwatchPreIntegration.Reset(); + _stopwatchBroadPhase.Reset(); + _stopwatchNarrowPhase.Reset(); + CleanUpThisFrame(deltaTime); + float dt = deltaTime / GAUSS_SEIDEL_ITERS; + for (int i = 0; i < GAUSS_SEIDEL_ITERS; i++) + { + _stopwatchPreIntegration.Start(); + PreIntegration(dt); + _stopwatchPreIntegration.Stop(); + + Resolve(dt, _stopwatchBroadPhase, _stopwatchNarrowPhase); + } + + // foreach (var pobj in _objects) + // { + // Main.NewText(pobj.RigidBody.LinearVelocity); + // } + } + + public List<(Vector2, Color)> GetCurrentWireFrames() + { + List<(Vector2, Color)> result = new List<(Vector2, Color)>(); + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + + result.AddRange(pobj.GetWireFrameWires()); + } + foreach (Constrain joint in _constrains) + { + result.AddRange(joint.GetDrawMesh()); + } + return result; + } + + public List EmitVertices() + { + List result = new List(); + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + + result.AddRange(pobj.Draw()); + } + return result; + } + + //public List GetCustomPhysicsObjects() + //{ + // List result = new List(); + // foreach (PhysicsObject pobj in _objects) + // { + // if (!pobj.IsActive) + // { + // continue; + // } + // if(pobj is CustomPhysicsObject cobj) + // { + // result.Add(cobj); + // } + // } + // return result; + //} + + /// + /// 进行预积分,先模拟一个步长 + /// + /// + private void PreIntegration(float deltaTime) + { + CleanUpThisSubstep(deltaTime); + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + pobj.ApplyGravity(new Vector2(0, -_gravity)); + } + foreach (var constrain in _constrains) + { + constrain.ApplyForce(deltaTime); + } + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + pobj.RecordOldState(); + pobj.Update(deltaTime); + } + foreach (var constrain in _constrains) + { + constrain.Apply(deltaTime); + } + } + + /// + /// 解算所有物体的约束条件,并且做出响应 + /// + /// + /// + /// + private void Resolve(float deltaTime, Stopwatch broadPhase, Stopwatch narrowPhase) + { + broadPhase.Start(); + List activeObjects = _objects.Where(obj => obj.IsActive).ToList(); + _broadPhase.Prepare(activeObjects, deltaTime); + var pairs = _broadPhase.GetCollisionPairs(deltaTime); + _numBroadPhasePairs = pairs.Count; + broadPhase.Stop(); + + narrowPhase.Start(); + _numNarrowPhasePairs = 0; + List contacts = new List(); + foreach (var pair in pairs) + { + if (pair.Key.TestCollisionCondition(pair.Value, deltaTime, out CollisionInfo info)) + { + // 非线性投影:预先移开以获得contact points + float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + + info.Source.RigidBody.MoveBody(info.Normal * weightA * info.Depth, deltaTime); + info.Target.RigidBody.MoveBody(-info.Normal * weightB * info.Depth, deltaTime); + + List events; + pair.Key.GetContactInfo(info, deltaTime, out events); + contacts.AddRange(events); + + //// 非线性投影:回溯,计算实际投影后姿态 + // foreach (var e in events) + // { + // double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(e.LocalOffsetSrc), e.Normal); + // double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(e.LocalOffsetTarget), e.Normal); + // double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; + // double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; + // double effectiveMass = R1 + R2 + e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass; + + // double linearMoveA = info.Depth * e.Source.RigidBody.InvMass / effectiveMass; + // double linearMoveB = -info.Depth * e.Target.RigidBody.InvMass / effectiveMass; + // double angularMoveA = info.Depth * R1 / effectiveMass; + // double angularMoveB = -info.Depth * R2 / effectiveMass; + + // info.Source.RigidBody.MoveBody(info.Normal * (float)linearMoveA, deltaTime); + // info.Target.RigidBody.MoveBody(info.Normal * (float)linearMoveB, deltaTime); + + // double implusePerMoveA = GeometryUtils.Cross(e.LocalOffsetSrc, e.Normal) * e.Source.RigidBody.GlobalInverseInertiaTensor; + // double implusePerMoveB = GeometryUtils.Cross(e.LocalOffsetTarget, e.Normal) * e.Target.RigidBody.GlobalInverseInertiaTensor; + // if (R1 > 0) + // { + // info.Source.Rotation += (float)(angularMoveA * implusePerMoveA / R1); + // } + // if (R2 > 0) + // { + // info.Target.Rotation += (float)(angularMoveB * implusePerMoveB / R2); + // } + // } + + // ApplyConstrains(events, pair.Key.ParentObject, deltaTime); + _numNarrowPhasePairs++; + } + } + + contacts.Sort((a, b) => + { + return -a.Depth.CompareTo(b.Depth); + }); + + foreach (var contact in contacts) + { + float stiffness = Math.Max(0, (contact.Source.RigidBody.Restitution + contact.Target.RigidBody.Restitution) / 2); + SolveContact_Weak(contact, stiffness, true, deltaTime); + } + for (int i = 0; i < 16; i++) + { + bool stable = true; + foreach (var contact in contacts) + { + if (!SolveContact_Weak(contact, 0, false, deltaTime)) + { + stable = false; + } + } + if (stable) + { + break; + } + } + foreach (var contact in contacts) + { + SolveFriction_Weak(contact, deltaTime); + contact.Source.RigidBody.MatchAwakeState(contact.Target.RigidBody, deltaTime); + } + + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + pobj.RigidBody.CheckAwake(deltaTime); + } + narrowPhase.Stop(); + } + + private void SolveFriction_Weak(CollisionEvent2D e, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); + var vb = e.Target.RigidBody.LinearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + if (e.NormalVelOld == 0) + { + return; + } + float vrel_n = Vector2.Dot(va - vb, e.Normal); + Vector2 vt = (va - vb) - vrel_n * e.Normal; + + float vel_t = vt.Length(); + float friction = Math.Max(0, (e.Source.RigidBody.Friction + e.Target.RigidBody.Friction) / 2); + + vt = vt.SafeNormalize(Vector2.Zero); + if (vt.Length() == 0) + { + return; + } + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), vt); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), vt); + double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + + // * Utils.Cross(rb, e.Normal))), e.Normal); + double effectiveMass = e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass + R1 + R2; + + // (a × b) × c = (c • a)b - (c • b)a + double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); + Vector2 J = (float)J_n * vt; // + (float)J_t * va_t_unit; + + // var offset = e.Position - (_globalCentroid + ri); + e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); + } + + private bool SolveContact_Weak(CollisionEvent2D e, float restitution, bool initial, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); + var vb = e.Target.RigidBody.LinearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + float vrel_n = Vector2.Dot(va - vb, e.Normal); + + if (vrel_n >= 0) + { + return true; + } + + if (initial) + { + if (e.Source.RigidBody.MovementType == MovementType.Player) + { + e.Source.RigidBody.ContactNormals.Add(e.Normal); + } + if (e.Target.RigidBody.MovementType == MovementType.Player) + { + e.Target.RigidBody.ContactNormals.Add(-e.Normal); + } + } + + float vnew_n = restitution * Math.Max(-vrel_n, 0); + + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); + double R1 = rAdotN * rAdotN * e.Source.RigidBody.GlobalInverseInertiaTensor; + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; + double J_n = (vnew_n - vrel_n) / (e.Source.RigidBody.InvMass + e.Target.RigidBody.InvMass + R1 + R2); + Vector2 J = (float)J_n * e.Normal; // + (float)J_t * va_t_unit; + + e.NormalVelOld += (float)J_n; + + // var offset = e.Position - (_globalCentroid + ri); + e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); + + return vrel_n > -0.1f; + } + + private Dictionary>> GroupCollisions(List> pairs) + { + Dictionary>> groupedPairs = new Dictionary>>(); + UnionFind uf = new UnionFind(_objects.Count); + foreach (var pair in pairs) + { + uf.Union(pair.Key.ParentObject.GUID, pair.Value.ParentObject.GUID); + } + foreach (var pair in pairs) + { + int group = uf.Find(pair.Key.ParentObject.GUID); + if (groupedPairs.ContainsKey(group)) + { + groupedPairs[group].Add(pair); + } + else + { + groupedPairs.Add(group, new List>() { pair }); + } + } + return groupedPairs; + } + + private void ApplyConstrains(List events, PhysicsObject pobj, float deltaTime) + { + if (deltaTime == 0) + { + return; + } + pobj.RigidBody.RespondToEvents(events, deltaTime); + } + + private void CleanUpThisFrame(float deltaTime) + { + foreach (PhysicsObject pobj in _objects) + { + pobj.CleanThisFrame(deltaTime); + } + } + + private void CleanUpThisSubstep(float deltaTime) + { + foreach (PhysicsObject pobj in _objects) + { + if (!pobj.IsActive) + { + continue; + } + pobj.CleanThisSubstep(deltaTime); + } + } + + public void RemoveObject(PhysicsObject obj) + { + _objectFreeList.AddLast(obj.GUID); + obj.IsActive = false; + } + + private void RemoveObject(int GUID) + { + Debug.Assert(_objects[GUID].IsActive); + RemoveObject(_objects[GUID]); + } + + private List AllocateGUIDs(int size) + { + List results = new List(); + while (size > 0) + { + if (_objectFreeList.Count > 0) + { + results.Add(_objectFreeList.First()); + _objectFreeList.RemoveFirst(); + } + else + { + results.Add(_maximumAllocatedId++); + } + } + return results; + } + + private int AllocateGUID() + { + if (_objectFreeList.Count == 0) + { + return _maximumAllocatedId++; + } + int id = _objectFreeList.First(); + _objectFreeList.RemoveFirst(); + return id; + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs new file mode 100644 index 000000000..701fcc5be --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs @@ -0,0 +1,221 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Constrains; + +namespace Everglow.Commons.Physics.PBEngine.PlayGround.Constrain +{ + /// + /// 储存了一些测试约束的场景 + /// + public class JointPlayground + { + public static PhysicsSimulation SimpleJoint() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(500, 660); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -40), new Vector2(0, 64)); + world.AddConstrain(joint); + + return world; + } + + public static PhysicsSimulation SimpleJointMultiple() + { + var world = new PhysicsSimulation(); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(32, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(500, 600); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(500, 500); + dynamicBox1.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox1); + + var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -50), new Vector2(0, 50)); + world.AddConstrain(joint); + + var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -70), new Vector2(0, 70)); + world.AddConstrain(joint2); + + // var kPlane = new PhysicsObject( + // new BoxCollider(200, 32), null); + // kPlane.Position = new Vector2(512, 400); + // kPlane.Rotation = 0; + // kPlane.RigidBody.ApplyAngularVelocity(0.9f); + // kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; + // world.AddPhysicsObject(kPlane); + return world; + } + + public static PhysicsSimulation DynamicJoints() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(500, 500); + dynamicBox.Rotation = 0.2f; + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(500, 440); + dynamicBox1.Rotation = 0.3f; + world.AddPhysicsObject(dynamicBox1); + + var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -50), new Vector2(0, 50)); + world.AddConstrain(joint2); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(700, 32), null); + staticPlane1.Position = new Vector2(512, 300); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + return world; + } + + public static PhysicsSimulation NewtonPendulum() + { + var world = new PhysicsSimulation(); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(512, 32), null); + staticPlane1.Position = new Vector2(512, 800); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + for (int i = 0; i < 5; i++) + { + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + 64 * (i - 2), 600); + if(i == 4) + { + dynamicBox.Position += new Vector2(60, 60); + } + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + if (i == 0) + { + dynamicBox.RigidBody.LinearVelocity = new Vector2(-50, 0); + } + + var joint1 = new JointConstrain(staticPlane1, dynamicBox, new Vector2(64 * (i - 2), -16), new Vector2(0, 200)); + world.AddConstrain(joint1); + } + + // var dynamicBox1 = new PhysicsObject( + // new SphereCollider(32), new RigidBody2D(256)); + // dynamicBox1.Position = new Vector2(512, 584); + // dynamicBox1.Rotation = 0.0f; + // world.AddPhysicsObject(dynamicBox1); + + // var dynamicBox2 = new PhysicsObject( + // new SphereCollider(32), new RigidBody2D(256)); + // dynamicBox2.Position = new Vector2(576, 584); + // dynamicBox2.Rotation = 0.0f; + // world.AddPhysicsObject(dynamicBox2); + + // var joint2 = new Joint(staticPlane1, dynamicBox1, new Vector2(0, -16), new Vector2(0, 200)); + // world.AddConstrain(joint2); + + // var joint3 = new Joint(staticPlane1, dynamicBox2, new Vector2(64, -16), new Vector2(0, 200)); + // world.AddConstrain(joint3); + + return world; + } + + public static PhysicsSimulation SimpleSpring() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(300, 100); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); + world.AddConstrain(joint); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(400, 10); + dynamicBox1.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox1); + + var joint1 = new SpringConstrain(dynamicBox, dynamicBox1, 29f, 200); + world.AddConstrain(joint1); + + return world; + } + + public static PhysicsSimulation SpringTriganluar() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 64), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(300, 100); + dynamicBox.Rotation = 0.0f; + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(32, 32), null); + staticPlane1.Position = new Vector2(512, 700); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(32, 32), null); + staticPlane2.Position = new Vector2(212, 300); + staticPlane2.Rotation = 0f; + world.AddPhysicsObject(staticPlane2); + + var staticPlane3 = new PhysicsObject( + new BoxCollider(32, 32), null); + staticPlane3.Position = new Vector2(812, 300); + staticPlane3.Rotation = 0f; + world.AddPhysicsObject(staticPlane3); + + var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); + world.AddConstrain(joint); + var joint1 = new SpringConstrain(staticPlane2, dynamicBox, 29f, 200); + world.AddConstrain(joint1); + + var joint2 = new SpringConstrain(staticPlane3, dynamicBox, 29f, 200); + world.AddConstrain(joint2); + + return world; + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs new file mode 100644 index 000000000..2c39d0022 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs @@ -0,0 +1,341 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; + +namespace Everglow.Commons.Physics.PBEngine.PlayGround.Contact +{ + /// + /// 储存了一些物理引擎接触测试的场景 + /// + public class ContactPlayGround1 + { + public static PhysicsSimulation SimpleGround1() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + + return world; + } + + public static PhysicsSimulation InclinedGround() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0.22f; + world.AddPhysicsObject(staticPlane1); + + return world; + } + + public static PhysicsSimulation MultipleInclinedGround() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 900); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane1.Position = new Vector2(300, 700); + staticPlane1.Rotation = -0.32f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane2.Position = new Vector2(680, 200); + staticPlane2.Rotation = 0.32f; + world.AddPhysicsObject(staticPlane2); + + return world; + } + + public static PhysicsSimulation MultipleInclinedGround2() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 900); + world.AddPhysicsObject(dynamicBox); + + for (int i = 0; i < 5; i++) + { + var staticPlane1 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane1.Position = new Vector2(300, 700 - i * 200); + staticPlane1.Rotation = -0.32f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane2.Position = new Vector2(680, 700 - i * 200 - 100); + staticPlane2.Rotation = 0.32f; + world.AddPhysicsObject(staticPlane2); + } + return world; + } + + public static PhysicsSimulation MultipleInclinedGround3() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 20; i++) + { + var dynamicBox = new PhysicsObject( + new SphereCollider(16), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i % 10) * 40, 900 + i / 10 * 40); + world.AddPhysicsObject(dynamicBox); + } + + for (int i = 0; i < 5; i++) + { + var staticPlane1 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane1.Position = new Vector2(300, 700 - i * 250); + staticPlane1.Rotation = -0.38f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(500, 32), null); + staticPlane2.Position = new Vector2(680, 700 - i * 250 - 125); + staticPlane2.Rotation = 0.38f; + world.AddPhysicsObject(staticPlane2); + } + return world; + } + + public static PhysicsSimulation MultipleInclinedGroundSphere() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 20; i++) + { + var dynamicBall = new PhysicsObject( + new SphereCollider(16), new RigidBody2D(256)); + dynamicBall.Position = new Vector2(512, 800 + i * 40); + world.AddPhysicsObject(dynamicBall); + } + + var staticPlane1 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane1.Position = new Vector2(200, 400); + staticPlane1.Rotation = -0.78f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane2.Position = new Vector2(400, 400); + staticPlane2.Rotation = 0.78f; + world.AddPhysicsObject(staticPlane2); + + return world; + } + + public static PhysicsSimulation DoubleSlope() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(162, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(300, 600); + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(300, 760); + dynamicBox1.RigidBody.ApplyForce(new Vector2(100, 0)); + world.AddPhysicsObject(dynamicBox1); + + for (int i = 0; i < 100; i++) + { + var dynamicBox2 = new PhysicsObject( + new BoxCollider(55, 32), new RigidBody2D(256)); + dynamicBox2.Position = new Vector2(350, 700 + 36 * i); + dynamicBox2.RigidBody.ApplyForce(new Vector2(100, 0)); + world.AddPhysicsObject(dynamicBox2); + } + + var staticPlane1 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane1.Position = new Vector2(200, 400); + staticPlane1.Rotation = -0.78f; + world.AddPhysicsObject(staticPlane1); + + var staticPlane2 = new PhysicsObject( + new BoxCollider(400, 32), null); + staticPlane2.Position = new Vector2(400, 400); + staticPlane2.Rotation = 0.78f; + world.AddPhysicsObject(staticPlane2); + + return world; + } + + public static PhysicsSimulation SimpleKinematic() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 200; i++) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i % 10) * 35, 900 + i / 10 * 35); + world.AddPhysicsObject(dynamicBox); + } + + var kPlane = new PhysicsObject( + new BoxCollider(500, 32), null); + kPlane.Position = new Vector2(512, 512); + kPlane.Rotation = 0; + kPlane.RigidBody.ApplyAngularVelocity(0.9f); + kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; + world.AddPhysicsObject(kPlane); + + var sPlane = new PhysicsObject( + new BoxCollider(32, 600), null); + sPlane.Position = new Vector2(0, 512); + sPlane.Rotation = 0; + world.AddPhysicsObject(sPlane); + + var sPlane2 = new PhysicsObject( + new BoxCollider(32, 600), null); + sPlane2.Position = new Vector2(1024, 512); + sPlane2.Rotation = 0; + world.AddPhysicsObject(sPlane2); + + var sPlane1 = new PhysicsObject( + new BoxCollider(1024, 32), null); + sPlane1.Position = new Vector2(512, 0); + sPlane1.Rotation = 0; + world.AddPhysicsObject(sPlane1); + return world; + } + + public static PhysicsSimulation SimpleSphere() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation SlopeSphere() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 800); + world.AddPhysicsObject(dynamicBox); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(300, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0.3f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation SphereAndBox() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 10; i++) + { + var dynamicBox = new PhysicsObject( + new SphereCollider(32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i - 5) * 50, 800); + world.AddPhysicsObject(dynamicBox); + } + + for (int i = 0; i < 10; i++) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (i - 5) * 50, 700); + world.AddPhysicsObject(dynamicBox); + } + + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation Box2() + { + var world = new PhysicsSimulation(); + + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512, 700); + dynamicBox.RigidBody.Restitution = 0.3f; + world.AddPhysicsObject(dynamicBox); + + var dynamicBox1 = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox1.Position = new Vector2(512, 734); + dynamicBox1.RigidBody.Restitution = 0.3f; + world.AddPhysicsObject(dynamicBox1); + + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + + public static PhysicsSimulation BoxStack() + { + var world = new PhysicsSimulation(); + + for (int i = 0; i < 5; i++) + { + for (int j = 1; j <= i + 1; j++) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(32, 32), new RigidBody2D(256)); + dynamicBox.Position = new Vector2(512 + (j - 2) * 38 - 16 * i, 500 - i * 34); + dynamicBox.RigidBody.Restitution = 0.3f; + world.AddPhysicsObject(dynamicBox); + } + } + + var staticPlane1 = new PhysicsObject( + new BoxCollider(600, 32), null); + staticPlane1.Position = new Vector2(512, 200); + staticPlane1.Rotation = 0f; + world.AddPhysicsObject(staticPlane1); + return world; + } + } +} \ No newline at end of file diff --git a/Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs similarity index 100% rename from Sources/Everglow.Core/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs rename to Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs diff --git a/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs new file mode 100644 index 000000000..afc3682e6 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs @@ -0,0 +1,736 @@ +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Utilities; +using Terraria; + +namespace Everglow.Commons.Physics.PBEngine +{ + /// + /// 2D刚体物理组件,用于实际的刚体运动模拟和响应的模块 + /// + public class RigidBody2D + { + /// + /// 指向物理对象的指针 + /// + public PhysicsObject ParentObject + { + get => _bindObject; + set => _bindObject = value; + } + + /// + /// 世界坐标下的质心坐标(指物理世界 + /// + public Vector2 CentroidWorldSpace + { + get => _globalCentroid; + set => _globalCentroid = value; + } + + /// + /// 质量 + /// + public float Mass + { + get => _mass; + set => _mass = value; + } + + /// + /// 质量的倒数 + /// + public float InvMass + { + get => (MovementType == MovementType.Dynamic || MovementType == MovementType.Player) ? 1 / _mass : 0; + } + + /// + /// 世界坐标下惯性张量的倒数 + /// + public double GlobalInverseInertiaTensor + { + get => MovementType == MovementType.Dynamic ? _globalInverseInertiaTensor : 0; + } + + /// + /// 是否启用重力 + /// + public bool UseGravity + { + get; set; + } + + /// + /// 线速度 + /// + public Vector2 LinearVelocity + { + get => MovementType == MovementType.Static ? Vector2.Zero : _linearVelocity; + set => _linearVelocity = value; + } + + /// + /// 角速度 + /// + public float AngularVelocity + { + get => MovementType == MovementType.Static ? 0 : _angularVelocity; + set => _angularVelocity = value; + } + + /// + /// 移动模式 + /// + public MovementType MovementType + { + get; set; + } + + /// + /// 线速度的阻尼 + /// + public float Drag + { + get => _drag; + set => _drag = value; + } + + /// + /// 角速度的阻尼 + /// + public float AngularDrag + { + get => _angularDrag; + set => _angularDrag = value; + } + + /// + /// 碰撞弹出的系数 + /// + public float Restitution + { + get => _restitution; + set => _restitution = value; + } + + /// + /// 摩擦力系数 + /// + public float Friction + { + get => _friction; + set => _friction = value; + } + + public List TangetRelativeVelocity + { + get => _contactTangentVels; + } + + public List ContactNormals + { + get => _contactNormals; + } + + /// + /// 物体是否是唤醒状态,如果唤醒那么可以进行运动模拟,注意唤醒时会重置运动值 + /// + public bool IsAwake + { + get => _isAwake; + set + { + if (value && !_isAwake) + { + _motion = SleepEPS * 2; + } + else if (!value && _isAwake) + { + _linearVelocity = Vector2.Zero; + _angularVelocity = 0; + } + _isAwake = value; + } + } + + /// + /// 物体是否可以进入沉睡状态,用户能控制的物体应该设为false + /// + public bool CanSleep + { + get => _canSleep; + set => _canSleep = value; + } + + private double _localInverseInertiaTensor; + private double _globalInverseInertiaTensor; + + private Vector2 _globalCentroid; + + private Vector2 _linearVelocity; + private float _angularVelocity; + + private float _torque; + private Vector2 _force; + private float _mass; + private float _drag; + private float _angularDrag; + private float _restitution; + private float _friction; + + private List _contactTangentVels; + private List _contactNormals; + + private List _impluses; + + private bool _isAwake; + private bool _canSleep; + private double _motion; + + private PhysicsObject _bindObject; + + private const double SleepEPS = 1e-2; + + public RigidBody2D(float mass) + { + _mass = mass; + MovementType = MovementType.Dynamic; + UseGravity = true; + _isAwake = true; + _canSleep = true; + _linearVelocity = Vector2.Zero; + _angularVelocity = 0; + _drag = 0.06f; + _angularDrag = 0.06f; + _impluses = new List(); + _restitution = 0.5f; + _friction = 0.5f; + _contactTangentVels = new List(); + _contactNormals = new List(); + + _motion = SleepEPS * 2; + } + + private void CalculateMassCentroidAndMoI() + { + _globalInverseInertiaTensor = _localInverseInertiaTensor = 1 / _bindObject.Collider.InertiaTensor(_mass); + } + + public void Initialize() + { + CalculateMassCentroidAndMoI(); + } + + /// + /// 进行一次运动积分 + /// + /// + public void Update(float deltaTime) + { + if (!_isAwake || MovementType == MovementType.Static) + { + return; + } + + _angularVelocity += (float)(_globalInverseInertiaTensor * deltaTime * _torque); + _linearVelocity += deltaTime * _force / _mass; + + // _oldRot = _rotation; + + // _oldPos = _globalCentroid; + _globalCentroid += deltaTime * _linearVelocity; + _bindObject.Rotation += deltaTime * _angularVelocity; + + Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); + Debug.Assert(!float.IsNaN(_angularVelocity)); + + StabilizeBody(); + + // CollisionDetection(deltaTime); + // CollisionResponse(deltaTime); + } + + private void StabilizeBody() + { + if (Math.Abs(_linearVelocity.X) < PhysicsSimulation.EPS) + { + _linearVelocity.X = 0; + } + if (Math.Abs(_linearVelocity.Y) < PhysicsSimulation.EPS) + { + _linearVelocity.Y = 0; + } + + if (Math.Abs(_angularVelocity) < PhysicsSimulation.EPS) + { + _angularVelocity = 0; + } + } + + public void ApplyForce(Vector2 force) + { + if (MovementType == MovementType.Static) + { + return; + } + _force += force; + } + + public void CleanInformationSubstep(float deltaTime) + { + _torque = 0; + _force = Vector2.Zero; + _impluses.Clear(); + + // Main.NewText((_linearVelocity.LengthSquared() * _mass * 0.5f + _mass * 9.8f * _globalCentroid.Y).ToString("F1")); + } + + public void CleanInformationThisFrame(float deltaTime) + { + _linearVelocity *= 1 - _drag * _drag; + _angularVelocity *= 1 - _angularDrag * _angularDrag; + _contactTangentVels.Clear(); + _contactNormals.Clear(); + } + + public bool TryRespondTo2Events(List events, float deltaTime) + { + Debug.Assert(events.Count == 2); + Debug.Assert(events[0].Target == events[1].Target); + var target = events[0].Target; + + var ri0 = events[0].LocalOffsetSrc; + var rb0 = events[0].LocalOffsetTarget; + var n0 = events[0].Normal; + var va0 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); + var vb0 = events[0].Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); + float relv0_n = Vector2.Dot(va0 - vb0, n0); + if (relv0_n >= 0) + { + return false; + } + + var ri1 = events[1].LocalOffsetSrc; + var rb1 = events[1].LocalOffsetTarget; + var n1 = events[1].Normal; + var va1 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); + var vb1 = events[1].Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); + float relv1_n = Vector2.Dot(va1 - vb1, n1); + if (relv1_n >= 0) + { + return false; + } + Matrix2x2 matrix = new Matrix2x2() + { + [0, 0] = InvMass + events[0].Target.RigidBody.InvMass + + Vector2.Dot( + GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri0, n0))), n0) + + Vector2.Dot( + GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor + * GeometryUtils.Cross(rb0, n0))), n0), + [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot( + GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri1, n1))), n0), + + [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot( + GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri0, n0))), n1), + [1, 1] = InvMass + events[1].Target.RigidBody.InvMass + + Vector2.Dot( + GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + * GeometryUtils.Cross(ri1, n1))), n1) + + Vector2.Dot( + GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor + * GeometryUtils.Cross(rb1, n1))), n1), + }; + float C = 0.4f; + var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); + if (!J.HasNaNs()) + { + AddImpluse(J.X * events[0].Normal, ri0, target.RigidBody, -J.X * events[0].Normal, rb0); + AddImpluse(J.Y * events[1].Normal, ri1, target.RigidBody, -J.Y * events[1].Normal, rb1); + } + return true; + } + + public void ApplyAngularVelocity(float w) + { + _angularVelocity += w; + } + + private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); + var va = _linearVelocity + v2; + var vb = e.Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); + var bias = 0.1f * Math.Max(e.Depth - 0.02f, 0) / deltaTime; + + float va_n = Vector2.Dot(va - vb, e.Normal); + if (MovementType == MovementType.Player) + { + _contactNormals.Add(e.Normal); + } + if (e.Target.RigidBody.MovementType == MovementType.Player) + { + e.Target.RigidBody._contactNormals.Add(-e.Normal); + } + if (va_n >= 0) + { + e.NormalVelOld = 0; + return; + } + float stiffness = Math.Max(0, (_restitution + e.Target.RigidBody.Restitution) / 2); + float vnew_n = stiffness * Math.Max(-va_n - 20 * deltaTime, 0); + + // (a × b) × c = (c • a)b - (c • b)a + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); + double R1 = rAdotN * rAdotN * GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + + // * Utils.Cross(rb, e.Normal))), e.Normal); + double J_n = (vnew_n - va_n) / (InvMass + e.Target.RigidBody.InvMass + R1 + R2); + Vector2 J = (float)J_n * e.Normal / count; // + (float)J_t * va_t_unit; + e.NormalVelOld = (float)J_n; + + // var offset = e.Position - (_globalCentroid + ri); + AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); + } + + private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime) + { + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); + var va = _linearVelocity + v2; + var vb = e.Target.RigidBody._linearVelocity + + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); + + if (e.NormalVelOld == 0) + { + return; + } + + float vel_n = Vector2.Dot(va - vb, e.Normal); + Vector2 vt = (va - vb) - vel_n * e.Normal; + + float vel_t = vt.Length(); + float friction = Math.Max(0, (_friction + e.Target.RigidBody.Friction) / 2); + + vt = vt.SafeNormalize(Vector2.Zero); + if (vt.Length() == 0) + { + return; + } + + _contactTangentVels.Add(-Vector2.Dot(vt, vb) * vt); + e.Target.RigidBody._contactTangentVels.Add(-Vector2.Dot(vt, va) * vt); + if (friction == 0) + { + return; + } + + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), vt); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), vt); + double R1 = rAdotN * rAdotN * GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + + // * Utils.Cross(rb, e.Normal))), e.Normal); + double effectiveMass = InvMass + e.Target.RigidBody.InvMass + R1 + R2; + + // (a × b) × c = (c • a)b - (c • b)a + double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); + Vector2 J = (float)J_n * vt / count; // + (float)J_t * va_t_unit; + + AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); + } + + /// + /// 对接触事件进行响应 + /// + /// + /// + public void RespondToEvents(List events, float deltaTime) + { + foreach (var e in events) + { + SolveContactImpluse(e, events.Count, deltaTime); + } + ApplyImpluses(); + foreach (var e in events) + { + SolveFrictionImpluse(e, events.Count, deltaTime); + MatchAwakeState(e.Target.RigidBody, deltaTime); + } + ApplyImpluses(); + } + + public void Integrate(float deltaTime) + { + if (MovementType == MovementType.Static) + { + return; + } + _bindObject.Rotation += deltaTime * _angularVelocity; + _globalCentroid += deltaTime * _linearVelocity; + _bindObject.Position = _globalCentroid; + } + + // public void ResolveImpluse(float deltaTime) + // { + // if (_impluses.Count == 0) + // { + // return; + // } + // //_bindObject.Rotation = _bindObject.OldRotation; + // //_globalCentroid = _bindObject.OldPosition; + // _impluses.Sort((a, b) => + // { + // return a.Time.CompareTo(b.Time); + // }); + // float lastTime = 0; + // int sameTimeCount = 0; + // for (int i = 0; i < _impluses.Count; i++) + // { + // sameTimeCount++; + // if (i == _impluses.Count - 1 || _impluses[i].Time != _impluses[i + 1].Time) + // { + // float dt = (_impluses[i].Time - lastTime); + + // Vector2 linearVelocityChange = Vector2.Zero; + // float angularVelocityChange = 0; + // Vector2 normalAvg = Vector2.Zero; + + // //linearVelocityChange /= sameTimeCount; + // //angularVelocityChange /= sameTimeCount; + // //normalAvg /= sameTimeCount; + // //normalAvg = normalAvg.SafeNormalize(Vector2.Zero); + + // var resImpluses = ResolveConstrains(); + // for (int j = i - sameTimeCount + 1; j <= i; j++) + // { + // linearVelocityChange += 1.0f / _mass * resImpluses[j] * _impluses[j].Normal; + // angularVelocityChange += (float)(Utils.Cross(_impluses[j].RelativePosition, + // resImpluses[j] *_impluses[j].Normal * (float)_globalInverseInertiaTensor)); + // } + + // //var penetration = Math.Max(0, -Vector2.Dot(_linearVelocity * deltaTime, normalAvg)); + // //_linearVelocity += normalAvg * penetration * 0.7f; + + // deltaTime -= dt; + // lastTime = _impluses[i].Time; + // sameTimeCount = 0; + // } + // } + // _impluses.Clear(); + // } + + // private List ResolveConstrains() + // { + // List impluseArray = new List(); + // List impluseArrayTemp = new List(); + // for (int k = 0; k < _impluses.Count; k++) + // { + // impluseArray.Add(_impluses[k].Impluse.Length()); + // impluseArrayTemp.Add(0f); + // } + // for (int iter = 0; iter < 8; iter++) + // { + // int sameSourceCount = 0; + // for (int k = 0; k < _impluses.Count; k++) + // { + // var ri0 = _impluses[k].CollisionEvent.LocalOffsetSrc; + // var rb0 = _impluses[k].CollisionEvent.LocalOffsetTarget; + // var n0 = _impluses[k].CollisionEvent.Normal; + // var va0 = _impluses[k].CollisionEvent.Source.RigidBody.LinearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(ri0, _impluses[k].CollisionEvent.Source.RigidBody.AngularVelocity); + // var vb0 = _impluses[k].CollisionEvent.Target.RigidBody.LinearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(rb0, _impluses[k].CollisionEvent.Target.RigidBody.AngularVelocity); + // float relv0_n = Vector2.Dot(va0 - vb0, n0); + // float relv0_plus = -(1 + 0.4f) * Vector2.Dot(va0 - vb0, n0); + + // float Kj = _impluses[k].CollisionEvent.Source.RigidBody.InvMass + _impluses[k].CollisionEvent.Target.RigidBody.InvMass + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(ri0, n0))), n0) + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(_impluses[k].CollisionEvent.Target.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(rb0, n0))), n0); + + // float KLU = 0f; + // for (int l = 0; l < _impluses.Count; l++) + // { + // if (l == k) + // { + // continue; + // } + // var ri1 = _impluses[l].CollisionEvent.LocalOffsetSrc; + // var rb1 = _impluses[l].CollisionEvent.LocalOffsetTarget; + // var n1 = _impluses[l].CollisionEvent.Normal; + + // KLU += (_impluses[k].CollisionEvent.Source.RigidBody.InvMass + // * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, + // (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor + // * Utils.Cross(ri1, n1))), n0)) * impluseArray[l]; + // } + // impluseArrayTemp[k] = 1 / Kj * (relv0_plus - KLU); + // } + // sameSourceCount = 0; + // // Deep copy + // for (int k = 0; k < impluseArray.Count; k++) + // { + // impluseArray[k] = impluseArrayTemp[k]; + // } + // } + + // return impluseArray; + // } + public void AddForce(Vector2 force, Vector2 relPos) + { + _force += force; + _torque += GeometryUtils.Cross(relPos, force); + } + + /// + /// 把冲量数据累加到这个刚体上 + /// + /// + /// + /// + /// + /// + public void AddImpluse(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) + { + var entry = new ImpluseEntry() + { + Source = this, + Target = other, + ImpluseSource = J, + RelativePositionSource = relativePos, + ImpluseTarget = J2, + RelativePositionTarget = relativePos2, + }; + _impluses.Add(entry); + } + + /// + /// 对累加的冲量进行应用,更新各种速度 + /// + public void ApplyImpluses() + { + if (_impluses.Count > 0) + { + foreach (var imp in _impluses) + { + if (imp.Source.MovementType == MovementType.Dynamic || imp.Source.MovementType == MovementType.Player) + { + _linearVelocity += 1.0f / _mass * imp.ImpluseSource; + if (imp.Source.MovementType != MovementType.Player) + { + _angularVelocity += GeometryUtils.Cross(imp.RelativePositionSource, imp.ImpluseSource * (float)_globalInverseInertiaTensor); + } + } + if (imp.Target.MovementType == MovementType.Dynamic || imp.Target.MovementType == MovementType.Player) + { + imp.Target._linearVelocity += imp.Target.InvMass * imp.ImpluseTarget; + if (imp.Target.MovementType != MovementType.Player) + { + imp.Target._angularVelocity += GeometryUtils.Cross(imp.RelativePositionTarget, imp.ImpluseTarget * (float)imp.Target.GlobalInverseInertiaTensor); + } + } + } + } + + Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); + Debug.Assert(!float.IsNaN(_angularVelocity)); + StabilizeBody(); + _impluses.Clear(); + } + + /// + /// 立即施加一个冲量 + /// + /// + /// + public void AddImpluseImmediate(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) + { + if (MovementType != MovementType.Static && MovementType != MovementType.Kinematic) + { + _linearVelocity += 1.0f / _mass * J; + Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); + _angularVelocity += GeometryUtils.Cross( + relativePos, + J * (float)GlobalInverseInertiaTensor); + Debug.Assert(!float.IsNaN(_angularVelocity)); + } + + if (other.MovementType != MovementType.Static && MovementType != MovementType.Kinematic) + { + other._linearVelocity += other.InvMass * J2; + other._angularVelocity += GeometryUtils.Cross( + relativePos2, + J2 * (float)other.GlobalInverseInertiaTensor); + } + } + + public void MoveBody(Vector2 dir, float deltaTime) + { + _globalCentroid += dir; + + // _linearVelocity = (_globalCentroid - ParentObject.OldPosition) / deltaTime; + } + + public void MatchAwakeState(RigidBody2D other, float deltaTime) + { + if (other.MovementType == MovementType.Static) + { + return; + } + if (!_isAwake && !other._isAwake) + { + return; + } + if (_isAwake && !other._isAwake) + { + other.IsAwake = true; + } + else if (!_isAwake && other._isAwake) + { + IsAwake = true; + } + } + + public void CheckAwake(float deltaTime) + { + if (MovementType == MovementType.Static || MovementType == MovementType.Kinematic) + { + return; + } + double bias = Math.Pow(0.6, deltaTime); + _motion = bias * _motion + (1 - bias) * (_linearVelocity.LengthSquared() + + 3 * _angularVelocity * _angularVelocity); + + if (_motion > 16 * SleepEPS) + { + _motion = 16 * SleepEPS; + } + + if (_motion < SleepEPS) + { + IsAwake = false; + } + } + } +} \ No newline at end of file diff --git "a/Sources/Everglow.Core/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" "b/Sources/Everglow.Function/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" similarity index 100% rename from "Sources/Everglow.Core/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" rename to "Sources/Everglow.Function/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" diff --git a/Sources/Modules/Example/PhysicsDemo.cs b/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs similarity index 67% rename from Sources/Modules/Example/PhysicsDemo.cs rename to Sources/Everglow.Function/Physics/PhysicWorldSystem.cs index 54c1d7fbb..c0a99ae2d 100644 --- a/Sources/Modules/Example/PhysicsDemo.cs +++ b/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs @@ -1,56 +1,48 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using System.Data; using Everglow.Commons.Physics.PBEngine; -using ReLogic.Content; -using Terraria.GameContent; -using Microsoft.Xna.Framework.Input; -using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; -using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.Constrains; -using Terraria; -using ChatGPT.Core; -using static Terraria.ModLoader.PlayerDrawLayer; -using System.Diagnostics.Contracts; -using ChatGPT.Core.PBEngine.Collision.Colliders; -using ReLogic.Graphics; +using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Vertex; +using Microsoft.Xna.Framework.Input; +using ReLogic.Content; +using ReLogic.Graphics; +using Terraria.GameContent; -namespace Everglow.Example; +namespace Everglow.Commons.Physics; -internal class PhysicsPlayer : ModPlayer +public class PhysicsPlayer : ModPlayer { private PhysicsObject _movingPanel; private Vector2 _prevVelocity; private float _simulationDt = 0.1f; public Vector2 _extraVelocity; public bool _shouldStand; + public override void OnEnterWorld() { } + public override void PostUpdate() { - - Main.LocalPlayer.velocity += _extraVelocity; _extraVelocity = Vector2.Zero; if (Main.LocalPlayer.velocity.Y == 0 - && PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count > 0) + && PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count > 0) { Vector2 tangent = Vector2.Zero; - foreach (var v in PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity) + foreach (var v in PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity) { tangent += v; } - tangent /= PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count; + tangent /= PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count; _extraVelocity = tangent * _simulationDt; _extraVelocity = Vector2.Dot(_extraVelocity, new Vector2(1, 0)) * new Vector2(1, 0); } } + public override void PreUpdate() { _shouldStand = false; @@ -60,13 +52,11 @@ public override void PreUpdate() public override void PreUpdateMovement() { - //// Main.LocalPlayer.Center = Physics.Utils.ConvertToPhysicsSpace(Display.Instance._dummyPlayer.Position); - //Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity) * dt; - // - //var t = PhysicsDemo.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity; - //Main.NewText(Main.LocalPlayer.velocity, Color.GreenYellow); - + // Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicWorldSystem.Instance._dummyPlayer.RigidBody.LinearVelocity) * dt; + // + // var t = PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity; + // Main.NewText(Main.LocalPlayer.velocity, Color.GreenYellow); Player.velocity -= _extraVelocity; float dt = 0.1f; @@ -74,19 +64,17 @@ public override void PreUpdateMovement() var preVelY = Main.LocalPlayer.velocity.Y; var preVel = Main.LocalPlayer.velocity; - var broadPhase = PhysicsDemo.Instance._realSimulation.BroadPhaseCollisionDetector; + var broadPhase = PhysicWorldSystem.Instance._realSimulation.BroadPhaseCollisionDetector; - - List temporaryObjects = new List(); + var temporaryObjects = new List(); foreach (var proj in Main.projectile) { if (proj.active && proj.tileCollide) { - if (broadPhase.TestSingleCollision(proj.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() { - "Default" + "Default", })) { var obj = new PhysicsObject(new BoxCollider(proj.width, proj.height), new RigidBody2D(64)); @@ -104,10 +92,9 @@ public override void PreUpdateMovement() { if (npc.active && !npc.noTileCollide) { - if (broadPhase.TestSingleCollision(npc.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() { - "Default" + "Default", })) { var obj = new PhysicsObject(new BoxCollider(npc.width, npc.height), new RigidBody2D(512)); @@ -123,36 +110,36 @@ public override void PreUpdateMovement() foreach (var obj in temporaryObjects) { - PhysicsDemo.Instance._realSimulation.AddPhysicsObject(obj); + PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(obj); } // Main.NewText(Main.LocalPlayer.velocity, Color.Lime); - PhysicsDemo.Instance._dummyPlayer.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.velocity / dt); - PhysicsDemo.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(oldPos); - if (!PhysicsDemo.EnableRigidBodyTestPlayground) + PhysicWorldSystem.Instance._dummyPlayer.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.velocity / dt); + PhysicWorldSystem.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(oldPos); + if (!PhysicWorldSystem.EnableRigidBodyTestPlayground) { - PhysicsDemo.Instance._realSimulation.Update(dt); + PhysicWorldSystem.Instance._realSimulation.Update(dt); } foreach (var obj in temporaryObjects) { - PhysicsDemo.Instance._realSimulation.RemoveObject(obj); + PhysicWorldSystem.Instance._realSimulation.RemoveObject(obj); } - - //Main.LocalPlayer.Center = GeometryUtils.ConvertToPhysicsSpace(PhysicsDemo.Instance._dummyPlayer.RigidBody.CentroidWorldSpace) - player.; + // Main.LocalPlayer.Center = GeometryUtils.ConvertToPhysicsSpace(PhysicWorldSystem.Instance._dummyPlayer.RigidBody.CentroidWorldSpace) - player.; // Main.NewText(Main.LocalPlayer.velocity,Color.Cyan); // Find any valid, standable slope - if (PhysicsDemo.Instance._dummyPlayer.RigidBody.ContactNormals.Count > 0) + if (PhysicWorldSystem.Instance._dummyPlayer.RigidBody.ContactNormals.Count > 0) { - foreach (var v in PhysicsDemo.Instance._dummyPlayer.RigidBody.ContactNormals) + foreach (var v in PhysicWorldSystem.Instance._dummyPlayer.RigidBody.ContactNormals) { if (v.Y > 0.9f) { _shouldStand = true; + // Omit tangent velocity due to gravity - float mg = -PhysicsDemo.Instance._realSimulation.Gravity; + float mg = -PhysicWorldSystem.Instance._realSimulation.Gravity; Vector2 velDueG = new Vector2(0, -Player.gravDir * Player.gravity / dt) * dt; Vector2 normalDir = Vector2.Dot(velDueG, v) * v; Vector2 tanDir = velDueG - normalDir; @@ -162,21 +149,19 @@ public override void PreUpdateMovement() } } - //if (_shouldStand) - //{ + // if (_shouldStand) + // { // Main.LocalPlayer.velocity = Main.LocalPlayer.velocity - _prevVelocity; - //} + // } - // if (t.Length() > 0 && Math.Abs(GeometryUtils.Cross(new Vector2(1, 0), t)) < 0.3f) - //{ - // Main.LocalPlayer.velocity.Y = 0; - //} + // if (t.Length() > 0 && Math.Abs(GeometryUtils.Cross(new Vector2(1, 0), t)) < 0.3f) + // { + // Main.LocalPlayer.velocity.Y = 0; + // } // Main.LocalPlayer.velocity -= _extraVelocity; - if (_movingPanel != null) { - if (Main.time % 1200 < 600) { _movingPanel.RigidBody.LinearVelocity = new Vector2(-4, 0); @@ -194,6 +179,7 @@ public override void UpdateEquips() { base.UpdateEquips(); } + public override void SetControls() { base.SetControls(); @@ -201,68 +187,59 @@ public override void SetControls() { for (int i = 0; i < 2; i++) { - var ball = new PhysicsObject(new BoxCollider(32, 32), - new RigidBody2D(256)); - PhysicsDemo.Instance._realSimulation.AddPhysicsObject(ball); - ball.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld) + new Vector2(i, 0); - ball.RigidBody.LinearVelocity = new Vector2(0, 0); + // var ball = new PhysicsObject( + // new BoxCollider(32, 32), + // new RigidBody2D(256)); + // PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld) + new Vector2(i, 0); + // ball.RigidBody.LinearVelocity = new Vector2(0, 0); } } - if (Main.keyState[Keys.R] == KeyState.Down && Main.oldKeyState[Keys.R] == KeyState.Up) - { - if (PhysicsDemo.EnableRigidBodyTestPlayground) - { - RigidBodyDisplay.Instance.ReInitPhysWorld(); - } - PhysicsDemo.Instance.ReStart(); - } if (Main.keyState[Keys.T] == KeyState.Down && Main.oldKeyState[Keys.T] == KeyState.Up) { var dynamicBox = new PhysicsObject( new BoxCollider(128, 32), new RigidBody2D(256)); - //_movingPanel = dynamicBox; + // _movingPanel = dynamicBox; dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -200)); dynamicBox.Rotation = 0.0f; dynamicBox.RigidBody.MovementType = MovementType.Dynamic; dynamicBox.RigidBody.UseGravity = true; dynamicBox.RigidBody.Drag = 0.1f; - PhysicsDemo.Instance._realSimulation.AddPhysicsObject(dynamicBox); - + PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(dynamicBox); var staticPlane = new PhysicsObject( new BoxCollider(256, 128), null); staticPlane.Rotation = 0.3f; staticPlane.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(200, -200)); - PhysicsDemo.Instance._realSimulation.AddPhysicsObject(staticPlane); + PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(staticPlane); - //var ball = new PhysicsObject(new BoxCollider(32, 32), + // var ball = new PhysicsObject(new BoxCollider(32, 32), // new RigidBody2D(256)); - //Display.Instance._realSimulation.AddPhysicsObject(ball); - //ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -300)); - + // Display.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -300)); var staticPlane1 = new PhysicsObject( new BoxCollider(128, 32), null); staticPlane1.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(-200, -200)); staticPlane1.Rotation = 0.0f; staticPlane1.RigidBody.MovementType = MovementType.Static; staticPlane1.RigidBody.UseGravity = false; - PhysicsDemo.Instance._realSimulation.AddPhysicsObject(staticPlane1); - // _movingPanel = staticPlane1; + PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(staticPlane1); + // _movingPanel = staticPlane1; var joint = new SpringConstrain(staticPlane1, dynamicBox, 100f, 144f, new Vector2(64, -16), new Vector2(64, 16)); - PhysicsDemo.Instance._realSimulation.AddConstrain(joint); + PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint); var joint11 = new SpringConstrain(staticPlane1, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(-64, 16)); - PhysicsDemo.Instance._realSimulation.AddConstrain(joint11); + PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint11); - //var joint2 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, 16), new Vector2(64, -16)); - //PhysicsDemo.Instance._realSimulation.AddConstrain(joint2); - //var joint22 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(64, 16)); - //PhysicsDemo.Instance._realSimulation.AddConstrain(joint22); + // var joint2 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, 16), new Vector2(64, -16)); + // PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint2); + // var joint22 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(64, 16)); + // PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint22); - //if (Main.rand.NextBool(2)) - //{ + // if (Main.rand.NextBool(2)) + // { // var ball = new PhysicsObject(new BoxCollider(256, 32), // new RigidBody2D(128)); // ball.RigidBody.MovementType = Physics.Collision.MovementType.Kinematic; @@ -271,56 +248,51 @@ public override void SetControls() // ball.RigidBody.AngularVelocity = 0.9f; // Display.Instance._realSimulation.AddPhysicsObject(ball); // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -100)); - //} - //else - //{ + // } + // else + // { // var ball = new PhysicsObject(new SphereCollider(32), // new RigidBody2D(256)); // Display.Instance._realSimulation.AddPhysicsObject(ball); // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(100, 0)); // ball.RigidBody.LinearVelocity = new Vector2(-78, 0); - //} + // } } } } -internal class PhysicsDemo : ModSystem +public class PhysicWorldSystem : ModSystem { - private RigidBodyDisplay rigidBodyDisplay; + private RigidBodyRenderer rigidBodyRenderer; private Asset _renderShader; public PhysicsSimulation _realSimulation; - public static PhysicsDemo Instance; + public static PhysicWorldSystem Instance; public PhysicsObject _dummyPlayer; - public const bool EnableRigidBodyTestPlayground = true; + public static bool EnableRigidBodyTestPlayground = false; public const float Simulation_DeltaTime = 0.1f; public override void PostUpdateEverything() { if (EnableRigidBodyTestPlayground) { - rigidBodyDisplay.Update(); + rigidBodyRenderer.Update(); } } - public override void PostDrawTiles() { if (EnableRigidBodyTestPlayground) { - rigidBodyDisplay.Draw(Main.spriteBatch); + rigidBodyRenderer.Draw(Main.spriteBatch); } - List vertices = new List(); - foreach (var (p, c) in _realSimulation.GetCurrentWireFrames()) - { - vertices.Add(new Vertex2D(GeometryUtils.ConvertToPhysicsSpace(p) - Main.screenPosition, c, Vector3.Zero)); - } + var vertices = new List(); if (vertices.Count > 0) { // _renderShader.Value.Parameters["uColor"].SetValue(Color.White.ToVector4()); - _renderShader.Value.Parameters["uTransform"].SetValue(Matrix.CreateOrthographicOffCenter(0, Main.screenWidth, Main.screenHeight, 0, 0, 1)); + _renderShader.Value.Parameters["uTransform"].SetValue(Main.GameViewMatrix.TransformationMatrix * Matrix.CreateOrthographicOffCenter(0, Main.screenWidth, Main.screenHeight, 0, 0, 1)); _renderShader.Value.CurrentTechnique.Passes[0].Apply(); Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); @@ -334,21 +306,19 @@ public override void Load() { if (EnableRigidBodyTestPlayground) { - rigidBodyDisplay = new RigidBodyDisplay(); - rigidBodyDisplay.Initialize(); + rigidBodyRenderer = new RigidBodyRenderer(); + rigidBodyRenderer.Initialize(); } Instance = this; ReStart(); _renderShader = ModContent.Request("Everglow/Example/VFX/Default"); - On_Player.PlayerFrame += On_Player_PlayerFrame; On_Collision.TileCollision += On_Collision_TileCollision; base.Load(); } - private Vector2 On_Collision_TileCollision(On_Collision.orig_TileCollision orig, Vector2 Position, Vector2 Velocity, int Width, int Height, bool fallThrough, bool fall2, int gravDir) { Position = GeometryUtils.ConvertToPhysicsSpace(Position); @@ -360,12 +330,11 @@ private Vector2 On_Collision_TileCollision(On_Collision.orig_TileCollision orig, var collisions = _realSimulation.BroadPhaseCollisionDetector.GetSingleCollision(rect, Vector2.Zero, 0, new List() { - "Default" + "Default", }); if (collisions.Count > 0) { - var dummyCollider = new BoxCollider(Width, Height); var dummyPObject = new PhysicsObject(dummyCollider, new RigidBody2D(64)); dummyPObject.OldPosition = rect.Center; @@ -378,14 +347,15 @@ private Vector2 On_Collision_TileCollision(On_Collision.orig_TileCollision orig, { float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); - //if (weightA != 0 && weightB != 0) + + // if (weightA != 0 && weightB != 0) // weightA = weightB = 0.5f; - //Vector2 impluseVel = Vector2.Zero; + // Vector2 impluseVel = Vector2.Zero; // List events; // dummyCollider.GetContactInfo(info, Simulation_DeltaTime, out events); - //foreach(var e in events) - //{ + // foreach(var e in events) + // { // impluseVel += SolveContactImpluse(e, result + velChange, events.Count, Simulation_DeltaTime); // } // dummyPObject.RigidBody.MoveBody(info.Normal * weightA * info.Depth, Simulation_DeltaTime); @@ -397,21 +367,22 @@ private Vector2 On_Collision_TileCollision(On_Collision.orig_TileCollision orig, { dummyPObject.RigidBody.MoveBody(info.Depth / info.Normal.Y * new Vector2(0, 1), Simulation_DeltaTime); } - //if (float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.X) || float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.Y)) - //{ - // if (true) - // ; - //} + + // if (float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.X) || float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.Y)) + // { + // if (true) + // ; + // } // extraVel = collider.ParentObject.RigidBody.LinearVelocity * Simulation_DeltaTime; - //var n = Vector2.Dot(result, info.Normal) * info.Normal; - //result = result - n; + // var n = Vector2.Dot(result, info.Normal) * info.Normal; + // result = result - n; // velChange += impluseVel; } } // Main.NewText(result + dummyCollider.ParentObject.Position - oldCenter, Color.Red); // var newDir = result + new Vector2(0, d); - Velocity = (dummyCollider.ParentObject.Position - oldCenter); + Velocity = dummyCollider.ParentObject.Position - oldCenter; } Vector2 result = orig(GeometryUtils.ConvertToPhysicsSpace(Position), GeometryUtils.ConvertToPhysicsSpace(Velocity), Width, Height, fallThrough, fall2, gravDir); @@ -448,41 +419,45 @@ private Vector2 SolveContactImpluse(CollisionEvent2D e, Vector2 sourceVel, int c double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); double R1 = 0; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor - // * Utils.Cross(ri, e.Normal))), e.Normal); - double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor;//Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor - //* Utils.Cross(rb, e.Normal))), e.Normal); + + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + + // * Utils.Cross(rb, e.Normal))), e.Normal); double J_n = (vnew_n - va_n) / (1.0 / mass + e.Target.RigidBody.InvMass + R1 + R2); - Vector2 J = (float)J_n * e.Normal / count;// + (float)J_t * va_t_unit; + Vector2 J = (float)J_n * e.Normal / count; // + (float)J_t * va_t_unit; return J / mass; } private void On_Player_PlayerFrame(On_Player.orig_PlayerFrame orig, Player self) { - Main.LocalPlayer.velocity += self.GetModPlayer()._extraVelocity; if (self.GetModPlayer()._shouldStand) { Main.LocalPlayer.velocity.Y = 0; - } orig(self); Main.LocalPlayer.velocity -= self.GetModPlayer()._extraVelocity; - } + public override void PostDrawInterface(SpriteBatch sb) { - var start = Main.LocalPlayer.Center - new Vector2(1024 / 2, 1024 / 2) - Main.screenPosition; + if (EnableRigidBodyTestPlayground) + { + var start = Main.LocalPlayer.Center - new Vector2(1024 / 2, 1024 / 2) - Main.screenPosition; - sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Red); - sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_realSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Red); - sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_realSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Red); - sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_realSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Red); + sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Red); + sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_realSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Red); + sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_realSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Red); + sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_realSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Red); - double percent = _realSimulation.NumOfBroadPhasePairs == 0 ? 1 : (double)_realSimulation.NumOfNarrowPhasePairs / _realSimulation.NumOfBroadPhasePairs; - sb.DrawString(FontAssets.MouseText.Value, $"Detected Pairs: {_realSimulation.NumOfNarrowPhasePairs} / {_realSimulation.NumOfBroadPhasePairs}, {percent:F2}% of accuracy", start + new Vector2(0, 108), Color.Red); + double percent = _realSimulation.NumOfBroadPhasePairs == 0 ? 1 : (double)_realSimulation.NumOfNarrowPhasePairs / _realSimulation.NumOfBroadPhasePairs; + sb.DrawString(FontAssets.MouseText.Value, $"Detected Pairs: {_realSimulation.NumOfNarrowPhasePairs} / {_realSimulation.NumOfBroadPhasePairs}, {percent:F2}% of accuracy", start + new Vector2(0, 108), Color.Red); + } } + public void ReStart() { var terrariaCollisionGroup = new CollisionGraph(); @@ -513,4 +488,4 @@ public override void Unload() { base.Unload(); } -} +} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/RigidBodyRenderer.cs b/Sources/Everglow.Function/Physics/RigidBodyRenderer.cs new file mode 100644 index 000000000..ab1523575 --- /dev/null +++ b/Sources/Everglow.Function/Physics/RigidBodyRenderer.cs @@ -0,0 +1,85 @@ +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.PlayGround.Contact; +using Everglow.Commons.Vertex; +using ReLogic.Content; +using ReLogic.Graphics; +using Terraria.GameContent; + +namespace Everglow.Commons.Physics +{ + public class RigidBodyRenderer + { + private PhysicsSimulation _physicsSimulation; + private RenderTarget2D _canvasTarget; + private Asset _renderShader; + + public static RigidBodyRenderer Instance; + + public RigidBodyRenderer() + { + Instance = this; + } + + private const int CANVAS_SIZE = 1024; + + public void Update() + { + _physicsSimulation.Update(0.1f); + } + + public void Draw(SpriteBatch sb) + { + GeometryUtils.Blit(Main.screenTarget, Main.screenTargetSwap, null, null); + RenderPhysWorld(); + GeometryUtils.Blit(Main.screenTargetSwap, Main.screenTarget, null, null); + sb.Begin(); + var start = Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition; + sb.Draw(TextureAssets.MagicPixel.Value, new Rectangle((int)start.X - 2, (int)start.Y - 2, CANVAS_SIZE + 4, CANVAS_SIZE + 4), Color.White); + sb.Draw(_canvasTarget, Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition, + null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.FlipVertically, 0f); + + sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Green); + sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_physicsSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Green); + sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_physicsSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Green); + sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_physicsSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Green); + + sb.End(); + } + + private void RenderPhysWorld() + { + var vertices = new List(); + + Main.graphics.GraphicsDevice.SetRenderTarget(_canvasTarget); + Main.graphics.GraphicsDevice.Clear(Color.Black); + _renderShader.Value.Parameters["uTransform"].SetValue(Matrix.CreateOrthographicOffCenter(0f, CANVAS_SIZE, CANVAS_SIZE, 0f, 0f, 1f)); + _renderShader.Value.CurrentTechnique.Passes[0].Apply(); + Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; + Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); + + + } + + public void ReInitPhysWorld() + { + _physicsSimulation = ContactPlayGround1.DoubleSlope(); + + // var staticPlane3 = new PhysicsObject( + // new AABBCollider(1, 40), null); + // staticPlane3.Position = new Vector2(200, 300); + // staticPlane3.Rotation = 0f; + // _physicsSimulation.AddPhysicsObject(staticPlane3); + _physicsSimulation.Initialize(); + } + + public void Initialize() + { + ReInitPhysWorld(); + Main.QueueMainThreadAction(() => + { + _canvasTarget = new RenderTarget2D(Main.graphics.GraphicsDevice, 1024, 1024); + }); + _renderShader = ModContent.Request("Everglow/Example/VFX/Default"); + } + } +} \ No newline at end of file diff --git a/Sources/Modules/Example/RigidBodyDisplay.cs b/Sources/Modules/Example/RigidBodyDisplay.cs deleted file mode 100644 index 1ca44a269..000000000 --- a/Sources/Modules/Example/RigidBodyDisplay.cs +++ /dev/null @@ -1,97 +0,0 @@ -using ChatGPT.Core; -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.PlayGround.Constrain; -using Everglow.Commons.Physics.PBEngine.PlayGround.Contact; -using Everglow.Commons.Vertex; -using ReLogic.Content; -using ReLogic.Graphics; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Terraria; -using Terraria.GameContent; - -namespace Everglow.Commons.Physics.PBEngine -{ - internal class RigidBodyDisplay - { - private PhysicsSimulation _physicsSimulation; - private RenderTarget2D _canvasTarget; - private Asset _renderShader; - - public static RigidBodyDisplay Instance; - - public RigidBodyDisplay() - { - Instance = this; - } - - private const int CANVAS_SIZE = 1024; - - public void Update() - { - _physicsSimulation.Update(0.1f); - } - - public void Draw(SpriteBatch sb) - { - GeometryUtils.Blit(Main.screenTarget, Main.screenTargetSwap, null, null); - RenderPhysWorld(); - GeometryUtils.Blit(Main.screenTargetSwap, Main.screenTarget, null, null); - sb.Begin(); - var start = Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition; - sb.Draw(TextureAssets.MagicPixel.Value, new Rectangle((int)start.X - 2, (int)start.Y - 2, CANVAS_SIZE + 4, CANVAS_SIZE + 4), Color.White); - sb.Draw(_canvasTarget, Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition, - null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.FlipVertically, 0f); - - sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Green); - sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_physicsSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Green); - sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_physicsSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Green); - sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_physicsSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Green); - - sb.End(); - } - - private void RenderPhysWorld() - { - List vertices = new List(); - - foreach (var (p, c) in _physicsSimulation.GetCurrentWireFrames()) - { - vertices.Add(new Vertex2D(p, c, Vector3.Zero)); - } - - Main.graphics.GraphicsDevice.SetRenderTarget(_canvasTarget); - Main.graphics.GraphicsDevice.Clear(Color.Black); - _renderShader.Value.Parameters["uTransform"].SetValue(Matrix.CreateOrthographicOffCenter(0f, CANVAS_SIZE, CANVAS_SIZE, 0f, 0f, 1f)); - _renderShader.Value.CurrentTechnique.Passes[0].Apply(); - Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; - Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); - } - - public void ReInitPhysWorld() - { - _physicsSimulation = ContactPlayGround1.DoubleSlope(); - //var staticPlane3 = new PhysicsObject( - // new AABBCollider(1, 40), null); - //staticPlane3.Position = new Vector2(200, 300); - //staticPlane3.Rotation = 0f; - //_physicsSimulation.AddPhysicsObject(staticPlane3); - - _physicsSimulation.Initialize(); - } - - public void Initialize() - { - ReInitPhysWorld(); - Main.QueueMainThreadAction(() => - { - _canvasTarget = new RenderTarget2D(Main.graphics.GraphicsDevice, 1024, 1024); - }); - _renderShader = ModContent.Request("Everglow/Example/VFX/Default"); - } - } -} diff --git a/Sources/Modules/Example/Test/PhysicsBody_Square.cs b/Sources/Modules/Example/Test/PhysicsBody_Square.cs new file mode 100644 index 000000000..60ac5677a --- /dev/null +++ b/Sources/Modules/Example/Test/PhysicsBody_Square.cs @@ -0,0 +1,41 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics; +using Terraria.Audio; + +namespace Everglow.Example.Test; + +/// +/// Devs only. +/// +public class PhysicsBody_Square : ModItem +{ + public override void SetDefaults() + { + Item.useTime = 21; + Item.useAnimation = 21; + } + + public int soundID = 0; + + public override void HoldItem(Player player) + { + if (Main.mouseLeft && Main.mouseLeftRelease) + { + var box = new Square_Physics( + new BoxCollider(32, 32), + new RigidBody2D(256)); + PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(box); + box.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); + box.RigidBody.LinearVelocity = new Vector2(0, 0); + } + if(Main.mouseRight && Main.mouseRightRelease) + { + if (PhysicWorldSystem.EnableRigidBodyTestPlayground) + { + RigidBodyRenderer.Instance.ReInitPhysWorld(); + } + PhysicWorldSystem.Instance.ReStart(); + } + } +} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/PhysicsBody_Square.png b/Sources/Modules/Example/Test/PhysicsBody_Square.png new file mode 100644 index 0000000000000000000000000000000000000000..1566743133c87bb687532c25f84c3c1773667889 GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^av;pY3?xs=ZJr3E7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1M1^9%x{{R19Oi|AIr-}_wgtNdSvY3H^Z$Ah#TCfJI00kvVTq8IesvJDxT7zBWH6cA^&vw+33fNT)xEn{SO0rUopMzfRwC^dndfd#12z{uEu zaRJ0skd3ShASO)#vO$0eXf6|2Wss!>kOkFcXkY-6b)CEQoA`^=H9)3_r;B5V#`)v~ j1s0Zx2Q}(1eEi7F!24UQxR0k<8l=|K)z4*}Q$iB}WaLMn literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs new file mode 100644 index 000000000..bc76cce49 --- /dev/null +++ b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs @@ -0,0 +1,22 @@ +namespace Everglow.Example.Test; + +/// +/// Devs only. +/// +public class PhysicsBody_TestSystem : ModItem +{ + public override void SetDefaults() + { + Item.useTime = 21; + Item.useAnimation = 21; + } + + public int soundID = 0; + + public override void HoldItem(Player player) + { + if (Main.mouseLeft && Main.mouseLeftRelease) + { + } + } +} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.png b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.png new file mode 100644 index 0000000000000000000000000000000000000000..ace253d9d4ce43ccfb1322675f8eb5849172fae3 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^av;pY3?xs=ZJr3E7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1M1^9%x{{R19Oi|AIr-}_wgtNdSvY3H^Z$Ah#TCfJI00kvVTq8IesvJDxT7zBWH6cA^&vw+33fNT)xEn{SO0rUopMzfRwC^dndfd#12z{uEu zaRJ0skd3ShASO)#vO$0eXf6|2Wss!>kOkFcXkY-6b)CEQoA`^=H9)3`r;B5V#`)v~ z1s0Zxj5})>*-Z8^)KvzZbzd&Y%Bt~b@#QH$;u&OmdN}N^xW#-@nbj}Pz;O4n$gx*z R_Vs|w@^tlcS?83{1OQ58RZ{=} literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/Square_Physics.cs b/Sources/Modules/Example/Test/Square_Physics.cs new file mode 100644 index 000000000..99b90ad8a --- /dev/null +++ b/Sources/Modules/Example/Test/Square_Physics.cs @@ -0,0 +1,35 @@ +using Everglow.Commons.Enums; +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.VFX; +using Everglow.Commons.VFX.Pipelines; + +namespace Everglow.Example.Test; + +[Pipeline(typeof(WCSPipeline))] +public class Square_Physics : CustomPhysicsObject +{ + public override CodeLayer DrawLayer => CodeLayer.PostDrawTiles; + + public Square_Physics(Collider2D collider, RigidBody2D rigidBody) + : base(collider, rigidBody) + { + } + + public override void Update() + { + //if(Collider is null) + //{ + // Active = false; + // return; + //} + } + + public override void Draw() + { + Vector2 pos = Position; + Color c = Lighting.GetColor(pos.ToTileCoordinates()); + Texture2D tex = ModAsset.Square_Physics.Value; + Ins.Batch.Draw(tex, pos, null, c, Rotation, tex.Size() * 0.5f, 1f, 0); + } +} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/Square_Physics.png b/Sources/Modules/Example/Test/Square_Physics.png new file mode 100644 index 0000000000000000000000000000000000000000..b51c4d0270218593495937d0614638f5d38fb79c GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^av;pY3?xs=ZJr3E7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O_~$2l#}z{{R1f`4Q<8KrUy2M`SSr1K)lSX0%`pRsjl1l(KW>pcVGK%2~@K!HNrE^(^HFq1IS@zkYZ$IU<9(ffLI#J202cH zkr^z`1Y{dBGBF4M=_nx1Y-a(BX93wD&|AjH@B-)u7>#Br15j!LI|B<)rGb&L0pkLQ zsURC!7eGv!0%U^#6VO~Hu*x7y3m^-s%h138Bo@TiscV2tK2I0N5RLQ62@ Date: Sat, 18 Oct 2025 21:16:43 +0800 Subject: [PATCH 08/13] Add some examples of physics engine. --- .../Physics/PBEngine/CustomPhysicsObject.cs | 40 -------- .../Physics/PBEngine/PhysicsSimulation.cs | 27 +++--- .../Physics/PhysicWorldSystem.cs | 57 +----------- .../Physics/RigidBodyRenderer.cs | 85 ----------------- .../Physics/VisualPhysicsUnit.cs | 68 ++++++++++++++ Sources/Modules/Example/Test/Diamond.png | Bin 0 -> 621 bytes .../Example/Test/Diamond_Physics_Item.cs | 86 ++++++++++++++++++ .../Example/Test/Diamond_Physics_Item.png | Bin 0 -> 496 bytes .../Modules/Example/Test/Diamond_glow0.png | Bin 0 -> 417 bytes .../Modules/Example/Test/Diamond_glow1.png | Bin 0 -> 407 bytes .../Modules/Example/Test/Diamond_glow2.png | Bin 0 -> 413 bytes .../Modules/Example/Test/Diamond_glow3.png | Bin 0 -> 453 bytes .../Modules/Example/Test/Diamond_glow4.png | Bin 0 -> 438 bytes .../Modules/Example/Test/Diamond_glow5.png | Bin 0 -> 411 bytes .../Modules/Example/Test/Diamond_glow6.png | Bin 0 -> 410 bytes .../Modules/Example/Test/Diamond_glow7.png | Bin 0 -> 436 bytes .../Modules/Example/Test/Diamond_glow8.png | Bin 0 -> 438 bytes Sources/Modules/Example/Test/MovePlatform.png | Bin 0 -> 373 bytes .../Example/Test/PhysicsBody_Square.cs | 41 --------- .../Example/Test/PhysicsBody_Square.png | Bin 375 -> 0 bytes .../Example/Test/PhysicsBody_TestSystem.cs | 63 +++++++++++++ .../Modules/Example/Test/Square_Physics.cs | 35 ------- .../Modules/Example/Test/Square_Physics.png | Bin 366 -> 0 bytes 23 files changed, 234 insertions(+), 268 deletions(-) delete mode 100644 Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs delete mode 100644 Sources/Everglow.Function/Physics/RigidBodyRenderer.cs create mode 100644 Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs create mode 100644 Sources/Modules/Example/Test/Diamond.png create mode 100644 Sources/Modules/Example/Test/Diamond_Physics_Item.cs create mode 100644 Sources/Modules/Example/Test/Diamond_Physics_Item.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow0.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow1.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow2.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow3.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow4.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow5.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow6.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow7.png create mode 100644 Sources/Modules/Example/Test/Diamond_glow8.png create mode 100644 Sources/Modules/Example/Test/MovePlatform.png delete mode 100644 Sources/Modules/Example/Test/PhysicsBody_Square.cs delete mode 100644 Sources/Modules/Example/Test/PhysicsBody_Square.png delete mode 100644 Sources/Modules/Example/Test/Square_Physics.cs delete mode 100644 Sources/Modules/Example/Test/Square_Physics.png diff --git a/Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs b/Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs deleted file mode 100644 index df4416c9c..000000000 --- a/Sources/Everglow.Function/Physics/PBEngine/CustomPhysicsObject.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Everglow.Commons.Enums; -using Everglow.Commons.Interfaces; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.VFX.CommonVFXDusts; -using Everglow.Commons.VFX; -using Everglow.Commons.VFX.Pipelines; - -namespace Everglow.Commons.Physics.PBEngine; - -/// -/// Custom PhysicsObject, allow modifying Draw(). -/// -public abstract class CustomPhysicsObject : PhysicsObject, IVisual -{ - public virtual bool Active { get; set; } = true; - - public virtual bool Visible { get; set; } = true; - - public virtual CodeLayer DrawLayer { get; } - - public CustomPhysicsObject(Collider2D collider, RigidBody2D rigidBody) - : base(collider, rigidBody) - { - Collider = collider; - RigidBody = rigidBody; - } - - public virtual void Draw() - { - } - - public virtual void Kill() - { - Active = false; - } - - public virtual void Update() - { - } -} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs index 446c76735..dceaa41e9 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs @@ -85,6 +85,17 @@ public PhysicsSimulation() { } + public void ClearPhysicsObjects() + { + foreach (PhysicsObject pobj in _objects) + { + if(pobj.IsActive) + { + RemoveObject(pobj); + } + } + } + public PhysicsSimulation(CollisionGraph graph) { _objectFreeList = new LinkedList(); @@ -111,6 +122,7 @@ public PhysicsSimulation(CollisionGraph graph) /// public void AddPhysicsObject(PhysicsObject pobj) { + //Main.NewText(pobj.RigidBody.Mass, Color.Red); pobj.GUID = AllocateGUID(); if (pobj.GUID == _objects.Count) { @@ -188,21 +200,6 @@ public void Update(float deltaTime) return result; } - public List EmitVertices() - { - List result = new List(); - foreach (PhysicsObject pobj in _objects) - { - if (!pobj.IsActive) - { - continue; - } - - result.AddRange(pobj.Draw()); - } - return result; - } - //public List GetCustomPhysicsObjects() //{ // List result = new List(); diff --git a/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs b/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs index c0a99ae2d..6a4e3ab46 100644 --- a/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs +++ b/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs @@ -116,10 +116,7 @@ public override void PreUpdateMovement() // Main.NewText(Main.LocalPlayer.velocity, Color.Lime); PhysicWorldSystem.Instance._dummyPlayer.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.velocity / dt); PhysicWorldSystem.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(oldPos); - if (!PhysicWorldSystem.EnableRigidBodyTestPlayground) - { - PhysicWorldSystem.Instance._realSimulation.Update(dt); - } + PhysicWorldSystem.Instance._realSimulation.Update(dt); foreach (var obj in temporaryObjects) { @@ -263,57 +260,20 @@ public override void SetControls() public class PhysicWorldSystem : ModSystem { - private RigidBodyRenderer rigidBodyRenderer; - private Asset _renderShader; public PhysicsSimulation _realSimulation; public static PhysicWorldSystem Instance; public PhysicsObject _dummyPlayer; - public static bool EnableRigidBodyTestPlayground = false; public const float Simulation_DeltaTime = 0.1f; public override void PostUpdateEverything() { - if (EnableRigidBodyTestPlayground) - { - rigidBodyRenderer.Update(); - } - } - - public override void PostDrawTiles() - { - if (EnableRigidBodyTestPlayground) - { - rigidBodyRenderer.Draw(Main.spriteBatch); - } - - var vertices = new List(); - - if (vertices.Count > 0) - { - // _renderShader.Value.Parameters["uColor"].SetValue(Color.White.ToVector4()); - _renderShader.Value.Parameters["uTransform"].SetValue(Main.GameViewMatrix.TransformationMatrix * Matrix.CreateOrthographicOffCenter(0, Main.screenWidth, Main.screenHeight, 0, 0, 1)); - _renderShader.Value.CurrentTechnique.Passes[0].Apply(); - Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; - Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); - } - - _realSimulation.BroadPhaseCollisionDetector.DrawDebugInfo(Main.spriteBatch); - base.PostDrawTiles(); } public override void Load() { - if (EnableRigidBodyTestPlayground) - { - rigidBodyRenderer = new RigidBodyRenderer(); - rigidBodyRenderer.Initialize(); - } - Instance = this; ReStart(); - _renderShader = ModContent.Request("Everglow/Example/VFX/Default"); - On_Player.PlayerFrame += On_Player_PlayerFrame; On_Collision.TileCollision += On_Collision_TileCollision; base.Load(); @@ -444,22 +404,15 @@ private void On_Player_PlayerFrame(On_Player.orig_PlayerFrame orig, Player self) public override void PostDrawInterface(SpriteBatch sb) { - if (EnableRigidBodyTestPlayground) - { - var start = Main.LocalPlayer.Center - new Vector2(1024 / 2, 1024 / 2) - Main.screenPosition; - - sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Red); - sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_realSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Red); - sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_realSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Red); - sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_realSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Red); - double percent = _realSimulation.NumOfBroadPhasePairs == 0 ? 1 : (double)_realSimulation.NumOfNarrowPhasePairs / _realSimulation.NumOfBroadPhasePairs; - sb.DrawString(FontAssets.MouseText.Value, $"Detected Pairs: {_realSimulation.NumOfNarrowPhasePairs} / {_realSimulation.NumOfBroadPhasePairs}, {percent:F2}% of accuracy", start + new Vector2(0, 108), Color.Red); - } } public void ReStart() { + if(_realSimulation is not null) + { + _realSimulation.ClearPhysicsObjects(); + } var terrariaCollisionGroup = new CollisionGraph(); _realSimulation = new PhysicsSimulation(terrariaCollisionGroup); terrariaCollisionGroup.AddSingleEdge("Default", "Default"); diff --git a/Sources/Everglow.Function/Physics/RigidBodyRenderer.cs b/Sources/Everglow.Function/Physics/RigidBodyRenderer.cs deleted file mode 100644 index ab1523575..000000000 --- a/Sources/Everglow.Function/Physics/RigidBodyRenderer.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics.PBEngine.PlayGround.Contact; -using Everglow.Commons.Vertex; -using ReLogic.Content; -using ReLogic.Graphics; -using Terraria.GameContent; - -namespace Everglow.Commons.Physics -{ - public class RigidBodyRenderer - { - private PhysicsSimulation _physicsSimulation; - private RenderTarget2D _canvasTarget; - private Asset _renderShader; - - public static RigidBodyRenderer Instance; - - public RigidBodyRenderer() - { - Instance = this; - } - - private const int CANVAS_SIZE = 1024; - - public void Update() - { - _physicsSimulation.Update(0.1f); - } - - public void Draw(SpriteBatch sb) - { - GeometryUtils.Blit(Main.screenTarget, Main.screenTargetSwap, null, null); - RenderPhysWorld(); - GeometryUtils.Blit(Main.screenTargetSwap, Main.screenTarget, null, null); - sb.Begin(); - var start = Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition; - sb.Draw(TextureAssets.MagicPixel.Value, new Rectangle((int)start.X - 2, (int)start.Y - 2, CANVAS_SIZE + 4, CANVAS_SIZE + 4), Color.White); - sb.Draw(_canvasTarget, Main.LocalPlayer.Center - new Vector2(CANVAS_SIZE / 2, CANVAS_SIZE / 2) - Main.screenPosition, - null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.FlipVertically, 0f); - - sb.DrawString(FontAssets.MouseText.Value, "[Profiler]", start, Color.Green); - sb.DrawString(FontAssets.MouseText.Value, $"PreIntegration Time: {_physicsSimulation.MeasuredPreIntegrationTimeInMs} ms", start + new Vector2(0, 28), Color.Green); - sb.DrawString(FontAssets.MouseText.Value, $"BroadPhase Time: {_physicsSimulation.MeasuredBroadPhaseTimeInMs} ms", start + new Vector2(0, 56), Color.Green); - sb.DrawString(FontAssets.MouseText.Value, $"NarrowPhase Time: {_physicsSimulation.MeasuredNarrowPhaseTimeInMs} ms", start + new Vector2(0, 74), Color.Green); - - sb.End(); - } - - private void RenderPhysWorld() - { - var vertices = new List(); - - Main.graphics.GraphicsDevice.SetRenderTarget(_canvasTarget); - Main.graphics.GraphicsDevice.Clear(Color.Black); - _renderShader.Value.Parameters["uTransform"].SetValue(Matrix.CreateOrthographicOffCenter(0f, CANVAS_SIZE, CANVAS_SIZE, 0f, 0f, 1f)); - _renderShader.Value.CurrentTechnique.Passes[0].Apply(); - Main.graphics.GraphicsDevice.Textures[0] = TextureAssets.MagicPixel.Value; - Main.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices.ToArray(), 0, vertices.Count / 2); - - - } - - public void ReInitPhysWorld() - { - _physicsSimulation = ContactPlayGround1.DoubleSlope(); - - // var staticPlane3 = new PhysicsObject( - // new AABBCollider(1, 40), null); - // staticPlane3.Position = new Vector2(200, 300); - // staticPlane3.Rotation = 0f; - // _physicsSimulation.AddPhysicsObject(staticPlane3); - _physicsSimulation.Initialize(); - } - - public void Initialize() - { - ReInitPhysWorld(); - Main.QueueMainThreadAction(() => - { - _canvasTarget = new RenderTarget2D(Main.graphics.GraphicsDevice, 1024, 1024); - }); - _renderShader = ModContent.Request("Everglow/Example/VFX/Default"); - } - } -} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs b/Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs new file mode 100644 index 000000000..95afca512 --- /dev/null +++ b/Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs @@ -0,0 +1,68 @@ +using Everglow.Commons.Enums; +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.VFX; +using Everglow.Commons.VFX.Pipelines; + +namespace Everglow.Commons.Physics; + +[Pipeline(typeof(WCSPipeline))] +public class VisualPhysicsUnit : Visual +{ + public override CodeLayer DrawLayer => CodeLayer.PostDrawTiles; + + public PhysicsObject physicsObject; + + public int Timer = 0; + + public delegate void VPU_Draw(VisualPhysicsUnit vpu); + + public event VPU_Draw CustomDraw; + + public delegate void VPU_Update(VisualPhysicsUnit vpu); + + public event VPU_Update CustomUpdate; + + public override void OnSpawn() + { + base.OnSpawn(); + } + + public override void Update() + { + if (physicsObject is null || !physicsObject.IsActive) + { + Active = false; + return; + } + Timer++; + CustomUpdate?.Invoke(this); + } + + public override void Draw() + { + if (physicsObject is null || !physicsObject.IsActive) + { + Active = false; + return; + } + CustomDraw?.Invoke(this); + } + + public override void Kill() + { + physicsObject.IsActive = false; + base.Kill(); + } + + public void RegisterCustomCode(VPU_Draw vpu_draw, VPU_Update vpu_update) + { + CustomDraw += vpu_draw; + CustomUpdate += vpu_update; + } + + public void UnregisterCustomCode(VPU_Draw vpu_draw, VPU_Update vpu_update) + { + CustomDraw -= vpu_draw; + CustomUpdate -= vpu_update; + } +} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/Diamond.png b/Sources/Modules/Example/Test/Diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..a8f3c8a58dbb8fe8da8d56ba7ae065dd01611f1e GIT binary patch literal 621 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil21AIbU<<;$7;+r(B{0#%nrA_;9y6(Se$^X2DGeCK}V>*UFinAohFZe$c zFa-No2!R9(JR*x382Ao>Fr%o3R|8N`qQo_#Bsf2V7NJjy2 zW;+X5JPXJMflpx>8!#?_m$Nv;eZ8x(p2rK(bCNS3cVHGxZ~o`M}e~F~q_@_OjvPFW* +/// Devs only. +/// +public class Diamond_Physics_Item : ModItem +{ + public override void SetDefaults() + { + Item.useTime = 21; + Item.useAnimation = 21; + } + + public override void HoldItem(Player player) + { + if (Main.mouseLeft && Main.mouseLeftRelease) + { + if (!Collision.IsWorldPointSolid(Main.MouseWorld)) + { + var rigBody = new SphereCollider(12); + + var box = new PhysicsObject( + new SphereCollider(12), + new RigidBody2D(20) { }); + + PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(box); + box.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); + box.RigidBody.LinearVelocity = new Vector2(0, 0); + + var vfx = new VisualPhysicsUnit(); + vfx.physicsObject = box; + vfx.Active = true; + vfx.Visible = true; + vfx.RegisterCustomCode(Draw, Update); + Ins.VFXManager.Add(vfx); + } + } + if (Main.mouseRight && Main.mouseRightRelease) + { + PhysicWorldSystem.Instance.ReStart(); + } + } + + public void Draw(VisualPhysicsUnit vpu) + { + Vector2 pos = vpu.physicsObject.Position; + pos.Y *= -1; + Color c = Lighting.GetColor(pos.ToTileCoordinates()); + c.A = 200; + Texture2D tex = ModAsset.Diamond.Value; + float rot = vpu.physicsObject.Rotation; + Ins.Batch.Draw(tex, pos, null, c, rot, tex.Size() * 0.5f, 1f, 0); + for (int i = 0; i < 9; i++) + { + tex = ModContent.Request(ModAsset.Diamond_Mod + "_glow" + i).Value; + float value = (pos.X + pos.Y) * 0.03f + rot + i; + float valueR = Math.Max(MathF.Sin(value), 0); + float valueG = Math.Max(MathF.Sin(value + 0.5f), 0); + float valueB = Math.Max(MathF.Sin(value + 1f), 0); + valueR = MathF.Pow(valueR, 4); + valueG = MathF.Pow(valueG, 4); + valueB = MathF.Pow(valueB, 4); + Color reflectColor = new Color(valueR, valueG, valueB, 0); + reflectColor = Lighting.GetColor(pos.ToTileCoordinates(), reflectColor) * 2; + reflectColor.A = 0; + if (i == 3 || i == 4) + { + reflectColor *= 0.2f; + } + Ins.Batch.Draw(tex, pos, null, reflectColor, rot, tex.Size() * 0.5f, 1f, 0); + } + } + + public void Update(VisualPhysicsUnit vpu) + { + // if(vpu.Timer > 150) + // { + // vpu.Kill(); + // } + } +} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/Diamond_Physics_Item.png b/Sources/Modules/Example/Test/Diamond_Physics_Item.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4a00624091f117ccab96b15d262a4e112ba183 GIT binary patch literal 496 zcmeAS@N?(olHy`uVBq!ia0vp^av;pY3?xs=ZJr3E7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`032l#}z{{R0U$aINs(zNn7UH9L#*AN9$JwrY7?rYyIfois;MtG)q zdTKFn06DA-QjDw&j6jwb5KBYZAV+I3GK0mLfNVoXCI$f@9RMddnCY zUI6_DqtPs707^|@XJ7%UG%zwYU|aw(6=Wmp0*FadfNT(80-DPNRvBbz0c1gS85$UX zWL@WO{U-h*bq$bN;pyTSqH%udg@e2f4g#%-x&opc?>31}*v>U!ZZFd{5d|U9fb9xp z$6H(ee@j&>o}p2tJ*~EQ(Ues!m-z}V?R>gZOs}Nf^ISFCtAyZn7Gis3H^lGA*;)GZ z#a)5j2TktH($O~;7XS7vX8XYuh5&=UJ(J(Od0k)kb0_a77j4~_kyECE+~eu$=d#Wz Gp$PzfZiOlU literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/Diamond_glow0.png b/Sources/Modules/Example/Test/Diamond_glow0.png new file mode 100644 index 0000000000000000000000000000000000000000..daab6653863adfe2d7ca30d76be8b56a7931fc1a GIT binary patch literal 417 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCils0(?STkNo@(WNxv)xB*Bpl?3?(|7Uo*-M|aT<1FxqEM{QfI|Ravq8eTe zKtYKT*NBqf{Irtt#G+J&fW*wa5 literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/Diamond_glow1.png b/Sources/Modules/Example/Test/Diamond_glow1.png new file mode 100644 index 0000000000000000000000000000000000000000..a5751b9fefc33f679a638741e126913bd60e0e63 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBufiR<}hF1enP@=>&q9iy!t)x7$D3u`~F*C13&(AeP!Bo#s z&%FEEcT1p}ZK)BSX`Y^13>-iXD}xjxD+42tvp@3Xlx~Oh9v) zz$$|*Er2YjE<*zYkgU_nm5+A)O#KLCx_P=dhIn|tJ!iHMR%mfJsfRkEICYw^6djX!5TUFM*XA-bco>WCJ|@&~&q9iy!t)x7$D3u`~F*C13&(AeP!Bo#s z&%FEEcT1p}ZK)BSX`Y^13>-iXD}xjxD+42tvp@3Xlx~Oh9v) zz$$|*Er2YjE<*zYkgU_nm5+A)O#KLC`gpoHhIn|ty{O2?pvdELQ8P{d=*OKRU7;!| zLNaTAofrLV> literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/Diamond_glow3.png b/Sources/Modules/Example/Test/Diamond_glow3.png new file mode 100644 index 0000000000000000000000000000000000000000..416d89dbce7d5b46305b56534f44ad8e813951c2 GIT binary patch literal 453 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$1AIbUP1pS|zx5Qz`}f@<0!T5J1o;L3|Icv!6faOM180FpWHAE+-yslY z6xHx*018T!xJHx&=ckpFCl;kL1SDqWmFW4ohA5co8S0sLU;AzeRI@EL!ZXd&Q;UHE z$YEuWVq|4t1hTw}~1<+$K z8qHD$pwt9*1{R=710!Pt#sv^lK{m23fS5D|$OZu>pt($7l|hykKo(S&p@9KN)@kL+ zN4tKeegrZDJY5_^Ec~xt@aAGr;Bh{+beu+N@$^MY{IWC#(kTapOuF{n*XRLI^*=syHg9hmS%)8M6G2z>bLaJU64JV Lu6{1-oD!M}~1<+$K z8qHD$pwt9*1{R=710!Pt#sv^lK{m23fS5D|$OZu>pt($7l|hykKo(S&p@9KN)@kL+ zN4tKeegrZdJzX3_Ed1Y|bL3)B;BdJp`M&aH%-L~Vxtkbhj|1)CP v9z0L&o!X?Hee62Q=hr6J`ZnLyt<^PUSjx+G^#9WSG?2lbu6{1-oD!M<#p!2A literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/Diamond_glow5.png b/Sources/Modules/Example/Test/Diamond_glow5.png new file mode 100644 index 0000000000000000000000000000000000000000..62f127c67d2833f1488988a78339072b65193beb GIT binary patch literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBufiR<}hF1enP@=>&q9iy!t)x7$D3u`~F*C13&(AeP!Bo#s z&%FEEcT1p}ZK)BSX`Y^13>-iXD}xjxD+42tvp@3Xlx~Oh9v) zz$$|*Er2YjE<*zYkgU_nm5+A)O#KLCdU?7yhIn|ty|9~;L4n6LF!^y%-}|7jjZ+0K zF6b@r|H<)lX-oW;by=H*lbSm!GjxSsiE7QOobJ|^E`8_2v`EW)1<#+o1)6g}n)#El VaLqcwi98@1JYD@<);T3K0RXMzT}}W1 literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/Diamond_glow6.png b/Sources/Modules/Example/Test/Diamond_glow6.png new file mode 100644 index 0000000000000000000000000000000000000000..c8aa6f0b00c7529642d00585a4b123a3196f5448 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBufiR<}hF1enP@=>&q9iy!t)x7$D3u`~F*C13&(AeP!Bo#s z&%FEEcT1p}ZK)BSX`Y^13>-iXD}xjxD+42tvp@3Xlx~Oh9v) zz$$|*Er2YjE<*zYkgU_nm5+A)O#KLCdV0DzhIn|tJ#Q$)ARxeUQ2C?g`R|&76Sd3) zELko;R13~#W7w~`)%0#!VEg2mdKI;Vst0E!-3cK`qY literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/Diamond_glow7.png b/Sources/Modules/Example/Test/Diamond_glow7.png new file mode 100644 index 0000000000000000000000000000000000000000..6ce2ffac111d99974b188b56c6e4375feac14738 GIT binary patch literal 436 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCils0(?ST%WpjeGAAwxasg6IB|(0{{~4ZcH}C@TI14-?iy0XB4uLSEsD@Vq zP*9@8HKHUqKdq!Zu_%=xATcwqM9W{Vw|D`Zvik%N4CNxNv{1sBy~2;_pQp s*bly&SkwLf)kMTj%^Kb|MZqH8(v9C{Uh8IjQk4F=((#7v uysKMEPW`k`FP$&C=!5X-=Gb#Nrq39y#FGY9yDxc>kDfB6yV6F@F!fk$L90|Vb-5N14{zaj-FC{f}XQ4*Y=R#Ki= zl*$m0n3-3i=jR%tV5(=RXWo77yCqP~w$upEG*3@01`Z&Hl|hP;m4Ok+@&aOMC>!KB z4Mt|LI1`X<$jHPX0HmXUIJ2DvES?2qgFpfhgY?5_G)ozP-kZSAzyefZU}S8-xBy}* zNC)czh)Gj`Y!F}qn!^NE8DwbzWI=Tq8W@0NRo& fq^Bv+fJ;JxflYvcv9_SS0;Jf})z4*}Q$iB}1O`K- literal 0 HcmV?d00001 diff --git a/Sources/Modules/Example/Test/PhysicsBody_Square.cs b/Sources/Modules/Example/Test/PhysicsBody_Square.cs deleted file mode 100644 index 60ac5677a..000000000 --- a/Sources/Modules/Example/Test/PhysicsBody_Square.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics; -using Terraria.Audio; - -namespace Everglow.Example.Test; - -/// -/// Devs only. -/// -public class PhysicsBody_Square : ModItem -{ - public override void SetDefaults() - { - Item.useTime = 21; - Item.useAnimation = 21; - } - - public int soundID = 0; - - public override void HoldItem(Player player) - { - if (Main.mouseLeft && Main.mouseLeftRelease) - { - var box = new Square_Physics( - new BoxCollider(32, 32), - new RigidBody2D(256)); - PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(box); - box.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); - box.RigidBody.LinearVelocity = new Vector2(0, 0); - } - if(Main.mouseRight && Main.mouseRightRelease) - { - if (PhysicWorldSystem.EnableRigidBodyTestPlayground) - { - RigidBodyRenderer.Instance.ReInitPhysWorld(); - } - PhysicWorldSystem.Instance.ReStart(); - } - } -} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/PhysicsBody_Square.png b/Sources/Modules/Example/Test/PhysicsBody_Square.png deleted file mode 100644 index 1566743133c87bb687532c25f84c3c1773667889..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^av;pY3?xs=ZJr3E7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1M1^9%x{{R19Oi|AIr-}_wgtNdSvY3H^Z$Ah#TCfJI00kvVTq8IesvJDxT7zBWH6cA^&vw+33fNT)xEn{SO0rUopMzfRwC^dndfd#12z{uEu zaRJ0skd3ShASO)#vO$0eXf6|2Wss!>kOkFcXkY-6b)CEQoA`^=H9)3_r;B5V#`)v~ j1s0Zx2Q}(1eEi7F!24UQxR0k<8l=|K)z4*}Q$iB}WaLMn diff --git a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs index bc76cce49..d0d4909ae 100644 --- a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs +++ b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs @@ -1,3 +1,9 @@ +using Everglow.Commons.Physics; +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using static Everglow.Commons.VFX.VFXBatchExtension; + namespace Everglow.Example.Test; /// @@ -17,6 +23,63 @@ public override void HoldItem(Player player) { if (Main.mouseLeft && Main.mouseLeftRelease) { + if (!Collision.IsWorldPointSolid(Main.MouseWorld)) + { + var rigBody = new RigidBody2D(1) + { + MovementType = MovementType.Kinematic, + UseGravity = false, + }; + var box = new PhysicsObject( + new BoxCollider(192, 16), + null); + + PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(box); + box.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); + box.RigidBody.LinearVelocity = new Vector2(0, 0); + + var vfx = new VisualPhysicsUnit(); + vfx.Timer = 0; + vfx.physicsObject = box; + vfx.Active = true; + vfx.Visible = true; + vfx.RegisterCustomCode(Draw, Update); + Ins.VFXManager.Add(vfx); + } + } + } + + public void Draw(VisualPhysicsUnit vpu) + { + Vector2 pos = vpu.physicsObject.Position; + pos.Y *= -1; + Color c = Lighting.GetColor(pos.ToTileCoordinates()); + c.A = 200; + Texture2D tex = ModAsset.MovePlatform.Value; + float rot = vpu.physicsObject.Rotation; + Ins.Batch.Draw(tex, pos, null, c, rot, tex.Size() * 0.5f, 1f, 0); + } + + public void Update(VisualPhysicsUnit vpu) + { + Vector2 vel; + var timer = vpu.Timer % 600; + if (timer < 100) + { + vel = Vector2.zeroVector; + } + else if(timer >= 100 && timer < 300) + { + vel = new Vector2(0, -2); + } + else if (timer >= 300 && timer < 400) + { + vel = Vector2.zeroVector; + } + else + { + vel = new Vector2(0, 2); } + vpu.physicsObject.Position += vel; } } \ No newline at end of file diff --git a/Sources/Modules/Example/Test/Square_Physics.cs b/Sources/Modules/Example/Test/Square_Physics.cs deleted file mode 100644 index 99b90ad8a..000000000 --- a/Sources/Modules/Example/Test/Square_Physics.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Everglow.Commons.Enums; -using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.VFX; -using Everglow.Commons.VFX.Pipelines; - -namespace Everglow.Example.Test; - -[Pipeline(typeof(WCSPipeline))] -public class Square_Physics : CustomPhysicsObject -{ - public override CodeLayer DrawLayer => CodeLayer.PostDrawTiles; - - public Square_Physics(Collider2D collider, RigidBody2D rigidBody) - : base(collider, rigidBody) - { - } - - public override void Update() - { - //if(Collider is null) - //{ - // Active = false; - // return; - //} - } - - public override void Draw() - { - Vector2 pos = Position; - Color c = Lighting.GetColor(pos.ToTileCoordinates()); - Texture2D tex = ModAsset.Square_Physics.Value; - Ins.Batch.Draw(tex, pos, null, c, Rotation, tex.Size() * 0.5f, 1f, 0); - } -} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/Square_Physics.png b/Sources/Modules/Example/Test/Square_Physics.png deleted file mode 100644 index b51c4d0270218593495937d0614638f5d38fb79c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^av;pY3?xs=ZJr3E7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O_~$2l#}z{{R1f`4Q<8KrUy2M`SSr1K)lSX0%`pRsjl1l(KW>pcVGK%2~@K!HNrE^(^HFq1IS@zkYZ$IU<9(ffLI#J202cH zkr^z`1Y{dBGBF4M=_nx1Y-a(BX93wD&|AjH@B-)u7>#Br15j!LI|B<)rGb&L0pkLQ zsURC!7eGv!0%U^#6VO~Hu*x7y3m^-s%h138Bo@TiscV2tK2I0N5RLQ62@ Date: Wed, 22 Oct 2025 10:50:24 +0800 Subject: [PATCH 09/13] docs: Rename description files to README.md --- Sources/Everglow.Core/Utilities/{Utils.md => README.md} | 0 .../Everglow.Function/Physics/PBEngine/README.md | 0 .../Modules/Example/README.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename Sources/Everglow.Core/Utilities/{Utils.md => README.md} (100%) rename "Sources/Everglow.Function/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" => Sources/Everglow.Function/Physics/PBEngine/README.md (100%) rename "Sources/Modules/Example/\346\226\207\346\241\243.md" => Sources/Modules/Example/README.md (100%) diff --git a/Sources/Everglow.Core/Utilities/Utils.md b/Sources/Everglow.Core/Utilities/README.md similarity index 100% rename from Sources/Everglow.Core/Utilities/Utils.md rename to Sources/Everglow.Core/Utilities/README.md diff --git "a/Sources/Everglow.Function/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" b/Sources/Everglow.Function/Physics/PBEngine/README.md similarity index 100% rename from "Sources/Everglow.Function/Physics/PBEngine/\347\211\251\347\220\206\345\274\225\346\223\216\346\226\207\346\241\243.md" rename to Sources/Everglow.Function/Physics/PBEngine/README.md diff --git "a/Sources/Modules/Example/\346\226\207\346\241\243.md" b/Sources/Modules/Example/README.md similarity index 100% rename from "Sources/Modules/Example/\346\226\207\346\241\243.md" rename to Sources/Modules/Example/README.md From 81605a532252a5d9b81220ab591ad36a04c22a99 Mon Sep 17 00:00:00 2001 From: Cloudea Date: Fri, 24 Oct 2025 12:12:13 +0800 Subject: [PATCH 10/13] fix(Function): Fix obvious naming issues --- .../Collision/BroadPhase/CollisionGraph.cs | 2 +- .../{ImpluseEntry.cs => ImpulseEntry.cs} | 6 +- .../Constraint.cs} | 4 +- .../JointConstraint.cs} | 10 +- .../SpringConstraint.cs} | 10 +- .../Physics/PBEngine/GeometryUtils.cs | 2 +- .../Physics/PBEngine/PhysicsSimulation.cs | 30 +- .../PlayGround/Constrain/JointPlayground.cs | 22 +- .../Physics/PBEngine/RigidBody2D.cs | 144 +++--- .../Physics/PhysicWorldSystem.cs | 444 ------------------ .../Physics/PhysicsPlayer.cs | 259 ++++++++++ .../Physics/PhysicsWorldSystem.cs | 184 ++++++++ .../Example/Test/Diamond_Physics_Item.cs | 4 +- .../Example/Test/PhysicsBody_TestSystem.cs | 2 +- 14 files changed, 561 insertions(+), 562 deletions(-) rename Sources/Everglow.Function/Physics/PBEngine/Collision/{ImpluseEntry.cs => ImpulseEntry.cs} (77%) rename Sources/Everglow.Function/Physics/PBEngine/{Constrains/Constrain.cs => Constraints/Constraint.cs} (77%) rename Sources/Everglow.Function/Physics/PBEngine/{Constrains/JointConstrain.cs => Constraints/JointConstraint.cs} (87%) rename Sources/Everglow.Function/Physics/PBEngine/{Constrains/SpringConstrain.cs => Constraints/SpringConstraint.cs} (95%) delete mode 100644 Sources/Everglow.Function/Physics/PhysicWorldSystem.cs create mode 100644 Sources/Everglow.Function/Physics/PhysicsPlayer.cs create mode 100644 Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs index c1a34ce69..f6c4a26a3 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs @@ -5,7 +5,7 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase /// public class CollisionGraph { - public static CollisionGraph DefualtGraph + public static CollisionGraph DefaultGraph = new CollisionGraph(new Dictionary>() { { "Default", new List() { "Default" } } }); public Dictionary> Graph diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/ImpluseEntry.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/ImpulseEntry.cs similarity index 77% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/ImpluseEntry.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/ImpulseEntry.cs index 8c9449e0c..a4d494624 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/ImpluseEntry.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/ImpulseEntry.cs @@ -6,13 +6,13 @@ namespace Everglow.Commons.Physics.PBEngine.Collision { - public struct ImpluseEntry + public struct ImpulseEntry { public RigidBody2D Source; public RigidBody2D Target; - public Vector2 ImpluseSource; + public Vector2 ImpulseSource; public Vector2 RelativePositionSource; - public Vector2 ImpluseTarget; + public Vector2 ImpulseTarget; public Vector2 RelativePositionTarget; } } diff --git a/Sources/Everglow.Function/Physics/PBEngine/Constrains/Constrain.cs b/Sources/Everglow.Function/Physics/PBEngine/Constraints/Constraint.cs similarity index 77% rename from Sources/Everglow.Function/Physics/PBEngine/Constrains/Constrain.cs rename to Sources/Everglow.Function/Physics/PBEngine/Constraints/Constraint.cs index 5174fdc5e..16722cc05 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Constrains/Constrain.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Constraints/Constraint.cs @@ -4,9 +4,9 @@ using System.Text; using System.Threading.Tasks; -namespace Everglow.Commons.Physics.PBEngine.Constrains +namespace Everglow.Commons.Physics.PBEngine.Constraints { - public abstract class Constrain + public abstract class Constraint { public abstract void Apply(float deltaTime); public abstract void ApplyForce(float deltaTime); diff --git a/Sources/Everglow.Function/Physics/PBEngine/Constrains/JointConstrain.cs b/Sources/Everglow.Function/Physics/PBEngine/Constraints/JointConstraint.cs similarity index 87% rename from Sources/Everglow.Function/Physics/PBEngine/Constrains/JointConstrain.cs rename to Sources/Everglow.Function/Physics/PBEngine/Constraints/JointConstraint.cs index 05fa7f979..2e777125b 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Constrains/JointConstrain.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Constraints/JointConstraint.cs @@ -5,18 +5,18 @@ using System.Threading.Tasks; using Terraria; -namespace Everglow.Commons.Physics.PBEngine.Constrains +namespace Everglow.Commons.Physics.PBEngine.Constraints { /// /// TODO: 待定约束 /// - public class JointConstrain : Constrain + public class JointConstraint : Constraint { private PhysicsObject _objA; private PhysicsObject _objB; private Vector2 _localPosA; private Vector2 _localPosB; - public JointConstrain(PhysicsObject A, PhysicsObject B, Vector2 localPos1, Vector2 localPos2) + public JointConstraint(PhysicsObject A, PhysicsObject B, Vector2 localPos1, Vector2 localPos2) { _objA = A; _objB = B; @@ -43,7 +43,7 @@ public override void Apply(float deltaTime) Vector2 addB = newCenter - _objB.LocalToWorldPos(_localPosB); Vector2 oldPosB = _objB.Position; _objA.Position += addA; - _objB.Position = (newCenter - dirB * 200); + _objB.Position = newCenter - dirB * 200; _objA.RigidBody.LinearVelocity = (_objA.Position - _objA.OldPosition) / deltaTime; _objB.RigidBody.LinearVelocity = (_objB.Position - _objB.OldPosition) / deltaTime; @@ -58,7 +58,7 @@ public override void ApplyForce(float deltaTime) public override List<(Vector2, Color)> GetDrawMesh() { - List<(Vector2, Color)> drawMesh = new List<(Vector2, Color)>(); + var drawMesh = new List<(Vector2, Color)>(); drawMesh.Add((_objA.LocalToWorldPos(_localPosA), Color.Green)); drawMesh.Add((_objB.RigidBody.CentroidWorldSpace, Color.Green)); diff --git a/Sources/Everglow.Function/Physics/PBEngine/Constrains/SpringConstrain.cs b/Sources/Everglow.Function/Physics/PBEngine/Constraints/SpringConstraint.cs similarity index 95% rename from Sources/Everglow.Function/Physics/PBEngine/Constrains/SpringConstrain.cs rename to Sources/Everglow.Function/Physics/PBEngine/Constraints/SpringConstraint.cs index d16bbf656..5b5226586 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Constrains/SpringConstrain.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Constraints/SpringConstraint.cs @@ -6,12 +6,12 @@ using Everglow.Commons.Utilities; using Terraria; -namespace Everglow.Commons.Physics.PBEngine.Constrains +namespace Everglow.Commons.Physics.PBEngine.Constraints { /// /// 弹簧约束 /// - public class SpringConstrain : Constrain + public class SpringConstraint : Constraint { private PhysicsObject _objA; private PhysicsObject _objB; @@ -21,7 +21,7 @@ public class SpringConstrain : Constrain private Vector2 _localPosA; private Vector2 _localPosB; - public SpringConstrain(PhysicsObject A, PhysicsObject B, float elasticity, float restLength) + public SpringConstraint(PhysicsObject A, PhysicsObject B, float elasticity, float restLength) { _objA = A; _objB = B; @@ -32,7 +32,7 @@ public SpringConstrain(PhysicsObject A, PhysicsObject B, float elasticity, float _localPosB = Vector2.Zero; } - public SpringConstrain(PhysicsObject A, PhysicsObject B, float elasticity, float restLength, + public SpringConstraint(PhysicsObject A, PhysicsObject B, float elasticity, float restLength, Vector2 localPosA, Vector2 localPosB) : this(A, B, elasticity, restLength) { @@ -167,7 +167,7 @@ public override void ApplyForce(float deltaTime) public override List<(Vector2, Color)> GetDrawMesh() { - List<(Vector2, Color)> drawMesh = new List<(Vector2, Color)>(); + var drawMesh = new List<(Vector2, Color)>(); drawMesh.Add((_objA.LocalToWorldPos(_localPosA), Color.Green)); drawMesh.Add((_objB.LocalToWorldPos(_localPosB), Color.Green)); diff --git a/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs b/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs index 49edd0da7..919726a18 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs @@ -99,7 +99,7 @@ public static void Blit(Texture2D source, RenderTarget2D dest, Effect effect, st /// /// /// - public static Vector2 AnuglarVelocityToLinearVelocity(Vector2 a, float w) + public static Vector2 AngularVelocityToLinearVelocity(Vector2 a, float w) { return new Vector2(-a.Y, a.X) * w; } diff --git a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs index dceaa41e9..a26b5b9b8 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs @@ -2,7 +2,7 @@ using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.Constrains; +using Everglow.Commons.Physics.PBEngine.Constraints; using Everglow.Commons.Vertex; using Terraria; @@ -24,7 +24,7 @@ public int GAUSS_SEIDEL_ITERS private List _objects; private LinkedList _objectFreeList; private int _maximumAllocatedId; - private List _constrains; + private List _constraints; private IBroadPhase _broadPhase; private float _gravity; @@ -81,7 +81,7 @@ public IBroadPhase BroadPhaseCollisionDetector } public PhysicsSimulation() - : this(CollisionGraph.DefualtGraph) + : this(CollisionGraph.DefaultGraph) { } @@ -101,7 +101,7 @@ public PhysicsSimulation(CollisionGraph graph) _objectFreeList = new LinkedList(); _objects = new List(); _maximumAllocatedId = 0; - _constrains = new List(); + _constraints = new List(); _broadPhase = new HashGridMethod(graph); _gravity = 9.8f; @@ -139,9 +139,9 @@ public void AddPhysicsObject(PhysicsObject pobj) /// 向物理世界添加约束对象 /// /// - public void AddConstrain(Constrain constrain) + public void AddConstrain(Constraint constrain) { - _constrains.Add(constrain); + _constraints.Add(constrain); } /// @@ -193,7 +193,7 @@ public void Update(float deltaTime) result.AddRange(pobj.GetWireFrameWires()); } - foreach (Constrain joint in _constrains) + foreach (Constraint joint in _constraints) { result.AddRange(joint.GetDrawMesh()); } @@ -232,7 +232,7 @@ private void PreIntegration(float deltaTime) } pobj.ApplyGravity(new Vector2(0, -_gravity)); } - foreach (var constrain in _constrains) + foreach (var constrain in _constraints) { constrain.ApplyForce(deltaTime); } @@ -245,7 +245,7 @@ private void PreIntegration(float deltaTime) pobj.RecordOldState(); pobj.Update(deltaTime); } - foreach (var constrain in _constrains) + foreach (var constrain in _constraints) { constrain.Apply(deltaTime); } @@ -365,9 +365,9 @@ private void SolveFriction_Weak(CollisionEvent2D e, float deltaTime) var ri = e.LocalOffsetSrc; var rb = e.LocalOffsetTarget; - var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); + var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AngularVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); var vb = e.Target.RigidBody.LinearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + GeometryUtils.AngularVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); if (e.NormalVelOld == 0) { @@ -399,7 +399,7 @@ private void SolveFriction_Weak(CollisionEvent2D e, float deltaTime) Vector2 J = (float)J_n * vt; // + (float)J_t * va_t_unit; // var offset = e.Position - (_globalCentroid + ri); - e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); + e.Source.RigidBody.AddImpulseImmediate(J, ri, e.Target.RigidBody, -J, rb); Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); } @@ -408,9 +408,9 @@ private bool SolveContact_Weak(CollisionEvent2D e, float restitution, bool initi var ri = e.LocalOffsetSrc; var rb = e.LocalOffsetTarget; - var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); + var va = e.Source.RigidBody.LinearVelocity + GeometryUtils.AngularVelocityToLinearVelocity(ri, e.Source.RigidBody.AngularVelocity); var vb = e.Target.RigidBody.LinearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + GeometryUtils.AngularVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); float vrel_n = Vector2.Dot(va - vb, e.Normal); @@ -443,7 +443,7 @@ private bool SolveContact_Weak(CollisionEvent2D e, float restitution, bool initi e.NormalVelOld += (float)J_n; // var offset = e.Position - (_globalCentroid + ri); - e.Source.RigidBody.AddImpluseImmediate(J, ri, e.Target.RigidBody, -J, rb); + e.Source.RigidBody.AddImpulseImmediate(J, ri, e.Target.RigidBody, -J, rb); Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); return vrel_n > -0.1f; diff --git a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs index 701fcc5be..fe1dbb228 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs @@ -1,5 +1,5 @@ using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.Constrains; +using Everglow.Commons.Physics.PBEngine.Constraints; namespace Everglow.Commons.Physics.PBEngine.PlayGround.Constrain { @@ -24,7 +24,7 @@ public static PhysicsSimulation SimpleJoint() staticPlane1.Rotation = 0f; world.AddPhysicsObject(staticPlane1); - var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -40), new Vector2(0, 64)); + var joint = new JointConstraint(staticPlane1, dynamicBox, new Vector2(0, -40), new Vector2(0, 64)); world.AddConstrain(joint); return world; @@ -52,10 +52,10 @@ public static PhysicsSimulation SimpleJointMultiple() dynamicBox1.Rotation = 0.0f; world.AddPhysicsObject(dynamicBox1); - var joint = new JointConstrain(staticPlane1, dynamicBox, new Vector2(0, -50), new Vector2(0, 50)); + var joint = new JointConstraint(staticPlane1, dynamicBox, new Vector2(0, -50), new Vector2(0, 50)); world.AddConstrain(joint); - var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -70), new Vector2(0, 70)); + var joint2 = new JointConstraint(dynamicBox, dynamicBox1, new Vector2(0, -70), new Vector2(0, 70)); world.AddConstrain(joint2); // var kPlane = new PhysicsObject( @@ -84,7 +84,7 @@ public static PhysicsSimulation DynamicJoints() dynamicBox1.Rotation = 0.3f; world.AddPhysicsObject(dynamicBox1); - var joint2 = new JointConstrain(dynamicBox, dynamicBox1, new Vector2(0, -50), new Vector2(0, 50)); + var joint2 = new JointConstraint(dynamicBox, dynamicBox1, new Vector2(0, -50), new Vector2(0, 50)); world.AddConstrain(joint2); var staticPlane1 = new PhysicsObject( @@ -123,7 +123,7 @@ public static PhysicsSimulation NewtonPendulum() dynamicBox.RigidBody.LinearVelocity = new Vector2(-50, 0); } - var joint1 = new JointConstrain(staticPlane1, dynamicBox, new Vector2(64 * (i - 2), -16), new Vector2(0, 200)); + var joint1 = new JointConstraint(staticPlane1, dynamicBox, new Vector2(64 * (i - 2), -16), new Vector2(0, 200)); world.AddConstrain(joint1); } @@ -164,7 +164,7 @@ public static PhysicsSimulation SimpleSpring() staticPlane1.Rotation = 0f; world.AddPhysicsObject(staticPlane1); - var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); + var joint = new SpringConstraint(staticPlane1, dynamicBox, 29f, 200); world.AddConstrain(joint); var dynamicBox1 = new PhysicsObject( @@ -173,7 +173,7 @@ public static PhysicsSimulation SimpleSpring() dynamicBox1.Rotation = 0.0f; world.AddPhysicsObject(dynamicBox1); - var joint1 = new SpringConstrain(dynamicBox, dynamicBox1, 29f, 200); + var joint1 = new SpringConstraint(dynamicBox, dynamicBox1, 29f, 200); world.AddConstrain(joint1); return world; @@ -207,12 +207,12 @@ public static PhysicsSimulation SpringTriganluar() staticPlane3.Rotation = 0f; world.AddPhysicsObject(staticPlane3); - var joint = new SpringConstrain(staticPlane1, dynamicBox, 29f, 200); + var joint = new SpringConstraint(staticPlane1, dynamicBox, 29f, 200); world.AddConstrain(joint); - var joint1 = new SpringConstrain(staticPlane2, dynamicBox, 29f, 200); + var joint1 = new SpringConstraint(staticPlane2, dynamicBox, 29f, 200); world.AddConstrain(joint1); - var joint2 = new SpringConstrain(staticPlane3, dynamicBox, 29f, 200); + var joint2 = new SpringConstraint(staticPlane3, dynamicBox, 29f, 200); world.AddConstrain(joint2); return world; diff --git a/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs index afc3682e6..6b851f91f 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs @@ -122,7 +122,7 @@ public float Friction set => _friction = value; } - public List TangetRelativeVelocity + public List TangentRelativeVelocity { get => _contactTangentVels; } @@ -154,7 +154,7 @@ public bool IsAwake } /// - /// 物体是否可以进入沉睡状态,用户能控制的物体应该设为false + /// 物体是否可以进入沉睡状态,用户能控制的物体应该设为false /// public bool CanSleep { @@ -181,7 +181,7 @@ public bool CanSleep private List _contactTangentVels; private List _contactNormals; - private List _impluses; + private List _impulses; private bool _isAwake; private bool _canSleep; @@ -202,7 +202,7 @@ public RigidBody2D(float mass) _angularVelocity = 0; _drag = 0.06f; _angularDrag = 0.06f; - _impluses = new List(); + _impulses = new List(); _restitution = 0.5f; _friction = 0.5f; _contactTangentVels = new List(); @@ -280,7 +280,7 @@ public void CleanInformationSubstep(float deltaTime) { _torque = 0; _force = Vector2.Zero; - _impluses.Clear(); + _impulses.Clear(); // Main.NewText((_linearVelocity.LengthSquared() * _mass * 0.5f + _mass * 9.8f * _globalCentroid.Y).ToString("F1")); } @@ -302,9 +302,9 @@ public bool TryRespondTo2Events(List events, float deltaTime) var ri0 = events[0].LocalOffsetSrc; var rb0 = events[0].LocalOffsetTarget; var n0 = events[0].Normal; - var va0 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, _angularVelocity); + var va0 = _linearVelocity + GeometryUtils.AngularVelocityToLinearVelocity(ri0, _angularVelocity); var vb0 = events[0].Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); + + GeometryUtils.AngularVelocityToLinearVelocity(rb0, events[0].Target.RigidBody._angularVelocity); float relv0_n = Vector2.Dot(va0 - vb0, n0); if (relv0_n >= 0) { @@ -314,9 +314,9 @@ public bool TryRespondTo2Events(List events, float deltaTime) var ri1 = events[1].LocalOffsetSrc; var rb1 = events[1].LocalOffsetTarget; var n1 = events[1].Normal; - var va1 = _linearVelocity + GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, _angularVelocity); + var va1 = _linearVelocity + GeometryUtils.AngularVelocityToLinearVelocity(ri1, _angularVelocity); var vb1 = events[1].Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); + + GeometryUtils.AngularVelocityToLinearVelocity(rb1, events[1].Target.RigidBody._angularVelocity); float relv1_n = Vector2.Dot(va1 - vb1, n1); if (relv1_n >= 0) { @@ -326,32 +326,32 @@ public bool TryRespondTo2Events(List events, float deltaTime) { [0, 0] = InvMass + events[0].Target.RigidBody.InvMass + Vector2.Dot( - GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + GeometryUtils.AngularVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor * GeometryUtils.Cross(ri0, n0))), n0) + Vector2.Dot( - GeometryUtils.AnuglarVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor + GeometryUtils.AngularVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor * GeometryUtils.Cross(rb0, n0))), n0), [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot( - GeometryUtils.AnuglarVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor + GeometryUtils.AngularVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor * GeometryUtils.Cross(ri1, n1))), n0), [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot( - GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + GeometryUtils.AngularVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor * GeometryUtils.Cross(ri0, n0))), n1), [1, 1] = InvMass + events[1].Target.RigidBody.InvMass + Vector2.Dot( - GeometryUtils.AnuglarVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor + GeometryUtils.AngularVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor * GeometryUtils.Cross(ri1, n1))), n1) + Vector2.Dot( - GeometryUtils.AnuglarVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor + GeometryUtils.AngularVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor * GeometryUtils.Cross(rb1, n1))), n1), }; float C = 0.4f; var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); if (!J.HasNaNs()) { - AddImpluse(J.X * events[0].Normal, ri0, target.RigidBody, -J.X * events[0].Normal, rb0); - AddImpluse(J.Y * events[1].Normal, ri1, target.RigidBody, -J.Y * events[1].Normal, rb1); + AddImpulse(J.X * events[0].Normal, ri0, target.RigidBody, -J.X * events[0].Normal, rb0); + AddImpulse(J.Y * events[1].Normal, ri1, target.RigidBody, -J.Y * events[1].Normal, rb1); } return true; } @@ -361,15 +361,15 @@ public void ApplyAngularVelocity(float w) _angularVelocity += w; } - private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) + private void SolveContactImpulse(CollisionEvent2D e, int count, float deltaTime) { var ri = e.LocalOffsetSrc; var rb = e.LocalOffsetTarget; - var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); + var v2 = GeometryUtils.AngularVelocityToLinearVelocity(ri, _angularVelocity); var va = _linearVelocity + v2; var vb = e.Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); + + GeometryUtils.AngularVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); var bias = 0.1f * Math.Max(e.Depth - 0.02f, 0) / deltaTime; float va_n = Vector2.Dot(va - vb, e.Normal); @@ -403,19 +403,19 @@ private void SolveContactImpluse(CollisionEvent2D e, int count, float deltaTime) e.NormalVelOld = (float)J_n; // var offset = e.Position - (_globalCentroid + ri); - AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + AddImpulse(J, ri, e.Target.RigidBody, -J, rb); Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); } - private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime) + private void SolveFrictionImpulse(CollisionEvent2D e, int count, float deltaTime) { var ri = e.LocalOffsetSrc; var rb = e.LocalOffsetTarget; - var v2 = GeometryUtils.AnuglarVelocityToLinearVelocity(ri, _angularVelocity); + var v2 = GeometryUtils.AngularVelocityToLinearVelocity(ri, _angularVelocity); var va = _linearVelocity + v2; var vb = e.Target.RigidBody._linearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); + + GeometryUtils.AngularVelocityToLinearVelocity(rb, e.Target.RigidBody._angularVelocity); if (e.NormalVelOld == 0) { @@ -455,7 +455,7 @@ private void SolveFrictionImpluse(CollisionEvent2D e, int count, float deltaTime double J_n = -Math.Min(friction * e.NormalVelOld, vel_t / effectiveMass); Vector2 J = (float)J_n * vt / count; // + (float)J_t * va_t_unit; - AddImpluse(J, ri, e.Target.RigidBody, -J, rb); + AddImpulse(J, ri, e.Target.RigidBody, -J, rb); Debug.Assert(!float.IsNaN(J.X) && !float.IsNaN(J.Y)); } @@ -468,15 +468,15 @@ public void RespondToEvents(List events, float deltaTime) { foreach (var e in events) { - SolveContactImpluse(e, events.Count, deltaTime); + SolveContactImpulse(e, events.Count, deltaTime); } - ApplyImpluses(); + ApplyImpulses(); foreach (var e in events) { - SolveFrictionImpluse(e, events.Count, deltaTime); + SolveFrictionImpulse(e, events.Count, deltaTime); MatchAwakeState(e.Target.RigidBody, deltaTime); } - ApplyImpluses(); + ApplyImpulses(); } public void Integrate(float deltaTime) @@ -492,24 +492,24 @@ public void Integrate(float deltaTime) // public void ResolveImpluse(float deltaTime) // { - // if (_impluses.Count == 0) + // if (_impulses.Count == 0) // { // return; // } // //_bindObject.Rotation = _bindObject.OldRotation; // //_globalCentroid = _bindObject.OldPosition; - // _impluses.Sort((a, b) => + // _impulses.Sort((a, b) => // { // return a.Time.CompareTo(b.Time); // }); // float lastTime = 0; // int sameTimeCount = 0; - // for (int i = 0; i < _impluses.Count; i++) + // for (int i = 0; i < _impulses.Count; i++) // { // sameTimeCount++; - // if (i == _impluses.Count - 1 || _impluses[i].Time != _impluses[i + 1].Time) + // if (i == _impulses.Count - 1 || _impulses[i].Time != _impulses[i + 1].Time) // { - // float dt = (_impluses[i].Time - lastTime); + // float dt = (_impulses[i].Time - lastTime); // Vector2 linearVelocityChange = Vector2.Zero; // float angularVelocityChange = 0; @@ -523,66 +523,66 @@ public void Integrate(float deltaTime) // var resImpluses = ResolveConstrains(); // for (int j = i - sameTimeCount + 1; j <= i; j++) // { - // linearVelocityChange += 1.0f / _mass * resImpluses[j] * _impluses[j].Normal; - // angularVelocityChange += (float)(Utils.Cross(_impluses[j].RelativePosition, - // resImpluses[j] *_impluses[j].Normal * (float)_globalInverseInertiaTensor)); + // linearVelocityChange += 1.0f / _mass * resImpluses[j] * _impulses[j].Normal; + // angularVelocityChange += (float)(Utils.Cross(_impulses[j].RelativePosition, + // resImpluses[j] *_impulses[j].Normal * (float)_globalInverseInertiaTensor)); // } // //var penetration = Math.Max(0, -Vector2.Dot(_linearVelocity * deltaTime, normalAvg)); // //_linearVelocity += normalAvg * penetration * 0.7f; // deltaTime -= dt; - // lastTime = _impluses[i].Time; + // lastTime = _impulses[i].Time; // sameTimeCount = 0; // } // } - // _impluses.Clear(); + // _impulses.Clear(); // } // private List ResolveConstrains() // { // List impluseArray = new List(); // List impluseArrayTemp = new List(); - // for (int k = 0; k < _impluses.Count; k++) + // for (int k = 0; k < _impulses.Count; k++) // { - // impluseArray.Add(_impluses[k].Impluse.Length()); + // impluseArray.Add(_impulses[k].Impluse.Length()); // impluseArrayTemp.Add(0f); // } // for (int iter = 0; iter < 8; iter++) // { // int sameSourceCount = 0; - // for (int k = 0; k < _impluses.Count; k++) + // for (int k = 0; k < _impulses.Count; k++) // { - // var ri0 = _impluses[k].CollisionEvent.LocalOffsetSrc; - // var rb0 = _impluses[k].CollisionEvent.LocalOffsetTarget; - // var n0 = _impluses[k].CollisionEvent.Normal; - // var va0 = _impluses[k].CollisionEvent.Source.RigidBody.LinearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(ri0, _impluses[k].CollisionEvent.Source.RigidBody.AngularVelocity); - // var vb0 = _impluses[k].CollisionEvent.Target.RigidBody.LinearVelocity - // + Utils.AnuglarVelocityToLinearVelocity(rb0, _impluses[k].CollisionEvent.Target.RigidBody.AngularVelocity); + // var ri0 = _impulses[k].CollisionEvent.LocalOffsetSrc; + // var rb0 = _impulses[k].CollisionEvent.LocalOffsetTarget; + // var n0 = _impulses[k].CollisionEvent.Normal; + // var va0 = _impulses[k].CollisionEvent.Source.RigidBody.LinearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(ri0, _impulses[k].CollisionEvent.Source.RigidBody.AngularVelocity); + // var vb0 = _impulses[k].CollisionEvent.Target.RigidBody.LinearVelocity + // + Utils.AnuglarVelocityToLinearVelocity(rb0, _impulses[k].CollisionEvent.Target.RigidBody.AngularVelocity); // float relv0_n = Vector2.Dot(va0 - vb0, n0); // float relv0_plus = -(1 + 0.4f) * Vector2.Dot(va0 - vb0, n0); - // float Kj = _impluses[k].CollisionEvent.Source.RigidBody.InvMass + _impluses[k].CollisionEvent.Target.RigidBody.InvMass - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor + // float Kj = _impulses[k].CollisionEvent.Source.RigidBody.InvMass + _impulses[k].CollisionEvent.Target.RigidBody.InvMass + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, (float)(_impulses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor // * Utils.Cross(ri0, n0))), n0) - // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(_impluses[k].CollisionEvent.Target.RigidBody.GlobalInverseInertiaTensor + // + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb0, (float)(_impulses[k].CollisionEvent.Target.RigidBody.GlobalInverseInertiaTensor // * Utils.Cross(rb0, n0))), n0); // float KLU = 0f; - // for (int l = 0; l < _impluses.Count; l++) + // for (int l = 0; l < _impulses.Count; l++) // { // if (l == k) // { // continue; // } - // var ri1 = _impluses[l].CollisionEvent.LocalOffsetSrc; - // var rb1 = _impluses[l].CollisionEvent.LocalOffsetTarget; - // var n1 = _impluses[l].CollisionEvent.Normal; + // var ri1 = _impulses[l].CollisionEvent.LocalOffsetSrc; + // var rb1 = _impulses[l].CollisionEvent.LocalOffsetTarget; + // var n1 = _impulses[l].CollisionEvent.Normal; - // KLU += (_impluses[k].CollisionEvent.Source.RigidBody.InvMass + // KLU += (_impulses[k].CollisionEvent.Source.RigidBody.InvMass // * Vector2.Dot(n0, n1) + Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri0, - // (float)(_impluses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor + // (float)(_impulses[k].CollisionEvent.Source.RigidBody.GlobalInverseInertiaTensor // * Utils.Cross(ri1, n1))), n0)) * impluseArray[l]; // } // impluseArrayTemp[k] = 1 / Kj * (relv0_plus - KLU); @@ -611,43 +611,43 @@ public void AddForce(Vector2 force, Vector2 relPos) /// /// /// - public void AddImpluse(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) + public void AddImpulse(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) { - var entry = new ImpluseEntry() + var entry = new ImpulseEntry() { Source = this, Target = other, - ImpluseSource = J, + ImpulseSource = J, RelativePositionSource = relativePos, - ImpluseTarget = J2, + ImpulseTarget = J2, RelativePositionTarget = relativePos2, }; - _impluses.Add(entry); + _impulses.Add(entry); } /// /// 对累加的冲量进行应用,更新各种速度 /// - public void ApplyImpluses() + public void ApplyImpulses() { - if (_impluses.Count > 0) + if (_impulses.Count > 0) { - foreach (var imp in _impluses) + foreach (var imp in _impulses) { if (imp.Source.MovementType == MovementType.Dynamic || imp.Source.MovementType == MovementType.Player) { - _linearVelocity += 1.0f / _mass * imp.ImpluseSource; + _linearVelocity += 1.0f / _mass * imp.ImpulseSource; if (imp.Source.MovementType != MovementType.Player) { - _angularVelocity += GeometryUtils.Cross(imp.RelativePositionSource, imp.ImpluseSource * (float)_globalInverseInertiaTensor); + _angularVelocity += GeometryUtils.Cross(imp.RelativePositionSource, imp.ImpulseSource * (float)_globalInverseInertiaTensor); } } if (imp.Target.MovementType == MovementType.Dynamic || imp.Target.MovementType == MovementType.Player) { - imp.Target._linearVelocity += imp.Target.InvMass * imp.ImpluseTarget; + imp.Target._linearVelocity += imp.Target.InvMass * imp.ImpulseTarget; if (imp.Target.MovementType != MovementType.Player) { - imp.Target._angularVelocity += GeometryUtils.Cross(imp.RelativePositionTarget, imp.ImpluseTarget * (float)imp.Target.GlobalInverseInertiaTensor); + imp.Target._angularVelocity += GeometryUtils.Cross(imp.RelativePositionTarget, imp.ImpulseTarget * (float)imp.Target.GlobalInverseInertiaTensor); } } } @@ -656,7 +656,7 @@ public void ApplyImpluses() Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); Debug.Assert(!float.IsNaN(_angularVelocity)); StabilizeBody(); - _impluses.Clear(); + _impulses.Clear(); } /// @@ -664,7 +664,7 @@ public void ApplyImpluses() /// /// /// - public void AddImpluseImmediate(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) + public void AddImpulseImmediate(Vector2 J, Vector2 relativePos, RigidBody2D other, Vector2 J2, Vector2 relativePos2) { if (MovementType != MovementType.Static && MovementType != MovementType.Kinematic) { diff --git a/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs b/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs deleted file mode 100644 index 6a4e3ab46..000000000 --- a/Sources/Everglow.Function/Physics/PhysicWorldSystem.cs +++ /dev/null @@ -1,444 +0,0 @@ -using System.Data; -using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; -using Everglow.Commons.Physics.PBEngine.Collision.Colliders; -using Everglow.Commons.Physics.PBEngine.Constrains; -using Everglow.Commons.Physics.PBEngine.GameInteraction; -using Everglow.Commons.Vertex; -using Microsoft.Xna.Framework.Input; -using ReLogic.Content; -using ReLogic.Graphics; -using Terraria.GameContent; - -namespace Everglow.Commons.Physics; - -public class PhysicsPlayer : ModPlayer -{ - private PhysicsObject _movingPanel; - private Vector2 _prevVelocity; - private float _simulationDt = 0.1f; - public Vector2 _extraVelocity; - public bool _shouldStand; - - public override void OnEnterWorld() - { - } - - public override void PostUpdate() - { - Main.LocalPlayer.velocity += _extraVelocity; - _extraVelocity = Vector2.Zero; - if (Main.LocalPlayer.velocity.Y == 0 - && PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count > 0) - { - Vector2 tangent = Vector2.Zero; - foreach (var v in PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity) - { - tangent += v; - } - tangent /= PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity.Count; - _extraVelocity = tangent * _simulationDt; - _extraVelocity = Vector2.Dot(_extraVelocity, new Vector2(1, 0)) * new Vector2(1, 0); - } - } - - public override void PreUpdate() - { - _shouldStand = false; - _prevVelocity = Main.LocalPlayer.velocity; - base.PreUpdate(); - } - - public override void PreUpdateMovement() - { - //// Main.LocalPlayer.Center = Physics.Utils.ConvertToPhysicsSpace(Display.Instance._dummyPlayer.Position); - // Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicWorldSystem.Instance._dummyPlayer.RigidBody.LinearVelocity) * dt; - // - // var t = PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity; - // Main.NewText(Main.LocalPlayer.velocity, Color.GreenYellow); - Player.velocity -= _extraVelocity; - - float dt = 0.1f; - var oldPos = Main.LocalPlayer.Center; - var preVelY = Main.LocalPlayer.velocity.Y; - var preVel = Main.LocalPlayer.velocity; - - var broadPhase = PhysicWorldSystem.Instance._realSimulation.BroadPhaseCollisionDetector; - - var temporaryObjects = new List(); - - foreach (var proj in Main.projectile) - { - if (proj.active && proj.tileCollide) - { - if (broadPhase.TestSingleCollision(proj.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() - { - "Default", - })) - { - var obj = new PhysicsObject(new BoxCollider(proj.width, proj.height), new RigidBody2D(64)); - obj.Position = GeometryUtils.ConvertToPhysicsSpace(proj.Center); - obj.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(proj.velocity / dt); - obj.RigidBody.MovementType = MovementType.Player; - obj.Tag = "Player"; - obj.RigidBody.Drag = 0; - temporaryObjects.Add(obj); - } - } - } - - foreach (var npc in Main.npc) - { - if (npc.active && !npc.noTileCollide) - { - if (broadPhase.TestSingleCollision(npc.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() - { - "Default", - })) - { - var obj = new PhysicsObject(new BoxCollider(npc.width, npc.height), new RigidBody2D(512)); - obj.Position = GeometryUtils.ConvertToPhysicsSpace(npc.Center); - obj.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(npc.velocity / dt); - obj.RigidBody.MovementType = MovementType.Player; - obj.RigidBody.Drag = 0; - obj.Tag = "Player"; - temporaryObjects.Add(obj); - } - } - } - - foreach (var obj in temporaryObjects) - { - PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(obj); - } - - // Main.NewText(Main.LocalPlayer.velocity, Color.Lime); - PhysicWorldSystem.Instance._dummyPlayer.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.velocity / dt); - PhysicWorldSystem.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(oldPos); - PhysicWorldSystem.Instance._realSimulation.Update(dt); - - foreach (var obj in temporaryObjects) - { - PhysicWorldSystem.Instance._realSimulation.RemoveObject(obj); - } - - // Main.LocalPlayer.Center = GeometryUtils.ConvertToPhysicsSpace(PhysicWorldSystem.Instance._dummyPlayer.RigidBody.CentroidWorldSpace) - player.; - // Main.NewText(Main.LocalPlayer.velocity,Color.Cyan); - - // Find any valid, standable slope - if (PhysicWorldSystem.Instance._dummyPlayer.RigidBody.ContactNormals.Count > 0) - { - foreach (var v in PhysicWorldSystem.Instance._dummyPlayer.RigidBody.ContactNormals) - { - if (v.Y > 0.9f) - { - _shouldStand = true; - - // Omit tangent velocity due to gravity - float mg = -PhysicWorldSystem.Instance._realSimulation.Gravity; - Vector2 velDueG = new Vector2(0, -Player.gravDir * Player.gravity / dt) * dt; - Vector2 normalDir = Vector2.Dot(velDueG, v) * v; - Vector2 tanDir = velDueG - normalDir; - Player.velocity.Y = 0; - break; - } - } - } - - // if (_shouldStand) - // { - // Main.LocalPlayer.velocity = Main.LocalPlayer.velocity - _prevVelocity; - - // } - - // if (t.Length() > 0 && Math.Abs(GeometryUtils.Cross(new Vector2(1, 0), t)) < 0.3f) - // { - // Main.LocalPlayer.velocity.Y = 0; - // } - // Main.LocalPlayer.velocity -= _extraVelocity; - if (_movingPanel != null) - { - if (Main.time % 1200 < 600) - { - _movingPanel.RigidBody.LinearVelocity = new Vector2(-4, 0); - } - else if (Main.time % 1200 >= 600) - { - _movingPanel.RigidBody.LinearVelocity = new Vector2(4, 0); - } - } - - base.PreUpdateMovement(); - } - - public override void UpdateEquips() - { - base.UpdateEquips(); - } - - public override void SetControls() - { - base.SetControls(); - if (Main.mouseLeft && Main.mouseLeftRelease) - { - for (int i = 0; i < 2; i++) - { - // var ball = new PhysicsObject( - // new BoxCollider(32, 32), - // new RigidBody2D(256)); - // PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(ball); - // ball.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld) + new Vector2(i, 0); - // ball.RigidBody.LinearVelocity = new Vector2(0, 0); - } - } - - if (Main.keyState[Keys.T] == KeyState.Down && Main.oldKeyState[Keys.T] == KeyState.Up) - { - var dynamicBox = new PhysicsObject( - new BoxCollider(128, 32), new RigidBody2D(256)); - - // _movingPanel = dynamicBox; - dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -200)); - dynamicBox.Rotation = 0.0f; - dynamicBox.RigidBody.MovementType = MovementType.Dynamic; - dynamicBox.RigidBody.UseGravity = true; - dynamicBox.RigidBody.Drag = 0.1f; - PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(dynamicBox); - - var staticPlane = new PhysicsObject( - new BoxCollider(256, 128), null); - staticPlane.Rotation = 0.3f; - staticPlane.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(200, -200)); - PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(staticPlane); - - // var ball = new PhysicsObject(new BoxCollider(32, 32), - // new RigidBody2D(256)); - // Display.Instance._realSimulation.AddPhysicsObject(ball); - // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -300)); - var staticPlane1 = new PhysicsObject( - new BoxCollider(128, 32), null); - staticPlane1.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(-200, -200)); - staticPlane1.Rotation = 0.0f; - staticPlane1.RigidBody.MovementType = MovementType.Static; - staticPlane1.RigidBody.UseGravity = false; - PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(staticPlane1); - - // _movingPanel = staticPlane1; - var joint = new SpringConstrain(staticPlane1, dynamicBox, 100f, 144f, new Vector2(64, -16), new Vector2(64, 16)); - PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint); - var joint11 = new SpringConstrain(staticPlane1, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(-64, 16)); - PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint11); - - // var joint2 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, 16), new Vector2(64, -16)); - // PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint2); - // var joint22 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(64, 16)); - // PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint22); - - // if (Main.rand.NextBool(2)) - // { - // var ball = new PhysicsObject(new BoxCollider(256, 32), - // new RigidBody2D(128)); - // ball.RigidBody.MovementType = Physics.Collision.MovementType.Kinematic; - // ball.RigidBody.UseGravity = false; - // ball.RigidBody.AngularDrag = 0; - // ball.RigidBody.AngularVelocity = 0.9f; - // Display.Instance._realSimulation.AddPhysicsObject(ball); - // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -100)); - // } - // else - // { - // var ball = new PhysicsObject(new SphereCollider(32), - // new RigidBody2D(256)); - // Display.Instance._realSimulation.AddPhysicsObject(ball); - // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(100, 0)); - // ball.RigidBody.LinearVelocity = new Vector2(-78, 0); - // } - } - } -} - -public class PhysicWorldSystem : ModSystem -{ - public PhysicsSimulation _realSimulation; - public static PhysicWorldSystem Instance; - public PhysicsObject _dummyPlayer; - - public const float Simulation_DeltaTime = 0.1f; - - public override void PostUpdateEverything() - { - } - - public override void Load() - { - Instance = this; - ReStart(); - On_Player.PlayerFrame += On_Player_PlayerFrame; - On_Collision.TileCollision += On_Collision_TileCollision; - base.Load(); - } - - private Vector2 On_Collision_TileCollision(On_Collision.orig_TileCollision orig, Vector2 Position, Vector2 Velocity, int Width, int Height, bool fallThrough, bool fall2, int gravDir) - { - Position = GeometryUtils.ConvertToPhysicsSpace(Position); - Velocity = GeometryUtils.ConvertToPhysicsSpace(Velocity); - var rect = new AABB(Position - new Vector2(0, Height), Position + new Vector2(Width, 0)); - var oldCenter = rect.Center; - - rect = rect.Move(Velocity); - - var collisions = _realSimulation.BroadPhaseCollisionDetector.GetSingleCollision(rect, Vector2.Zero, 0, new List() - { - "Default", - }); - - if (collisions.Count > 0) - { - var dummyCollider = new BoxCollider(Width, Height); - var dummyPObject = new PhysicsObject(dummyCollider, new RigidBody2D(64)); - dummyPObject.OldPosition = rect.Center; - dummyPObject.RigidBody.CentroidWorldSpace = rect.Center; - - foreach (var collider in collisions) - { - CollisionInfo info; - if (dummyCollider.TestCollisionCondition(collider, Simulation_DeltaTime, out info)) - { - float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); - float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); - - // if (weightA != 0 && weightB != 0) - // weightA = weightB = 0.5f; - - // Vector2 impluseVel = Vector2.Zero; - // List events; - // dummyCollider.GetContactInfo(info, Simulation_DeltaTime, out events); - // foreach(var e in events) - // { - // impluseVel += SolveContactImpluse(e, result + velChange, events.Count, Simulation_DeltaTime); - // } - // dummyPObject.RigidBody.MoveBody(info.Normal * weightA * info.Depth, Simulation_DeltaTime); - if (Math.Abs(info.Normal.Y) < 0.77f) - { - dummyPObject.RigidBody.MoveBody(info.Normal * weightA * info.Depth, Simulation_DeltaTime); - } - else - { - dummyPObject.RigidBody.MoveBody(info.Depth / info.Normal.Y * new Vector2(0, 1), Simulation_DeltaTime); - } - - // if (float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.X) || float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.Y)) - // { - // if (true) - // ; - // } - // extraVel = collider.ParentObject.RigidBody.LinearVelocity * Simulation_DeltaTime; - // var n = Vector2.Dot(result, info.Normal) * info.Normal; - // result = result - n; - // velChange += impluseVel; - } - } - - // Main.NewText(result + dummyCollider.ParentObject.Position - oldCenter, Color.Red); - // var newDir = result + new Vector2(0, d); - Velocity = dummyCollider.ParentObject.Position - oldCenter; - } - - Vector2 result = orig(GeometryUtils.ConvertToPhysicsSpace(Position), GeometryUtils.ConvertToPhysicsSpace(Velocity), Width, Height, fallThrough, fall2, gravDir); - return result; - } - - /// - /// 返回接触响应后的速度的变化量 - /// - /// - /// - /// - /// - private Vector2 SolveContactImpluse(CollisionEvent2D e, Vector2 sourceVel, int count, float deltaTime) - { - float mass = 64; - var ri = e.LocalOffsetSrc; - var rb = e.LocalOffsetTarget; - - var va = sourceVel; - var vb = e.Target.RigidBody.LinearVelocity - + GeometryUtils.AnuglarVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); - - float va_n = Vector2.Dot(va - vb, e.Normal); - if (va_n >= 0) - { - e.NormalVelOld = 0; - return Vector2.Zero; - } - float stiffness = 0; - float vnew_n = stiffness * Math.Max(-va_n - 20 * deltaTime, 0); - - // (a × b) × c = (c • a)b - (c • b)a - double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); - double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); - double R1 = 0; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor - - // * Utils.Cross(ri, e.Normal))), e.Normal); - double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor - - // * Utils.Cross(rb, e.Normal))), e.Normal); - double J_n = (vnew_n - va_n) / (1.0 / mass + e.Target.RigidBody.InvMass + R1 + R2); - Vector2 J = (float)J_n * e.Normal / count; // + (float)J_t * va_t_unit; - - return J / mass; - } - - private void On_Player_PlayerFrame(On_Player.orig_PlayerFrame orig, Player self) - { - Main.LocalPlayer.velocity += self.GetModPlayer()._extraVelocity; - - if (self.GetModPlayer()._shouldStand) - { - Main.LocalPlayer.velocity.Y = 0; - } - orig(self); - Main.LocalPlayer.velocity -= self.GetModPlayer()._extraVelocity; - } - - public override void PostDrawInterface(SpriteBatch sb) - { - - } - - public void ReStart() - { - if(_realSimulation is not null) - { - _realSimulation.ClearPhysicsObjects(); - } - var terrariaCollisionGroup = new CollisionGraph(); - _realSimulation = new PhysicsSimulation(terrariaCollisionGroup); - terrariaCollisionGroup.AddSingleEdge("Default", "Default"); - terrariaCollisionGroup.AddSingleEdge("Default", "Terrain"); - terrariaCollisionGroup.AddDoubleEdge("Player", "Default"); - - var terrain = new PhysicsObject(new TileCollider(), null); - _realSimulation.AddPhysicsObject(terrain); - terrain.Tag = "Terrain"; - terrain.RigidBody.MovementType = MovementType.Static; - - var rigidb = new RigidBody2D(64); - _dummyPlayer = new PhysicsObject(new BoxCollider(20, 42), rigidb); - _dummyPlayer.Tag = "Player"; - rigidb.MovementType = MovementType.Player; - rigidb.UseGravity = false; - rigidb.AngularDrag = 0; - rigidb.Drag = 0; - rigidb.Restitution = -1; - rigidb.Friction = -1; - _realSimulation.AddPhysicsObject(_dummyPlayer); - _realSimulation.Initialize(); - } - - public override void Unload() - { - base.Unload(); - } -} \ No newline at end of file diff --git a/Sources/Everglow.Function/Physics/PhysicsPlayer.cs b/Sources/Everglow.Function/Physics/PhysicsPlayer.cs new file mode 100644 index 000000000..e3c4b1216 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PhysicsPlayer.cs @@ -0,0 +1,259 @@ +using System.Data; +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Constraints; +using Everglow.Commons.Physics.PBEngine.GameInteraction; +using Everglow.Commons.Vertex; +using Microsoft.Xna.Framework.Input; +using ReLogic.Content; +using ReLogic.Graphics; +using Terraria.GameContent; + +namespace Everglow.Commons.Physics; + +public class PhysicsPlayer : ModPlayer +{ + private PhysicsObject _movingPanel; + private Vector2 _prevVelocity; + private float _simulationDt = 0.1f; + public Vector2 _extraVelocity; + public bool _shouldStand; + + public override void OnEnterWorld() + { + } + + public override void PostUpdate() + { + Main.LocalPlayer.velocity += _extraVelocity; + _extraVelocity = Vector2.Zero; + if (Main.LocalPlayer.velocity.Y == 0 + && PhysicsWorldSystem.Instance._dummyPlayer.RigidBody.TangentRelativeVelocity.Count > 0) + { + Vector2 tangent = Vector2.Zero; + foreach (var v in PhysicsWorldSystem.Instance._dummyPlayer.RigidBody.TangentRelativeVelocity) + { + tangent += v; + } + tangent /= PhysicsWorldSystem.Instance._dummyPlayer.RigidBody.TangentRelativeVelocity.Count; + _extraVelocity = tangent * _simulationDt; + _extraVelocity = Vector2.Dot(_extraVelocity, new Vector2(1, 0)) * new Vector2(1, 0); + } + } + + public override void PreUpdate() + { + _shouldStand = false; + _prevVelocity = Main.LocalPlayer.velocity; + base.PreUpdate(); + } + + public override void PreUpdateMovement() + { + //// Main.LocalPlayer.Center = Physics.Utils.ConvertToPhysicsSpace(Display.Instance._dummyPlayer.Position); + // Main.LocalPlayer.velocity = GeometryUtils.ConvertToPhysicsSpace(PhysicWorldSystem.Instance._dummyPlayer.RigidBody.LinearVelocity) * dt; + // + // var t = PhysicWorldSystem.Instance._dummyPlayer.RigidBody.TangetRelativeVelocity; + // Main.NewText(Main.LocalPlayer.velocity, Color.GreenYellow); + Player.velocity -= _extraVelocity; + + float dt = 0.1f; + var oldPos = Main.LocalPlayer.Center; + var preVelY = Main.LocalPlayer.velocity.Y; + var preVel = Main.LocalPlayer.velocity; + + var broadPhase = PhysicsWorldSystem.Instance._realSimulation.BroadPhaseCollisionDetector; + + var temporaryObjects = new List(); + + foreach (var proj in Main.projectile) + { + if (proj.active && proj.tileCollide) + { + if (broadPhase.TestSingleCollision(proj.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() + { + "Default", + })) + { + var obj = new PhysicsObject(new BoxCollider(proj.width, proj.height), new RigidBody2D(64)); + obj.Position = GeometryUtils.ConvertToPhysicsSpace(proj.Center); + obj.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(proj.velocity / dt); + obj.RigidBody.MovementType = MovementType.Player; + obj.Tag = "Player"; + obj.RigidBody.Drag = 0; + temporaryObjects.Add(obj); + } + } + } + + foreach (var npc in Main.npc) + { + if (npc.active && !npc.noTileCollide) + { + if (broadPhase.TestSingleCollision(npc.Hitbox.ToAABBPhysSpace().EnLarge(8 * 16), Vector2.Zero, 0, new List() + { + "Default", + })) + { + var obj = new PhysicsObject(new BoxCollider(npc.width, npc.height), new RigidBody2D(512)); + obj.Position = GeometryUtils.ConvertToPhysicsSpace(npc.Center); + obj.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(npc.velocity / dt); + obj.RigidBody.MovementType = MovementType.Player; + obj.RigidBody.Drag = 0; + obj.Tag = "Player"; + temporaryObjects.Add(obj); + } + } + } + + foreach (var obj in temporaryObjects) + { + PhysicsWorldSystem.Instance._realSimulation.AddPhysicsObject(obj); + } + + // Main.NewText(Main.LocalPlayer.velocity, Color.Lime); + PhysicsWorldSystem.Instance._dummyPlayer.RigidBody.LinearVelocity = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.velocity / dt); + PhysicsWorldSystem.Instance._dummyPlayer.Position = GeometryUtils.ConvertToPhysicsSpace(oldPos); + PhysicsWorldSystem.Instance._realSimulation.Update(dt); + + foreach (var obj in temporaryObjects) + { + PhysicsWorldSystem.Instance._realSimulation.RemoveObject(obj); + } + + // Main.LocalPlayer.Center = GeometryUtils.ConvertToPhysicsSpace(PhysicWorldSystem.Instance._dummyPlayer.RigidBody.CentroidWorldSpace) - player.; + // Main.NewText(Main.LocalPlayer.velocity,Color.Cyan); + + // Find any valid, standable slope + if (PhysicsWorldSystem.Instance._dummyPlayer.RigidBody.ContactNormals.Count > 0) + { + foreach (var v in PhysicsWorldSystem.Instance._dummyPlayer.RigidBody.ContactNormals) + { + if (v.Y > 0.9f) + { + _shouldStand = true; + + // Omit tangent velocity due to gravity + float mg = -PhysicsWorldSystem.Instance._realSimulation.Gravity; + Vector2 velDueG = new Vector2(0, -Player.gravDir * Player.gravity / dt) * dt; + Vector2 normalDir = Vector2.Dot(velDueG, v) * v; + Vector2 tanDir = velDueG - normalDir; + Player.velocity.Y = 0; + break; + } + } + } + + // if (_shouldStand) + // { + // Main.LocalPlayer.velocity = Main.LocalPlayer.velocity - _prevVelocity; + + // } + + // if (t.Length() > 0 && Math.Abs(GeometryUtils.Cross(new Vector2(1, 0), t)) < 0.3f) + // { + // Main.LocalPlayer.velocity.Y = 0; + // } + // Main.LocalPlayer.velocity -= _extraVelocity; + if (_movingPanel != null) + { + if (Main.time % 1200 < 600) + { + _movingPanel.RigidBody.LinearVelocity = new Vector2(-4, 0); + } + else if (Main.time % 1200 >= 600) + { + _movingPanel.RigidBody.LinearVelocity = new Vector2(4, 0); + } + } + + base.PreUpdateMovement(); + } + + public override void UpdateEquips() + { + base.UpdateEquips(); + } + + public override void SetControls() + { + base.SetControls(); + if (Main.mouseLeft && Main.mouseLeftRelease) + { + for (int i = 0; i < 2; i++) + { + // var ball = new PhysicsObject( + // new BoxCollider(32, 32), + // new RigidBody2D(256)); + // PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld) + new Vector2(i, 0); + // ball.RigidBody.LinearVelocity = new Vector2(0, 0); + } + } + + if (Main.keyState[Keys.T] == KeyState.Down && Main.oldKeyState[Keys.T] == KeyState.Up) + { + var dynamicBox = new PhysicsObject( + new BoxCollider(128, 32), new RigidBody2D(256)); + + // _movingPanel = dynamicBox; + dynamicBox.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -200)); + dynamicBox.Rotation = 0.0f; + dynamicBox.RigidBody.MovementType = MovementType.Dynamic; + dynamicBox.RigidBody.UseGravity = true; + dynamicBox.RigidBody.Drag = 0.1f; + PhysicsWorldSystem.Instance._realSimulation.AddPhysicsObject(dynamicBox); + + var staticPlane = new PhysicsObject( + new BoxCollider(256, 128), null); + staticPlane.Rotation = 0.3f; + staticPlane.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(200, -200)); + PhysicsWorldSystem.Instance._realSimulation.AddPhysicsObject(staticPlane); + + // var ball = new PhysicsObject(new BoxCollider(32, 32), + // new RigidBody2D(256)); + // Display.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -300)); + var staticPlane1 = new PhysicsObject( + new BoxCollider(128, 32), null); + staticPlane1.Position = GeometryUtils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(-200, -200)); + staticPlane1.Rotation = 0.0f; + staticPlane1.RigidBody.MovementType = MovementType.Static; + staticPlane1.RigidBody.UseGravity = false; + PhysicsWorldSystem.Instance._realSimulation.AddPhysicsObject(staticPlane1); + + // _movingPanel = staticPlane1; + var joint = new SpringConstraint(staticPlane1, dynamicBox, 100f, 144f, new Vector2(64, -16), new Vector2(64, 16)); + PhysicsWorldSystem.Instance._realSimulation.AddConstrain(joint); + var joint11 = new SpringConstraint(staticPlane1, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(-64, 16)); + PhysicsWorldSystem.Instance._realSimulation.AddConstrain(joint11); + + // var joint2 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, 16), new Vector2(64, -16)); + // PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint2); + // var joint22 = new SpringConstrain(staticPlane, dynamicBox, 100f, 144f, new Vector2(-64, -16), new Vector2(64, 16)); + // PhysicWorldSystem.Instance._realSimulation.AddConstrain(joint22); + + // if (Main.rand.NextBool(2)) + // { + // var ball = new PhysicsObject(new BoxCollider(256, 32), + // new RigidBody2D(128)); + // ball.RigidBody.MovementType = Physics.Collision.MovementType.Kinematic; + // ball.RigidBody.UseGravity = false; + // ball.RigidBody.AngularDrag = 0; + // ball.RigidBody.AngularVelocity = 0.9f; + // Display.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(0, -100)); + // } + // else + // { + // var ball = new PhysicsObject(new SphereCollider(32), + // new RigidBody2D(256)); + // Display.Instance._realSimulation.AddPhysicsObject(ball); + // ball.Position = Physics.Utils.ConvertToPhysicsSpace(Main.LocalPlayer.Center + new Vector2(100, 0)); + // ball.RigidBody.LinearVelocity = new Vector2(-78, 0); + // } + } + } +} diff --git a/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs b/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs new file mode 100644 index 000000000..0defb8b8f --- /dev/null +++ b/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs @@ -0,0 +1,184 @@ +using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.GameInteraction; + +namespace Everglow.Commons.Physics; + +public class PhysicsWorldSystem : ModSystem +{ + public PhysicsSimulation _realSimulation; + public static PhysicsWorldSystem Instance; + public PhysicsObject _dummyPlayer; + + public const float Simulation_DeltaTime = 0.1f; + + public override void Load() + { + Instance = this; + ReStart(); + On_Player.PlayerFrame += On_Player_PlayerFrame; + On_Collision.TileCollision += On_Collision_TileCollision; + } + + public override void Unload() + { + Instance = null; + On_Player.PlayerFrame -= On_Player_PlayerFrame; + On_Collision.TileCollision -= On_Collision_TileCollision; + } + + private Vector2 On_Collision_TileCollision(On_Collision.orig_TileCollision orig, Vector2 Position, Vector2 Velocity, int Width, int Height, bool fallThrough, bool fall2, int gravDir) + { + Position = GeometryUtils.ConvertToPhysicsSpace(Position); + Velocity = GeometryUtils.ConvertToPhysicsSpace(Velocity); + var rect = new AABB(Position - new Vector2(0, Height), Position + new Vector2(Width, 0)); + var oldCenter = rect.Center; + + rect = rect.Move(Velocity); + + var collisions = _realSimulation.BroadPhaseCollisionDetector.GetSingleCollision(rect, Vector2.Zero, 0, new List() + { + "Default", + }); + + if (collisions.Count > 0) + { + var dummyCollider = new BoxCollider(Width, Height); + var dummyPObject = new PhysicsObject(dummyCollider, new RigidBody2D(64)); + dummyPObject.OldPosition = rect.Center; + dummyPObject.RigidBody.CentroidWorldSpace = rect.Center; + + foreach (var collider in collisions) + { + CollisionInfo info; + if (dummyCollider.TestCollisionCondition(collider, Simulation_DeltaTime, out info)) + { + float weightA = info.Source.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + float weightB = info.Target.RigidBody.InvMass / (info.Source.RigidBody.InvMass + info.Target.RigidBody.InvMass); + + // if (weightA != 0 && weightB != 0) + // weightA = weightB = 0.5f; + + // Vector2 impluseVel = Vector2.Zero; + // List events; + // dummyCollider.GetContactInfo(info, Simulation_DeltaTime, out events); + // foreach(var e in events) + // { + // impluseVel += SolveContactImpluse(e, result + velChange, events.Count, Simulation_DeltaTime); + // } + // dummyPObject.RigidBody.MoveBody(info.Normal * weightA * info.Depth, Simulation_DeltaTime); + if (Math.Abs(info.Normal.Y) < 0.77f) + { + dummyPObject.RigidBody.MoveBody(info.Normal * weightA * info.Depth, Simulation_DeltaTime); + } + else + { + dummyPObject.RigidBody.MoveBody(info.Depth / info.Normal.Y * new Vector2(0, 1), Simulation_DeltaTime); + } + + // if (float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.X) || float.IsNaN(dummyPObject.RigidBody.CentroidWorldSpace.Y)) + // { + // if (true) + // ; + // } + // extraVel = collider.ParentObject.RigidBody.LinearVelocity * Simulation_DeltaTime; + // var n = Vector2.Dot(result, info.Normal) * info.Normal; + // result = result - n; + // velChange += impluseVel; + } + } + + // Main.NewText(result + dummyCollider.ParentObject.Position - oldCenter, Color.Red); + // var newDir = result + new Vector2(0, d); + Velocity = dummyCollider.ParentObject.Position - oldCenter; + } + + Vector2 result = orig(GeometryUtils.ConvertToPhysicsSpace(Position), GeometryUtils.ConvertToPhysicsSpace(Velocity), Width, Height, fallThrough, fall2, gravDir); + return result; + } + + /// + /// 返回接触响应后的速度的变化量 + /// + /// + /// + /// + /// + private Vector2 SolveContactImpulse(CollisionEvent2D e, Vector2 sourceVel, int count, float deltaTime) + { + float mass = 64; + var ri = e.LocalOffsetSrc; + var rb = e.LocalOffsetTarget; + + var va = sourceVel; + var vb = e.Target.RigidBody.LinearVelocity + + GeometryUtils.AngularVelocityToLinearVelocity(rb, e.Target.RigidBody.AngularVelocity); + + float va_n = Vector2.Dot(va - vb, e.Normal); + if (va_n >= 0) + { + e.NormalVelOld = 0; + return Vector2.Zero; + } + float stiffness = 0; + float vnew_n = stiffness * Math.Max(-va_n - 20 * deltaTime, 0); + + // (a × b) × c = (c • a)b - (c • b)a + double rAdotN = Vector2.Dot(GeometryUtils.Rotate90(ri), e.Normal); + double rBdotN = Vector2.Dot(GeometryUtils.Rotate90(rb), e.Normal); + double R1 = 0; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(ri, (float)(GlobalInverseInertiaTensor + + // * Utils.Cross(ri, e.Normal))), e.Normal); + double R2 = rBdotN * rBdotN * e.Target.RigidBody.GlobalInverseInertiaTensor; // Vector2.Dot(Utils.AnuglarVelocityToLinearVelocity(rb, (float)(e.Target.RigidBody.GlobalInverseInertiaTensor + + // * Utils.Cross(rb, e.Normal))), e.Normal); + double J_n = (vnew_n - va_n) / (1.0 / mass + e.Target.RigidBody.InvMass + R1 + R2); + Vector2 J = (float)J_n * e.Normal / count; // + (float)J_t * va_t_unit; + + return J / mass; + } + + private void On_Player_PlayerFrame(On_Player.orig_PlayerFrame orig, Player self) + { + Main.LocalPlayer.velocity += self.GetModPlayer()._extraVelocity; + + if (self.GetModPlayer()._shouldStand) + { + Main.LocalPlayer.velocity.Y = 0; + } + orig(self); + Main.LocalPlayer.velocity -= self.GetModPlayer()._extraVelocity; + } + + public void ReStart() + { + if(_realSimulation is not null) + { + _realSimulation.ClearPhysicsObjects(); + } + var terrariaCollisionGroup = new CollisionGraph(); + _realSimulation = new PhysicsSimulation(terrariaCollisionGroup); + terrariaCollisionGroup.AddSingleEdge("Default", "Default"); + terrariaCollisionGroup.AddSingleEdge("Default", "Terrain"); + terrariaCollisionGroup.AddDoubleEdge("Player", "Default"); + + var terrain = new PhysicsObject(new TileCollider(), null); + _realSimulation.AddPhysicsObject(terrain); + terrain.Tag = "Terrain"; + terrain.RigidBody.MovementType = MovementType.Static; + + var rigidb = new RigidBody2D(64); + _dummyPlayer = new PhysicsObject(new BoxCollider(20, 42), rigidb); + _dummyPlayer.Tag = "Player"; + rigidb.MovementType = MovementType.Player; + rigidb.UseGravity = false; + rigidb.AngularDrag = 0; + rigidb.Drag = 0; + rigidb.Restitution = -1; + rigidb.Friction = -1; + _realSimulation.AddPhysicsObject(_dummyPlayer); + _realSimulation.Initialize(); + } +} \ No newline at end of file diff --git a/Sources/Modules/Example/Test/Diamond_Physics_Item.cs b/Sources/Modules/Example/Test/Diamond_Physics_Item.cs index c9090a9e1..a411913ea 100644 --- a/Sources/Modules/Example/Test/Diamond_Physics_Item.cs +++ b/Sources/Modules/Example/Test/Diamond_Physics_Item.cs @@ -28,7 +28,7 @@ public override void HoldItem(Player player) new SphereCollider(12), new RigidBody2D(20) { }); - PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(box); + PhysicsWorldSystem.Instance._realSimulation.AddPhysicsObject(box); box.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); box.RigidBody.LinearVelocity = new Vector2(0, 0); @@ -42,7 +42,7 @@ public override void HoldItem(Player player) } if (Main.mouseRight && Main.mouseRightRelease) { - PhysicWorldSystem.Instance.ReStart(); + PhysicsWorldSystem.Instance.ReStart(); } } diff --git a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs index d0d4909ae..a3b91cc45 100644 --- a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs +++ b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs @@ -34,7 +34,7 @@ public override void HoldItem(Player player) new BoxCollider(192, 16), null); - PhysicWorldSystem.Instance._realSimulation.AddPhysicsObject(box); + PhysicsWorldSystem.Instance._realSimulation.AddPhysicsObject(box); box.Position = GeometryUtils.ConvertToPhysicsSpace(Main.MouseWorld); box.RigidBody.LinearVelocity = new Vector2(0, 0); From 835225b5e80948648f0e3be81e0e2a468add8e46 Mon Sep 17 00:00:00 2001 From: Cloudea Date: Fri, 24 Oct 2025 16:37:39 +0800 Subject: [PATCH 11/13] refactor(Function): Rename IBroadPhase to BroadPhase --- .../PBEngine/Collision/BroadPhase/BVHDetect.cs | 2 +- .../BroadPhase/{IBroadPhase.cs => BroadPhase.cs} | 11 ++--------- .../PBEngine/Collision/BroadPhase/BruteForceDetect.cs | 2 +- .../PBEngine/Collision/BroadPhase/ColliderEntry.cs | 9 +++++++++ .../PBEngine/Collision/BroadPhase/HashGridMethod.cs | 2 +- .../Physics/PBEngine/PhysicsSimulation.cs | 4 ++-- 6 files changed, 16 insertions(+), 14 deletions(-) rename Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/{IBroadPhase.cs => BroadPhase.cs} (80%) create mode 100644 Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/ColliderEntry.cs diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs index 1526a23a5..4e82c5c58 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs @@ -16,7 +16,7 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase /// /// 使用BVH辅助加速碰撞检测 /// - public class BVHDetect : IBroadPhase + public class BVHDetect : BroadPhase { private List _colliders; private Dictionary> _groups; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BroadPhase.cs similarity index 80% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BroadPhase.cs index 890ec45ba..f95775276 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/IBroadPhase.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BroadPhase.cs @@ -10,10 +10,10 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase /// /// 粗粒度碰撞检测的基类 /// - public abstract class IBroadPhase + public abstract class BroadPhase { protected CollisionGraph _collisionGraph; - public IBroadPhase(CollisionGraph graph) + public BroadPhase(CollisionGraph graph) { _collisionGraph = graph; } @@ -28,11 +28,4 @@ public IBroadPhase(CollisionGraph graph) public abstract void DrawDebugInfo(SpriteBatch sb); } - - public struct ColliderEntry - { - public int ColliderId; - public AABB BoundingBox; - public bool IsDynamic; - } } diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs index bba12c11f..91ffc8324 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs @@ -10,7 +10,7 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase /// /// 暴力遍历AABB的粗粒度碰撞检测算法 /// - public class BruteForceDetect : IBroadPhase + public class BruteForceDetect : BroadPhase { private List _colliders; private Dictionary> _groups; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/ColliderEntry.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/ColliderEntry.cs new file mode 100644 index 000000000..f8d3e42a4 --- /dev/null +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/ColliderEntry.cs @@ -0,0 +1,9 @@ +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +{ + public struct ColliderEntry + { + public int ColliderId; + public AABB BoundingBox; + public bool IsDynamic; + } +} diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs index d7f303e00..3b35360b1 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs @@ -10,7 +10,7 @@ namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase { - internal class HashGridMethod : IBroadPhase + internal class HashGridMethod : BroadPhase { private List _colliders; private Dictionary _groups; diff --git a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs index a26b5b9b8..19678a483 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs @@ -25,7 +25,7 @@ public int GAUSS_SEIDEL_ITERS private LinkedList _objectFreeList; private int _maximumAllocatedId; private List _constraints; - private IBroadPhase _broadPhase; + private BroadPhase _broadPhase; private float _gravity; private Stopwatch _stopwatchPreIntegration; @@ -75,7 +75,7 @@ public float Gravity get => _gravity; } - public IBroadPhase BroadPhaseCollisionDetector + public BroadPhase BroadPhaseCollisionDetector { get => _broadPhase; } From 68e3404825db7d2f80e4745de656d85f6677cccc Mon Sep 17 00:00:00 2001 From: Cloudea Date: Fri, 24 Oct 2025 23:06:21 +0800 Subject: [PATCH 12/13] refactor(Function): Reorganize folder structure of PBEngine --- .../Collision/BroadPhase/BVHDetect.cs | 4 +- .../Collision/BroadPhase/BroadPhase.cs | 2 + .../Collision/BroadPhase/BruteForceDetect.cs | 2 + .../Collision/BroadPhase/HashGridMethod.cs | 1 + .../Collision/BroadPhase/Structure/BVH.cs | 3 +- .../{ => Structure}/ColliderEntry.cs | 2 +- .../{ => Structure}/CollisionGraph.cs | 2 +- .../Structure/MultiLevelHashGrid.cs | 3 +- .../Collision/Colliders/BoxCollider.cs | 2 + .../Collision/Colliders/CapsuleCollider.cs | 1 + .../Collision/Colliders/Collider2D.cs | 2 + .../Collision/Colliders/SphereCollider.cs | 1 + .../PBEngine/Collision/CollisionEvent2D.cs | 1 + .../PBEngine/Collision/CollisionInfo.cs | 1 + .../PBEngine/Collision/{ => Shapes}/AABB.cs | 0 .../PBEngine/Collision/{ => Shapes}/Edge2D.cs | 5 ++- .../PBEngine/Constraints/JointConstraint.cs | 1 + .../PBEngine/Constraints/SpringConstraint.cs | 1 + .../PBEngine/{ => Core}/GeometryUtils.cs | 41 ++++++++++--------- .../{Collision => Core}/ImpulseEntry.cs | 2 +- .../{Collision => Core}/MovementType.cs | 2 +- .../PBEngine/{ => Core}/PhysicsObject.cs | 5 +-- .../PBEngine/{ => Core}/RigidBody2D.cs | 30 +++++++------- .../GameInteraction/TileCollisionUtils.cs | 2 + .../Physics/PBEngine/PhysicsSimulation.cs | 2 + .../PlayGround/Contact/ContactPlayGround1.cs | 3 +- .../PlayGround/Contact/ContactPlayGround2.cs | 3 +- .../Physics/PhysicsPlayer.cs | 2 +- .../Physics/PhysicsWorldSystem.cs | 1 + .../Physics/VisualPhysicsUnit.cs | 2 +- .../Example/Test/Diamond_Physics_Item.cs | 1 + .../Example/Test/PhysicsBody_TestSystem.cs | 2 +- 32 files changed, 75 insertions(+), 57 deletions(-) rename Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/{ => Structure}/ColliderEntry.cs (94%) rename Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/{ => Structure}/CollisionGraph.cs (99%) rename Sources/Everglow.Function/Physics/PBEngine/Collision/{ => Shapes}/AABB.cs (100%) rename Sources/Everglow.Function/Physics/PBEngine/Collision/{ => Shapes}/Edge2D.cs (92%) rename Sources/Everglow.Function/Physics/PBEngine/{ => Core}/GeometryUtils.cs (93%) rename Sources/Everglow.Function/Physics/PBEngine/{Collision => Core}/ImpulseEntry.cs (88%) rename Sources/Everglow.Function/Physics/PBEngine/{Collision => Core}/MovementType.cs (91%) rename Sources/Everglow.Function/Physics/PBEngine/{ => Core}/PhysicsObject.cs (95%) rename Sources/Everglow.Function/Physics/PBEngine/{ => Core}/RigidBody2D.cs (96%) diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs index 4e82c5c58..f846361e8 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BVHDetect.cs @@ -1,6 +1,3 @@ -using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using System; @@ -10,6 +7,7 @@ using System.Threading.Tasks; using Terraria.GameContent; using Terraria; +using Everglow.Commons.Physics.PBEngine.Core; namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase { diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BroadPhase.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BroadPhase.cs index f95775276..b3ffe925d 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BroadPhase.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BroadPhase.cs @@ -1,4 +1,6 @@ +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; using System; using System.Collections.Generic; using System.Linq; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs index 91ffc8324..820fb24e4 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/BruteForceDetect.cs @@ -1,4 +1,6 @@ +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; using System; using System.Collections.Generic; using System.Linq; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs index 3b35360b1..449f866e2 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/HashGridMethod.cs @@ -1,5 +1,6 @@ using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; using System; using System.Collections.Generic; using System.Linq; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs index ac9e70779..e982229f0 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/BVH.cs @@ -1,5 +1,4 @@ -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using MonoMod.Cil; using System; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/ColliderEntry.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/ColliderEntry.cs similarity index 94% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/ColliderEntry.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/ColliderEntry.cs index f8d3e42a4..76b4c2a68 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/ColliderEntry.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/ColliderEntry.cs @@ -1,4 +1,4 @@ -namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure { public struct ColliderEntry { diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/CollisionGraph.cs similarity index 99% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/CollisionGraph.cs index f6c4a26a3..579a9201d 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/CollisionGraph.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/CollisionGraph.cs @@ -1,4 +1,4 @@ -namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase +namespace Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure { /// /// 存储碰撞组的碰撞关系的图结构 diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs index 3c3c3ef67..88df0d935 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/BroadPhase/Structure/MultiLevelHashGrid.cs @@ -1,5 +1,4 @@ -using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision; using log4net.Core; using Microsoft.Xna.Framework; using Steamworks; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs index fd15abded..471beb25f 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/BoxCollider.cs @@ -1,3 +1,5 @@ +using Everglow.Commons.Physics.PBEngine.Collision.Shapes; +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Physics.PBEngine.GameInteraction; namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs index 4635ece58..8f062388b 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/CapsuleCollider.cs @@ -1,3 +1,4 @@ +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Utilities; using Terraria; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs index 80a376afe..54aade1c4 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/Collider2D.cs @@ -1,3 +1,5 @@ +using Everglow.Commons.Physics.PBEngine.Core; + namespace Everglow.Commons.Physics.PBEngine.Collision.Colliders { /// diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs index 6f98e3b25..8abef5cd1 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Colliders/SphereCollider.cs @@ -1,3 +1,4 @@ +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Physics.PBEngine.GameInteraction; using Terraria; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionEvent2D.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionEvent2D.cs index 8d6826e8d..735daf518 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionEvent2D.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionEvent2D.cs @@ -1,3 +1,4 @@ +using Everglow.Commons.Physics.PBEngine.Core; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionInfo.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionInfo.cs index 2cbe19e95..aa1db2327 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionInfo.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/CollisionInfo.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Everglow.Commons.Physics.PBEngine.Core; namespace Everglow.Commons.Physics.PBEngine.Collision { diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/AABB.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Shapes/AABB.cs similarity index 100% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/AABB.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/Shapes/AABB.cs diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/Edge2D.cs b/Sources/Everglow.Function/Physics/PBEngine/Collision/Shapes/Edge2D.cs similarity index 92% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/Edge2D.cs rename to Sources/Everglow.Function/Physics/PBEngine/Collision/Shapes/Edge2D.cs index bf0824a82..b09096849 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/Edge2D.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Collision/Shapes/Edge2D.cs @@ -3,10 +3,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Utilities; using Terraria; -namespace Everglow.Commons.Physics.PBEngine.Collision +namespace Everglow.Commons.Physics.PBEngine.Collision.Shapes { public class Edge2D { @@ -29,7 +30,7 @@ public Vector2 GetNormal() public bool Segment_Segment_Collision(Vector2 a, Vector2 b, out CollisionEvent2D collisionEvent) { - Matrix2x2 M = new Matrix2x2 + var M = new Matrix2x2 { [0, 0] = _pB.X - _pA.X, [0, 1] = -(b.X - a.X), diff --git a/Sources/Everglow.Function/Physics/PBEngine/Constraints/JointConstraint.cs b/Sources/Everglow.Function/Physics/PBEngine/Constraints/JointConstraint.cs index 2e777125b..ae2ae980e 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Constraints/JointConstraint.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Constraints/JointConstraint.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Everglow.Commons.Physics.PBEngine.Core; using Terraria; namespace Everglow.Commons.Physics.PBEngine.Constraints diff --git a/Sources/Everglow.Function/Physics/PBEngine/Constraints/SpringConstraint.cs b/Sources/Everglow.Function/Physics/PBEngine/Constraints/SpringConstraint.cs index 5b5226586..afcb48fb1 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Constraints/SpringConstraint.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Constraints/SpringConstraint.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Utilities; using Terraria; diff --git a/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs b/Sources/Everglow.Function/Physics/PBEngine/Core/GeometryUtils.cs similarity index 93% rename from Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs rename to Sources/Everglow.Function/Physics/PBEngine/Core/GeometryUtils.cs index 919726a18..b317f418b 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/GeometryUtils.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Core/GeometryUtils.cs @@ -1,4 +1,5 @@ using Everglow.Commons.Physics.PBEngine.Collision; +using Everglow.Commons.Physics.PBEngine.Collision.Shapes; using Everglow.Commons.Utilities; using System; using System.Collections; @@ -8,7 +9,7 @@ using System.Threading.Tasks; using Terraria; -namespace Everglow.Commons.Physics.PBEngine +namespace Everglow.Commons.Physics.PBEngine.Core { public static class GeometryUtils { @@ -120,7 +121,7 @@ public static float PointSignedDistanceToLine(Vector2 p, Vector2 start, Vector2 Vector2 ab = end - start; Vector2 ax = p - start; - float crossProductMagnitude = GeometryUtils.Cross(ab, ax); + float crossProductMagnitude = ab.Cross(ax); float ABLength = ab.Length(); return -crossProductMagnitude / ABLength; @@ -130,7 +131,7 @@ public static float PointSignedDistanceToSegmentGetNearest(Vector2 p, Vector2 st { var u = (end - start).SafeNormalize(Vector2.Zero); float d = Vector2.Dot(u, p - start); - float sign = GeometryUtils.Cross(u, p - start) > 0 ? -1 : 1; + float sign = u.Cross(p - start) > 0 ? -1 : 1; if (d <= 0) { pointOnSeg = start; @@ -200,7 +201,7 @@ public static void SphereConvexPolygonContactInfo(Vector2 cSphere, float radius, float minDepth = float.PositiveInfinity; foreach (var edge in edges) { - float d = GeometryUtils.PointDistance2ToSegmentWithClip(cSphere, edge._pA, edge._pB); + float d = PointDistance2ToSegmentWithClip(cSphere, edge._pA, edge._pB); if (d < minDepth) { minDepth = d; @@ -217,10 +218,10 @@ public static void SphereConvexPolygonContactInfo(Vector2 cSphere, float radius, foreach (var edge in edges) { - float d = GeometryUtils.PointDistance2ToSegmentWithClip(cSphere, edge._pA, edge._pB); + float d = PointDistance2ToSegmentWithClip(cSphere, edge._pA, edge._pB); if (d == minDepth) { - var p = GeometryUtils.ProjectPointOnSegment(cSphere, edge._pA, edge._pB); + var p = ProjectPointOnSegment(cSphere, edge._pA, edge._pB); localPositions.Add(new KeyValuePair( (p - cSphere).SafeNormalize(Vector2.Zero) * radius, p - centerPoly @@ -248,9 +249,9 @@ public static bool SphereConvexPolygonCollisionInfo(Vector2 cSphere, float radiu bool anyOutside = false; foreach (var edge in edges) { - float d = GeometryUtils.PointDistance2ToSegmentGetNearest(cSphere, edge._pA, edge._pB, out Vector2 pointOnSeg); + float d = PointDistance2ToSegmentGetNearest(cSphere, edge._pA, edge._pB, out Vector2 pointOnSeg); // 如果点在某一个线段的外面,那么就说明点在多边形的外面 - if (d > maxDepth && GeometryUtils.Cross(edge._pB - edge._pA, cSphere - edge._pA) <= 0) + if (d > maxDepth && (edge._pB - edge._pA).Cross(cSphere - edge._pA) <= 0) { maxDepth = d; normal = (cSphere - pointOnSeg).SafeNormalize(Vector2.Zero); @@ -364,14 +365,14 @@ public static void ConvexPolygonPolygonContactInfo(List edgesA, List( curPoint - polyCenterA, - GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) + ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterB )); } @@ -379,7 +380,7 @@ public static void ConvexPolygonPolygonContactInfo(List edgesA, List( curPoint - polyCenterA, - GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) + ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterB )); } @@ -389,20 +390,20 @@ public static void ConvexPolygonPolygonContactInfo(List edgesA, List( - GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterA, + ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterA, curPoint - polyCenterB )); } else if (Math.Abs(dist - closestDistance) < 1e-6) { localPositions.Add(new KeyValuePair( - GeometryUtils.ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterA, + ProjectPointOnSegment(curPoint, curEdge._pA, curEdge._pB) - polyCenterA, curPoint - polyCenterB )); } @@ -575,7 +576,7 @@ public static bool CapsuleConvexPolygonCollisionInfo(Vector2 segA1, Vector2 segB { // 中轴线段的法线和中轴线段本身都是关键分离轴 - Edge2D edge = new Edge2D(segA1, segB1); + var edge = new Edge2D(segA1, segB1); Vector2 axis = edge.GetNormal(); var (amin, amax) = GetProjectedInterval(new List() { segA1, segB1 }, edge._pA, axis); var (bmin, bmax) = GetProjectedInterval(corners, edge._pA, axis); @@ -646,13 +647,13 @@ public static void CapsuleConvexPolygonContactInfo(Vector2 segA1, Vector2 segB1, float radius1, List corners, List edges, Vector2 polyCenter, List> localPositions) { float closestDistance = float.PositiveInfinity; - Edge2D edgeSeg = new Edge2D(segA1, segB1); - List capsuleVertices = new List() { segA1, segB1 }; + var edgeSeg = new Edge2D(segA1, segB1); + var capsuleVertices = new List() { segA1, segB1 }; Vector2 capsuleCenter = (segA1 + segB1) / 2; Vector2 ultimateDir = (polyCenter - capsuleCenter).SafeNormalize(Vector2.Zero); foreach (var curPoint in corners) { - float dist = GeometryUtils.PointDistance2ToSegmentGetNearest(curPoint, edgeSeg._pA, edgeSeg._pB, out Vector2 p); + float dist = PointDistance2ToSegmentGetNearest(curPoint, edgeSeg._pA, edgeSeg._pB, out Vector2 p); if (dist < closestDistance) { closestDistance = dist; @@ -675,7 +676,7 @@ public static void CapsuleConvexPolygonContactInfo(Vector2 segA1, Vector2 segB1, { foreach (var curEdge in edges) { - float dist = GeometryUtils.PointDistance2ToSegmentGetNearest(capsulePoint, curEdge._pA, curEdge._pB, out Vector2 p); + float dist = PointDistance2ToSegmentGetNearest(capsulePoint, curEdge._pA, curEdge._pB, out Vector2 p); if (dist < closestDistance) { closestDistance = dist; @@ -706,7 +707,7 @@ public static void CapsuleConvexPolygonContactInfo(Vector2 segA1, Vector2 segB1, public static float MinimumSeparatingDistance(float amin, float amax, float bmin, float bmax) { float d = Math.Min(amax, bmax) - Math.Max(amin, bmin); - if ((amin >= bmin && amax <= bmax) || (amin <= bmin && amax >= bmax)) + if (amin >= bmin && amax <= bmax || amin <= bmin && amax >= bmax) { d = Math.Min(amax - bmin, bmax - amin); } diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/ImpulseEntry.cs b/Sources/Everglow.Function/Physics/PBEngine/Core/ImpulseEntry.cs similarity index 88% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/ImpulseEntry.cs rename to Sources/Everglow.Function/Physics/PBEngine/Core/ImpulseEntry.cs index a4d494624..95b0a9b34 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/ImpulseEntry.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Core/ImpulseEntry.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading.Tasks; -namespace Everglow.Commons.Physics.PBEngine.Collision +namespace Everglow.Commons.Physics.PBEngine.Core { public struct ImpulseEntry { diff --git a/Sources/Everglow.Function/Physics/PBEngine/Collision/MovementType.cs b/Sources/Everglow.Function/Physics/PBEngine/Core/MovementType.cs similarity index 91% rename from Sources/Everglow.Function/Physics/PBEngine/Collision/MovementType.cs rename to Sources/Everglow.Function/Physics/PBEngine/Core/MovementType.cs index f249d49da..3190e1b07 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/Collision/MovementType.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Core/MovementType.cs @@ -1,4 +1,4 @@ -namespace Everglow.Commons.Physics.PBEngine.Collision +namespace Everglow.Commons.Physics.PBEngine.Core { /// /// 刚体的移动模式,用于区分模拟方法以及提升性能 diff --git a/Sources/Everglow.Function/Physics/PBEngine/PhysicsObject.cs b/Sources/Everglow.Function/Physics/PBEngine/Core/PhysicsObject.cs similarity index 95% rename from Sources/Everglow.Function/Physics/PBEngine/PhysicsObject.cs rename to Sources/Everglow.Function/Physics/PBEngine/Core/PhysicsObject.cs index 0f6d79ec9..b36a080f9 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PhysicsObject.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Core/PhysicsObject.cs @@ -1,9 +1,8 @@ -using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Utilities; using Everglow.Commons.Vertex; -namespace Everglow.Commons.Physics.PBEngine +namespace Everglow.Commons.Physics.PBEngine.Core { /// /// 物理对象的容器,可以容纳Rigidbody,Collider等组件 @@ -143,7 +142,7 @@ public void RecordOldState() public List<(Vector2, Color)> GetWireFrameWires() { - List<(Vector2, Color)> wires_color = new List<(Vector2, Color)>(); + var wires_color = new List<(Vector2, Color)>(); List wires = _collider.GetWireFrameWires(); for (int i = 0; i < wires.Count; i++) { diff --git a/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs b/Sources/Everglow.Function/Physics/PBEngine/Core/RigidBody2D.cs similarity index 96% rename from Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs rename to Sources/Everglow.Function/Physics/PBEngine/Core/RigidBody2D.cs index 6b851f91f..a0676d730 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/RigidBody2D.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/Core/RigidBody2D.cs @@ -2,7 +2,7 @@ using Everglow.Commons.Utilities; using Terraria; -namespace Everglow.Commons.Physics.PBEngine +namespace Everglow.Commons.Physics.PBEngine.Core { /// /// 2D刚体物理组件,用于实际的刚体运动模拟和响应的模块 @@ -41,7 +41,7 @@ public float Mass /// public float InvMass { - get => (MovementType == MovementType.Dynamic || MovementType == MovementType.Player) ? 1 / _mass : 0; + get => MovementType == MovementType.Dynamic || MovementType == MovementType.Player ? 1 / _mass : 0; } /// @@ -322,29 +322,29 @@ public bool TryRespondTo2Events(List events, float deltaTime) { return false; } - Matrix2x2 matrix = new Matrix2x2() + var matrix = new Matrix2x2() { [0, 0] = InvMass + events[0].Target.RigidBody.InvMass + Vector2.Dot( GeometryUtils.AngularVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri0, n0))), n0) + * ri0.Cross(n0))), n0) + Vector2.Dot( GeometryUtils.AngularVelocityToLinearVelocity(rb0, (float)(events[0].Target.RigidBody.GlobalInverseInertiaTensor - * GeometryUtils.Cross(rb0, n0))), n0), + * rb0.Cross(n0))), n0), [0, 1] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot( GeometryUtils.AngularVelocityToLinearVelocity(ri0, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri1, n1))), n0), + * ri1.Cross(n1))), n0), [1, 0] = InvMass * Vector2.Dot(n0, n1) + Vector2.Dot( GeometryUtils.AngularVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri0, n0))), n1), + * ri0.Cross(n0))), n1), [1, 1] = InvMass + events[1].Target.RigidBody.InvMass + Vector2.Dot( GeometryUtils.AngularVelocityToLinearVelocity(ri1, (float)(GlobalInverseInertiaTensor - * GeometryUtils.Cross(ri1, n1))), n1) + * ri1.Cross(n1))), n1) + Vector2.Dot( GeometryUtils.AngularVelocityToLinearVelocity(rb1, (float)(events[1].Target.RigidBody.GlobalInverseInertiaTensor - * GeometryUtils.Cross(rb1, n1))), n1), + * rb1.Cross(n1))), n1), }; float C = 0.4f; var J = matrix.Inverse().Multiply(new Vector2(-(1 + C) * relv0_n, -(1 + C) * relv1_n)); @@ -423,7 +423,7 @@ private void SolveFrictionImpulse(CollisionEvent2D e, int count, float deltaTime } float vel_n = Vector2.Dot(va - vb, e.Normal); - Vector2 vt = (va - vb) - vel_n * e.Normal; + Vector2 vt = va - vb - vel_n * e.Normal; float vel_t = vt.Length(); float friction = Math.Max(0, (_friction + e.Target.RigidBody.Friction) / 2); @@ -600,7 +600,7 @@ public void Integrate(float deltaTime) public void AddForce(Vector2 force, Vector2 relPos) { _force += force; - _torque += GeometryUtils.Cross(relPos, force); + _torque += relPos.Cross(force); } /// @@ -639,7 +639,7 @@ public void ApplyImpulses() _linearVelocity += 1.0f / _mass * imp.ImpulseSource; if (imp.Source.MovementType != MovementType.Player) { - _angularVelocity += GeometryUtils.Cross(imp.RelativePositionSource, imp.ImpulseSource * (float)_globalInverseInertiaTensor); + _angularVelocity += imp.RelativePositionSource.Cross(imp.ImpulseSource * (float)_globalInverseInertiaTensor); } } if (imp.Target.MovementType == MovementType.Dynamic || imp.Target.MovementType == MovementType.Player) @@ -670,8 +670,7 @@ public void AddImpulseImmediate(Vector2 J, Vector2 relativePos, RigidBody2D othe { _linearVelocity += 1.0f / _mass * J; Debug.Assert(!float.IsNaN(_linearVelocity.X) && !float.IsNaN(_linearVelocity.Y)); - _angularVelocity += GeometryUtils.Cross( - relativePos, + _angularVelocity += relativePos.Cross( J * (float)GlobalInverseInertiaTensor); Debug.Assert(!float.IsNaN(_angularVelocity)); } @@ -679,8 +678,7 @@ public void AddImpulseImmediate(Vector2 J, Vector2 relativePos, RigidBody2D othe if (other.MovementType != MovementType.Static && MovementType != MovementType.Kinematic) { other._linearVelocity += other.InvMass * J2; - other._angularVelocity += GeometryUtils.Cross( - relativePos2, + other._angularVelocity += relativePos2.Cross( J2 * (float)other.GlobalInverseInertiaTensor); } } diff --git a/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs b/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs index 022c28b6e..620391364 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/GameInteraction/TileCollisionUtils.cs @@ -1,5 +1,7 @@ using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Collision.Shapes; +using Everglow.Commons.Physics.PBEngine.Core; using Terraria; namespace Everglow.Commons.Physics.PBEngine.GameInteraction diff --git a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs index 19678a483..542f1612b 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PhysicsSimulation.cs @@ -1,8 +1,10 @@ using Everglow.Commons.DataStructures; using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.Constraints; +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Vertex; using Terraria; diff --git a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs index 2c39d0022..bec8c253f 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround1.cs @@ -1,4 +1,5 @@ using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; namespace Everglow.Commons.Physics.PBEngine.PlayGround.Contact { @@ -205,7 +206,7 @@ public static PhysicsSimulation SimpleKinematic() kPlane.Position = new Vector2(512, 512); kPlane.Rotation = 0; kPlane.RigidBody.ApplyAngularVelocity(0.9f); - kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; + kPlane.RigidBody.MovementType = MovementType.Kinematic; world.AddPhysicsObject(kPlane); var sPlane = new PhysicsObject( diff --git a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs index 8d31c442f..26c585a64 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Contact/ContactPlayGround2.cs @@ -1,4 +1,5 @@ using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; using System; using System.Collections.Generic; using System.Linq; @@ -101,7 +102,7 @@ public static PhysicsSimulation MoveWithPad() kPlane.Rotation = 0; kPlane.RigidBody.Drag = 0; kPlane.RigidBody.LinearVelocity = new Vector2(10, 0); - kPlane.RigidBody.MovementType = Collision.MovementType.Kinematic; + kPlane.RigidBody.MovementType = MovementType.Kinematic; world.AddPhysicsObject(kPlane); return world; diff --git a/Sources/Everglow.Function/Physics/PhysicsPlayer.cs b/Sources/Everglow.Function/Physics/PhysicsPlayer.cs index e3c4b1216..fc733bec5 100644 --- a/Sources/Everglow.Function/Physics/PhysicsPlayer.cs +++ b/Sources/Everglow.Function/Physics/PhysicsPlayer.cs @@ -1,9 +1,9 @@ using System.Data; using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.Constraints; +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Physics.PBEngine.GameInteraction; using Everglow.Commons.Vertex; using Microsoft.Xna.Framework.Input; diff --git a/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs b/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs index 0defb8b8f..6bac35d85 100644 --- a/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs +++ b/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs @@ -2,6 +2,7 @@ using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Physics.PBEngine.GameInteraction; namespace Everglow.Commons.Physics; diff --git a/Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs b/Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs index 95afca512..18efa5250 100644 --- a/Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs +++ b/Sources/Everglow.Function/Physics/VisualPhysicsUnit.cs @@ -1,5 +1,5 @@ using Everglow.Commons.Enums; -using Everglow.Commons.Physics.PBEngine; +using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.VFX; using Everglow.Commons.VFX.Pipelines; diff --git a/Sources/Modules/Example/Test/Diamond_Physics_Item.cs b/Sources/Modules/Example/Test/Diamond_Physics_Item.cs index a411913ea..10acc554b 100644 --- a/Sources/Modules/Example/Test/Diamond_Physics_Item.cs +++ b/Sources/Modules/Example/Test/Diamond_Physics_Item.cs @@ -1,6 +1,7 @@ using Everglow.Commons.Physics; using Everglow.Commons.Physics.PBEngine; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; using static Everglow.Commons.VFX.VFXBatchExtension; namespace Everglow.Example.Test; diff --git a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs index a3b91cc45..fae584e08 100644 --- a/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs +++ b/Sources/Modules/Example/Test/PhysicsBody_TestSystem.cs @@ -1,7 +1,7 @@ using Everglow.Commons.Physics; using Everglow.Commons.Physics.PBEngine; -using Everglow.Commons.Physics.PBEngine.Collision; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; +using Everglow.Commons.Physics.PBEngine.Core; using static Everglow.Commons.VFX.VFXBatchExtension; namespace Everglow.Example.Test; From d36477f865d153a71cde2b90f2522f018ada25a9 Mon Sep 17 00:00:00 2001 From: CloudeaSoft Date: Thu, 27 Nov 2025 21:19:25 +0800 Subject: [PATCH 13/13] fix: Namespace mismatch --- .../Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs | 1 + Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs index fe1dbb228..5e8e56222 100644 --- a/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs +++ b/Sources/Everglow.Function/Physics/PBEngine/PlayGround/Constrain/JointPlayground.cs @@ -1,5 +1,6 @@ using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.Constraints; +using Everglow.Commons.Physics.PBEngine.Core; namespace Everglow.Commons.Physics.PBEngine.PlayGround.Constrain { diff --git a/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs b/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs index 6bac35d85..9c75d7c5b 100644 --- a/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs +++ b/Sources/Everglow.Function/Physics/PhysicsWorldSystem.cs @@ -1,6 +1,6 @@ using Everglow.Commons.Physics.PBEngine; using Everglow.Commons.Physics.PBEngine.Collision; -using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase; +using Everglow.Commons.Physics.PBEngine.Collision.BroadPhase.Structure; using Everglow.Commons.Physics.PBEngine.Collision.Colliders; using Everglow.Commons.Physics.PBEngine.Core; using Everglow.Commons.Physics.PBEngine.GameInteraction;