diff --git a/scripts/SimulationParameters/MicrobeStage/Biomes.json b/scripts/SimulationParameters/MicrobeStage/Biomes.json index 769d734f44a..bceafb26cfe 100644 --- a/scripts/SimulationParameters/MicrobeStage/Biomes.json +++ b/scripts/SimulationParameters/MicrobeStage/Biomes.json @@ -34,7 +34,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -162,7 +162,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -197,7 +197,7 @@ "dissolves": true, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 2, "ventAmount": 3.0, "damages": 0.0, @@ -318,7 +318,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -445,7 +445,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -498,7 +498,7 @@ "dissolves": true, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 2, "ventAmount": 3.0, "damages": 0.0, @@ -601,7 +601,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -654,7 +654,7 @@ "dissolves": true, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 2, "ventAmount": 3.0, "damages": 0.0, @@ -757,7 +757,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -884,7 +884,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -1011,7 +1011,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -1169,7 +1169,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -1297,7 +1297,7 @@ "dissolves": false, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 10000, "ventAmount": 3.0, "damages": 10.0, @@ -1350,7 +1350,7 @@ "dissolves": true, "radius": 1, "chunkScale": 1.0, - "mass": 1, + "mass": 7, "size": 2, "ventAmount": 3.0, "damages": 0.0, diff --git a/scripts/microbe_stage/biome.as b/scripts/microbe_stage/biome.as index 40cb60f3bd0..7df4a3fa82b 100644 --- a/scripts/microbe_stage/biome.as +++ b/scripts/microbe_stage/biome.as @@ -122,6 +122,9 @@ ObjectID createChunk(CellStageWorld@ world, uint chunkId, Float3 pos) // Need to set the tint model.GraphicalObject.setCustomParameter(1, Ogre::Vector4(1, 1, 1, 1)); + // Fluid mechanics. + world.Create_FluidEffectComponent(chunkEntity); + // Rigid Body auto rigidBody = world.Create_Physics(chunkEntity, position); diff --git a/scripts/microbe_stage/microbe_operations.as b/scripts/microbe_stage/microbe_operations.as index c6893f453ed..7697ce305ac 100644 --- a/scripts/microbe_stage/microbe_operations.as +++ b/scripts/microbe_stage/microbe_operations.as @@ -1079,6 +1079,9 @@ ObjectID _createMicrobeEntity(CellStageWorld@ world, bool aiControlled, auto compoundAbsorberComponent = world.Create_CompoundAbsorberComponent(entity); + // Uncomment this when we get pretty fluid graphics. + //world.Create_FluidEffectComponent(entity); + if (species.isBacteria) { compoundAbsorberComponent.setGrabScale(0.5f); } @@ -1253,6 +1256,7 @@ void kill(CellStageWorld@ world, ObjectID microbeEntity) double amount = double(microbeComponent.totalHexCountCache)/CORPSE_CHUNK_AMOUNT_DIVISER; // Chunk(should separate into own function) ObjectID chunkEntity = world.CreateEntity(); + world.Create_FluidEffectComponent(chunkEntity); auto positionAdded = Float3(GetEngine().GetRandom().GetFloat(-2.0f, 2.0f),0, GetEngine().GetRandom().GetFloat(-2.0f, 2.0f)); auto chunkPosition = world.Create_Position(chunkEntity, position._Position+positionAdded, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5318108af09..32e494fd45b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ # Add source files to the list set(GROUP_ENGINE + "engine/component_types.h" "engine/typedefs.h" "engine/player_data.cpp" "engine/player_data.h" @@ -44,7 +45,9 @@ set(GROUP_MICROBE_STAGE "microbe_stage/compounds.cpp" "microbe_stage/compounds.h" "microbe_stage/generate_cell_stage_world.rb" - "microbe_stage/generate_microbe_editor_world.rb" + "microbe_stage/generate_microbe_editor_world.rb" + "microbe_stage/fluid_system.cpp" + "microbe_stage/fluid_system.h" "microbe_stage/membrane_system.cpp" "microbe_stage/membrane_system.h" "microbe_stage/microbe_camera_system.cpp" diff --git a/src/engine/component_types.h b/src/engine/component_types.h index 84cd0a763c1..1ab4225f544 100644 --- a/src/engine/component_types.h +++ b/src/engine/component_types.h @@ -23,6 +23,7 @@ enum class THRIVE_COMPONENT : uint16_t { PROPERTIES, COMPOUND_VENTER, ENGULFABLE, + FLUID_EFFECT, DAMAGETOUCH, // TODO: check is this needed for anything // INVALID diff --git a/src/microbe_stage/compound_cloud_system.cpp b/src/microbe_stage/compound_cloud_system.cpp index a9950109467..3d41f4c73b1 100644 --- a/src/microbe_stage/compound_cloud_system.cpp +++ b/src/microbe_stage/compound_cloud_system.cpp @@ -318,21 +318,13 @@ void //////////////////////////////////////////////////////////////////////////////// // CompoundCloudSystem //////////////////////////////////////////////////////////////////////////////// -CompoundCloudSystem::CompoundCloudSystem() : - m_xVelocity(CLOUD_SIMULATION_WIDTH, - std::vector(CLOUD_SIMULATION_HEIGHT, 0)), - m_yVelocity(CLOUD_SIMULATION_WIDTH, - std::vector(CLOUD_SIMULATION_HEIGHT, 0)) -{} - -CompoundCloudSystem::~CompoundCloudSystem() {} void CompoundCloudSystem::Init(CellStageWorld& world) { // Use the curl of a Perlin noise field to create a turbulent velocity // field. - createVelocityField(); + // createVelocityField(); // Skip if no graphics if(!Ogre::Root::getSingletonPtr()) @@ -678,7 +670,7 @@ void "it didn't initialize"); } - processCloud(*value.second, renderTime); + processCloud(*value.second, renderTime, world.GetFluidSystem()); } } @@ -1077,10 +1069,12 @@ void // ------------------------------------ // void CompoundCloudSystem::processCloud(CompoundCloudComponent& cloud, - int renderTime) + int renderTime, + FluidSystem& fluidSystem) { // Try to slow things down (doesn't seem to work great) renderTime /= 10; + Float2 pos(cloud.m_position.X, cloud.m_position.Z); // The diffusion rate seems to have a bigger effect @@ -1088,22 +1082,26 @@ void if(cloud.m_compoundId1 != NULL_COMPOUND) { diffuse(0.007f, cloud.m_oldDens1, cloud.m_density1, renderTime); // Move the compound clouds about the velocity field. - advect(cloud.m_oldDens1, cloud.m_density1, renderTime); + advect( + cloud.m_oldDens1, cloud.m_density1, renderTime, fluidSystem, pos); } if(cloud.m_compoundId2 != NULL_COMPOUND) { diffuse(0.007f, cloud.m_oldDens2, cloud.m_density2, renderTime); // Move the compound clouds about the velocity field. - advect(cloud.m_oldDens2, cloud.m_density2, renderTime); + advect( + cloud.m_oldDens2, cloud.m_density2, renderTime, fluidSystem, pos); } if(cloud.m_compoundId3 != NULL_COMPOUND) { diffuse(0.007f, cloud.m_oldDens3, cloud.m_density3, renderTime); // Move the compound clouds about the velocity field. - advect(cloud.m_oldDens3, cloud.m_density3, renderTime); + advect( + cloud.m_oldDens3, cloud.m_density3, renderTime, fluidSystem, pos); } if(cloud.m_compoundId4 != NULL_COMPOUND) { diffuse(0.007f, cloud.m_oldDens4, cloud.m_density4, renderTime); // Move the compound clouds about the velocity field. - advect(cloud.m_oldDens4, cloud.m_density4, renderTime); + advect( + cloud.m_oldDens4, cloud.m_density4, renderTime, fluidSystem, pos); } // No graphics check @@ -1189,40 +1187,6 @@ void } } - -void - CompoundCloudSystem::createVelocityField() -{ - const float nxScale = m_noiseScale; - // "float(CLOUD_SIMULATION_WIDTH) / float(CLOUD_SIMULATION_HEIGHT)" is the - // aspect ratio of the cloud. This is 1 if the cloud is a square. - const float nyScale = nxScale * (float(CLOUD_SIMULATION_WIDTH) / - float(CLOUD_SIMULATION_HEIGHT)); - - for(int x = 0; x < CLOUD_SIMULATION_WIDTH; x++) { - for(int y = 0; y < CLOUD_SIMULATION_HEIGHT; y++) { - const float x0 = - (float(x - 1) / float(CLOUD_SIMULATION_WIDTH)) * nxScale; - const float y0 = - (float(y - 1) / float(CLOUD_SIMULATION_HEIGHT)) * nyScale; - const float x1 = - (float(x + 1) / float(CLOUD_SIMULATION_WIDTH)) * nxScale; - const float y1 = - (float(y + 1) / float(CLOUD_SIMULATION_HEIGHT)) * nyScale; - - float n0 = m_fieldPotential.noise(x0, y0, 0); - float n1 = m_fieldPotential.noise(x1, y0, 0); - const float ny = n0 - n1; - n0 = m_fieldPotential.noise(x0, y0, 0); - n1 = m_fieldPotential.noise(x0, y1, 0); - const float nx = n1 - n0; - - m_xVelocity[x][y] = nx / 2; - m_yVelocity[x][y] = ny / 2; - } - } -} - void CompoundCloudSystem::diffuse(float diffRate, std::vector>& oldDens, @@ -1243,7 +1207,9 @@ void void CompoundCloudSystem::advect(const std::vector>& oldDens, std::vector>& density, - int dt) + int dt, + FluidSystem& fluidSystem, + Float2 pos) { for(int x = 0; x < CLOUD_SIMULATION_WIDTH; x++) { for(int y = 0; y < CLOUD_SIMULATION_HEIGHT; y++) { @@ -1256,8 +1222,15 @@ void for(size_t x = 1; x < CLOUD_SIMULATION_WIDTH - 1; x++) { for(size_t y = 1; y < CLOUD_SIMULATION_HEIGHT - 1; y++) { if(oldDens[x][y] > 1) { - float dx = x + dt * m_xVelocity[x][y]; - float dy = y + dt * m_yVelocity[x][y]; + constexpr float viscosity = + 0.0525f; // TODO: give each cloud a viscosity value in the + // JSON file and use it instead. + Float2 velocity = fluidSystem.getVelocityAt( + pos + Float2(x, y) * CLOUD_RESOLUTION) * + viscosity; + + float dx = x + dt * velocity.X; + float dy = y + dt * velocity.Y; dx = std::clamp(dx, 0.5f, CLOUD_SIMULATION_WIDTH - 1.5f); dy = std::clamp(dy, 0.5f, CLOUD_SIMULATION_HEIGHT - 1.5f); diff --git a/src/microbe_stage/compound_cloud_system.h b/src/microbe_stage/compound_cloud_system.h index 44049325a7d..a302e55794c 100644 --- a/src/microbe_stage/compound_cloud_system.h +++ b/src/microbe_stage/compound_cloud_system.h @@ -16,6 +16,7 @@ namespace thrive { +class FluidSystem; class CompoundCloudSystem; class CellStageWorld; @@ -342,12 +343,12 @@ class CompoundCloudSystem { /** * @brief Constructor */ - CompoundCloudSystem(); + CompoundCloudSystem() = default; /** * @brief Destructor */ - ~CompoundCloudSystem(); + ~CompoundCloudSystem() = default; /** * @brief Initializes the system @@ -497,7 +498,9 @@ class CompoundCloudSystem { size_t startIndex); void - processCloud(CompoundCloudComponent& cloud, int renderTime); + processCloud(CompoundCloudComponent& cloud, + int renderTime, + FluidSystem& fluidSystem); void initializeCloud(CompoundCloudComponent& cloud, @@ -509,9 +512,6 @@ class CompoundCloudSystem { size_t rowBytes, uint8_t* pDest); - void - createVelocityField(); - void diffuse(float diffRate, std::vector>& oldDens, @@ -521,7 +521,9 @@ class CompoundCloudSystem { void advect(const std::vector>& oldDens, std::vector>& density, - int dt); + int dt, + FluidSystem& fluidSystem, + Float2 pos); private: //! This system now spawns these entities when it needs them @@ -542,17 +544,6 @@ class CompoundCloudSystem { Ogre::MeshPtr m_planeMesh; - // Shared perlin noise for adding turbulence to the movement of compounds in - // the clouds - PerlinNoise m_fieldPotential; - const float m_noiseScale = 5; - - //! The velocity of the fluid. - //! This is not updated after the initial generation, which isn't probably - //! the best way to simulate fluid velocity - std::vector> m_xVelocity; - std::vector> m_yVelocity; - //! This is here to not have to allocate memory every tick std::vector m_tooFarAwayClouds; }; diff --git a/src/microbe_stage/fluid_system.cpp b/src/microbe_stage/fluid_system.cpp new file mode 100644 index 00000000000..c99eda68542 --- /dev/null +++ b/src/microbe_stage/fluid_system.cpp @@ -0,0 +1,71 @@ +#include "fluid_system.h" + +using namespace thrive; + +const Float2 FluidSystem::scale(0.05f, 0.05f); + +FluidEffectComponent::FluidEffectComponent() : Leviathan::Component(TYPE) {} + +FluidSystem::FluidSystem() : + noiseDisturbancesX(69), noiseDisturbancesY(13), noiseCurrentsX(420), + noiseCurrentsY(1337) +{} + +void + FluidSystem::Run(GameWorld& world) +{ + millisecondsPassed += + Leviathan::TICKSPEED; // TODO: get this thing plugged to FPS. + + for(auto& [id, components] : CachedComponents.GetIndex()) { + Leviathan::PhysicsBody* rigidBody = std::get<1>(*components).GetBody(); + + if(!rigidBody) // Missing body for some reason. + continue; + + Float3 pos = rigidBody->GetPosition(); + Float2 vel = getVelocityAt(Float2(pos.X, pos.Z)) * maxForceApplied; + + rigidBody->GiveImpulse(Float3(vel.X, 0.0f, vel.Y)); + } +} + +// TODO: also figure out if there's a way to do this that doesn't generate only +// horiontal or vertical currents +Float2 + FluidSystem::getVelocityAt(Float2 position) +{ + const Float2 scaledPosition = position * positionScaling; + + const float disturbances_x = + noiseDisturbancesX.noise(scaledPosition.X, scaledPosition.Y, + millisecondsPassed * disturbanceTimescale) * + 2.0f - + 1.0f; + ; + const float disturbances_y = + noiseDisturbancesY.noise(scaledPosition.X, scaledPosition.Y, + millisecondsPassed * disturbanceTimescale) * + 2.0f - + 1.0f; + + const float currents_x = + noiseCurrentsX.noise(scaledPosition.X * currentsStretchingMultiplier, + scaledPosition.Y, millisecondsPassed * currentsTimescale) * + 2.0f - + 1.0f; + const float currents_y = + noiseCurrentsY.noise(scaledPosition.X, + scaledPosition.Y * currentsStretchingMultiplier, + millisecondsPassed * currentsTimescale) * + 2.0f - + 1.0f; + + const Float2 disturbancesVelocity(disturbances_x, disturbances_y); + const Float2 currentsVelocity( + std::abs(currents_x) > minCurrentIntensity ? currents_x : 0.0f, + std::abs(currents_y) > minCurrentIntensity ? currents_y : 0.0f); + + return (disturbancesVelocity * disturbanceToCurrentsRatio + + currentsVelocity * (1.0f - disturbanceToCurrentsRatio)); +} diff --git a/src/microbe_stage/fluid_system.h b/src/microbe_stage/fluid_system.h new file mode 100644 index 00000000000..74e032d2b24 --- /dev/null +++ b/src/microbe_stage/fluid_system.h @@ -0,0 +1,79 @@ +#pragma once + +#include "general/perlin_noise.h" +#include +#include +#include +#include + +namespace thrive { + +// TODO: add more variables here for more complex fluid dynamics +class FluidEffectComponent : public Leviathan::Component { +public: + FluidEffectComponent(); + + REFERENCE_HANDLE_UNCOUNTED_TYPE(MembraneComponent); + static constexpr auto TYPE = + componentTypeConvert(THRIVE_COMPONENT::FLUID_EFFECT); +}; + +class FluidSystem + : public Leviathan::System< + std::tuple> { +public: + //! Updates the membrane calculations every frame + FluidSystem(); + + void + Run(GameWorld& world); + + void + CreateNodes( + const std::vector>& + firstdata, + const std::vector>& + seconddata, + const ComponentHolder& firstholder, + const ComponentHolder& secondholder) + { + TupleCachedComponentCollectionHelper( + CachedComponents, firstdata, seconddata, firstholder, secondholder); + } + + void + DestroyNodes( + const std::vector>& + firstdata, + const std::vector>& + seconddata) + { + CachedComponents.RemoveBasedOnKeyTupleList(firstdata); + CachedComponents.RemoveBasedOnKeyTupleList(seconddata); + } + + Float2 + getVelocityAt(Float2 position); + +private: + Float2 + sampleNoise(Float2 pos, float time); + + float millisecondsPassed = 0.0; + PerlinNoise noiseDisturbancesX; + PerlinNoise noiseDisturbancesY; + PerlinNoise noiseCurrentsX; + PerlinNoise noiseCurrentsY; + + static const Float2 scale; + + static constexpr float maxForceApplied = 0.525f; + static constexpr float disturbanceTimescale = 0.001f; + static constexpr float currentsTimescale = 0.001f / 500.0f; + static constexpr float currentsStretchingMultiplier = 1.0f / 10.0f; + static constexpr float minCurrentIntensity = 0.4f; + static constexpr float disturbanceToCurrentsRatio = 0.15f; + static constexpr float positionScaling = 0.05f; +}; + +} // namespace thrive diff --git a/src/microbe_stage/generate_cell_stage_world.rb b/src/microbe_stage/generate_cell_stage_world.rb index 1222a022f44..7c2cfe3f3ce 100644 --- a/src/microbe_stage/generate_cell_stage_world.rb +++ b/src/microbe_stage/generate_cell_stage_world.rb @@ -18,6 +18,7 @@ generator.addInclude "thrive_world_factory.h" +generator.addInclude "microbe_stage/fluid_system.h" generator.addInclude "microbe_stage/membrane_system.h" generator.addInclude "microbe_stage/compound_cloud_system.h" generator.addInclude "microbe_stage/process_system.h" @@ -35,6 +36,7 @@ cellWorld = GameWorldClass.new( "CellStageWorld", componentTypes: [ + EntityComponent.new("FluidEffectComponent", [ConstructorInfo.new([])]), EntityComponent.new("ProcessorComponent", [ConstructorInfo.new([])]), EntityComponent.new("CompoundBagComponent", [ConstructorInfo.new([])]), EntityComponent.new("CompoundVenterComponent", [ConstructorInfo.new([])]), @@ -115,7 +117,11 @@ # "runrender" runtick: {group: 100, parameters: [ "GetScene()" - ]}), + ]}), + + EntitySystem.new("FluidSystem", ["FluidEffectComponent", "Physics"], + runtick: {group: 49, parameters: []} + ), EntitySystem.new("SpawnSystem", ["SpawnedComponent", "Position"], runtick: {group: 50, parameters: []}, diff --git a/src/scripting/script_initializer.cpp b/src/scripting/script_initializer.cpp index b86ad835db6..43d34ec9d5b 100644 --- a/src/scripting/script_initializer.cpp +++ b/src/scripting/script_initializer.cpp @@ -574,6 +574,8 @@ static uint16_t CompoundCloudComponentTYPEProxy = static_cast(CompoundCloudComponent::TYPE); static uint16_t MembraneComponentTYPEProxy = static_cast(MembraneComponent::TYPE); +static uint16_t FluidEffectComponentTYPEProxy = + static_cast(FluidEffectComponent::TYPE); static uint16_t SpeciesComponentTYPEProxy = static_cast(SpeciesComponent::TYPE); static uint16_t CompoundBagComponentTYPEProxy = @@ -761,6 +763,16 @@ bool engine, "CompoundCloudComponent", &CompoundCloudComponentTYPEProxy)) return false; + // ------------------------------------ // + if(engine->RegisterObjectType( + "FluidEffectComponent", 0, asOBJ_REF | asOBJ_NOCOUNT) < 0) { + ANGELSCRIPT_REGISTERFAIL; + } + + if(!bindComponentTypeId( + engine, "FluidEffectComponent", &FluidEffectComponentTYPEProxy)) + return false; + // ------------------------------------ // if(engine->RegisterObjectType( "MembraneComponent", 0, asOBJ_REF | asOBJ_NOCOUNT) < 0) {