From 9f38c219e2851324b8b2694315abed4d3138fca2 Mon Sep 17 00:00:00 2001 From: faketruth Date: Tue, 14 Feb 2012 13:37:13 +0000 Subject: Updated fluid simulators with geser's patch file! Fluids should behave more like Minecraft's fluids, and lava+water creates stone/cobble/obsidian! git-svn-id: http://mc-server.googlecode.com/svn/trunk@257 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/cFluidSimulator.cpp | 373 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 324 insertions(+), 49 deletions(-) (limited to 'source/cFluidSimulator.cpp') diff --git a/source/cFluidSimulator.cpp b/source/cFluidSimulator.cpp index e4f0aa85a..2d8952a05 100644 --- a/source/cFluidSimulator.cpp +++ b/source/cFluidSimulator.cpp @@ -1,21 +1,29 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - +#include "Globals.h" #include "cFluidSimulator.h" #include "cWorld.h" #include "Vector3i.h" #include "BlockID.h" #include "Defines.h" +#include #include "cPickup.h" #include "cItem.h" +#include + +//#define DEBUG_FLUID +#ifdef DEBUG_FLUID +#define LOG_FLUID(...) LOGWARN( __VA_ARGS__ ) +#else +#define LOG_FLUID(...) +#endif + class cFluidSimulator::FluidData { public: FluidData( cWorld* a_World, cFluidSimulator *a_Simulator ) - : m_ActiveFluid( new std::list < Vector3i >() ) + : m_ActiveFluid( new std::set < Vector3i >() ) , m_Simulator (a_Simulator) - , m_Buffer( new std::list< Vector3i >() ) + , m_Buffer( new std::set< Vector3i >() ) , m_World( a_World ) {} @@ -25,39 +33,156 @@ public: delete m_ActiveFluid; } - std::vector< Vector3i > GetLowestPoints( int a_X, int a_Y, int a_Z ) + void UpdateWave(Vector3i a_LeftCorner, Vector3i a_CurBlock) { - std::vector< Vector3i > Points; - if( m_World->GetBlock(a_X, a_Y-1, a_Z) == E_BLOCK_AIR ) + Vector3i LevelPoints [] = { + Vector3i( a_CurBlock.x-1, a_CurBlock.y, a_CurBlock.z ), + Vector3i( a_CurBlock.x+1, a_CurBlock.y, a_CurBlock.z ), + Vector3i( a_CurBlock.x, a_CurBlock.y, a_CurBlock.z-1 ), + Vector3i( a_CurBlock.x, a_CurBlock.y, a_CurBlock.z+1 ), + }; + + for(int i=0; i<4; i++) { - Points.push_back( Vector3i( a_X, a_Y-1, a_Z ) ); - return Points; + Vector3i cur = LevelPoints[i]; + switch(m_Relief[cur.x][cur.z]) + { + case E_HOLE: + { + m_StartSide[cur.x][cur.z] = m_StartSide[a_CurBlock.x][a_CurBlock.z]; + m_CurResult|=m_StartSide[cur.x][cur.z]; + m_NearestHole = m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1; + LOG_FLUID("Hole found: %d \t curResult: %d", int(m_StartSide[cur.x][cur.z]), int(m_CurResult) ); + LOG_FLUID("Coordinates: (%d, %d)", cur.x, cur.z); + }break; + + case E_BLOCK: + {}break; + + case E_PLAIN: + { + if (m_WayLength[cur.x][cur.z] > m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1) + { + m_WayLength[cur.x][cur.z] = m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1; + m_StartSide[cur.x][cur.z] = m_StartSide[a_CurBlock.x][a_CurBlock.z]; + m_WaveQueue.push(cur); + } + else if(m_WayLength[cur.x][cur.z] == m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1) + { + m_StartSide[cur.x][cur.z] |= m_StartSide[a_CurBlock.x][a_CurBlock.z]; + } + LOG_FLUID("Plain step: (%d, %d) from %d", cur.x, cur.z, m_StartSide[cur.x][cur.z]); + } + } } + } - Vector3i LowerPoints [] = { - Vector3i( a_X-1, a_Y-1, a_Z ), - Vector3i( a_X+1, a_Y-1, a_Z ), - Vector3i( a_X, a_Y-1, a_Z-1 ), - Vector3i( a_X, a_Y-1, a_Z+1 ), - }; - bool bFluidFound = false; - for( int i = 0; i < 4; ++i ) + std::vector< Vector3i > GetLowestPoints( int a_X, int a_Y, int a_Z ) + { + + std::vector< Vector3i > Points; //result + + Vector3i CornerGlobal(a_X - AREA_WIDTH/2, a_Y, a_Z - AREA_WIDTH/2); + + //TODO: rewrite without relief, get blocks directly in algorithm + for(int x=0; xGetBlock( LowerPoints[i].x, LowerPoints[i].y, LowerPoints[i].z ); - char Block2 = m_World->GetBlock( LowerPoints[i].x, a_Y, LowerPoints[i].z ); - if( Block1 == E_BLOCK_AIR && Block2 == E_BLOCK_AIR ) + for(int z=0; zGetBlock( CornerGlobal.x + x, CornerGlobal.y, CornerGlobal.z + z ); + char DownBlock = m_World->GetBlock( CornerGlobal.x + x, CornerGlobal.y-1, CornerGlobal.z + z ); + + if(m_Simulator->IsSolidBlock(UpperBlock)||(m_Simulator->IsStationaryBlock(UpperBlock))) + { + m_Relief[x][z] = E_BLOCK; + } + else if(m_Simulator->IsSolidBlock(DownBlock)) + { + m_Relief[x][z] = E_PLAIN; + } + else + { + m_Relief[x][z] = E_HOLE; + } + m_WayLength[x][z] = 255; + m_StartSide[x][z] = E_SIDE_NONE; } - else if( m_Simulator->IsAllowedBlock(Block2) && m_Simulator->IsPassableForFluid(Block1) ) + LOG_FLUID("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", m_Relief[x][0], m_Relief[x][1], m_Relief[x][2], m_Relief[x][3], m_Relief[x][4], m_Relief[x][5], m_Relief[x][6], m_Relief[x][7], m_Relief[x][8], m_Relief[x][9], m_Relief[x][10]); + } + + m_NearestHole = 5; + m_CurResult = 0; + while(!m_WaveQueue.empty()) m_WaveQueue.pop(); + + int left = AREA_WIDTH/2 - 1; + int right = AREA_WIDTH/2 + 1; + int center = AREA_WIDTH/2; + + Vector3i r(right, 0, center); //right block + Vector3i l(left, 0, center); //left block + Vector3i f(center, 0, right); //front block + Vector3i b(center, 0, left); //back block + Vector3i c(center, 0, center); //center block + + m_WayLength[c.x][c.z] = 0; + + Vector3i Nearest[] = {r, l, f, b}; + unsigned char Sides[] = {E_SIDE_RIGHT, E_SIDE_LEFT, E_SIDE_FRONT, E_SIDE_BACK}; + + for(int i=0; i<4; i++) + { + Vector3i cur = Nearest[i]; + switch(m_Relief[cur.x][cur.z]) { - bFluidFound = true; + case E_HOLE: + { + m_StartSide[cur.x][cur.z] = Sides[i]; + m_CurResult |= m_StartSide[cur.x][cur.z]; + m_NearestHole = 1; + LOG_FLUID("Hole found: %d \t curResult: %d", int(Sides[i]), int(m_CurResult) ); + }break; + + case E_BLOCK: + {}break; + + case E_PLAIN: + { + m_WaveQueue.push(cur); + m_StartSide[cur.x][cur.z] = Sides[i]; + m_WayLength[cur.x][cur.z] = 1; + LOG_FLUID("Plain found: %d", int(Sides[i])); + } } } - if( Points.size() == 0 && !bFluidFound ) + Vector3i curBlock; + + bool bContinue = !m_WaveQueue.empty(); + + if(!m_WaveQueue.empty()) + { + curBlock = m_WaveQueue.front(); + bContinue = (m_WayLength[curBlock.x][curBlock.z] < m_NearestHole); + } + + while(bContinue) + { + LOG_FLUID("while iteration" ); + curBlock = m_WaveQueue.front(); + UpdateWave(CornerGlobal, curBlock); + m_WaveQueue.pop(); + + bContinue = ( (!m_WaveQueue.empty()) && (m_WayLength[m_WaveQueue.front().x][m_WaveQueue.front().z] < m_NearestHole) ); + } + + + + if(m_CurResult & E_SIDE_LEFT) Points.push_back(Vector3i( a_X-1, a_Y, a_Z )); + if(m_CurResult & E_SIDE_RIGHT) Points.push_back(Vector3i( a_X+1, a_Y, a_Z )); + if(m_CurResult & E_SIDE_FRONT) Points.push_back(Vector3i( a_X, a_Y, a_Z+1 )); + if(m_CurResult & E_SIDE_BACK) Points.push_back(Vector3i( a_X, a_Y, a_Z-1 )); + + if(Points.empty()) { Vector3i LevelPoints [] = { Vector3i( a_X-1, a_Y, a_Z ), @@ -72,13 +197,35 @@ public: Points.push_back( LevelPoints[i] ); } } + return Points; + + } - std::list< Vector3i >* m_ActiveFluid; - std::list< Vector3i >* m_Buffer; + std::set< Vector3i >* m_ActiveFluid; + std::set< Vector3i >* m_Buffer; cWorld* m_World; cFluidSimulator *m_Simulator; + + const static int AREA_WIDTH = 11; + + const static unsigned char E_SIDE_RIGHT = 0x10; + const static unsigned char E_SIDE_LEFT = 0x20; + const static unsigned char E_SIDE_FRONT = 0x40; + const static unsigned char E_SIDE_BACK = 0x80; + const static unsigned char E_SIDE_NONE = 0x00; + + enum eRelief {E_HOLE = 0, E_PLAIN = 1, E_BLOCK = 2}; + + eRelief m_Relief[AREA_WIDTH][AREA_WIDTH]; + unsigned char m_WayLength[AREA_WIDTH][AREA_WIDTH]; + unsigned char m_StartSide[AREA_WIDTH][AREA_WIDTH]; + + std::queue m_WaveQueue; + + int m_NearestHole; + unsigned char m_CurResult; }; cFluidSimulator::cFluidSimulator( cWorld* a_World ) @@ -98,28 +245,20 @@ void cFluidSimulator::AddBlock( int a_X, int a_Y, int a_Z ) if(!IsAllowedBlock(m_World->GetBlock(a_X, a_Y, a_Z))) //This should save very much time because it doesn´t have to iterate through all blocks return; - // Check for duplicates - std::list< Vector3i > & ActiveFluid = *m_Data->m_ActiveFluid; - for( std::list< Vector3i >::iterator itr = ActiveFluid.begin(); itr != ActiveFluid.end(); ++itr ) - { - Vector3i & Pos = *itr; - if( Pos.x == a_X && Pos.y == a_Y && Pos.z == a_Z ) - return; - } - - ActiveFluid.push_back( Vector3i( a_X, a_Y, a_Z ) ); + std::set< Vector3i > & ActiveFluid = *m_Data->m_ActiveFluid; + ActiveFluid.insert( Vector3i( a_X, a_Y, a_Z ) ); } char cFluidSimulator::GetHighestLevelAround( int a_X, int a_Y, int a_Z ) { - char Max = m_MaxHeight + 1; + char Max = m_MaxHeight + m_FlowReduction; #define __HIGHLEVEL_CHECK__( x, y, z ) \ if( IsAllowedBlock( m_World->GetBlock( x, y, z ) ) ) \ { \ char Meta; \ if( (Meta = m_World->GetBlockMeta( x, y, z ) ) < Max ) Max = Meta; \ - else if( Meta == m_MaxHeight + 1 ) Max = 0; \ + else if( Meta == m_MaxHeight + m_FlowReduction ) Max = 0; \ if( Max == 0 ) return 0; \ } @@ -141,18 +280,24 @@ void cFluidSimulator::Simulate( float a_Dt ) std::swap( m_Data->m_ActiveFluid, m_Data->m_Buffer ); // Swap so blocks can be added to empty ActiveFluid array m_Data->m_ActiveFluid->clear(); - std::list< Vector3i > & FluidBlocks = *m_Data->m_Buffer; - for( std::list< Vector3i >::iterator itr = FluidBlocks.begin(); itr != FluidBlocks.end(); ++itr ) + std::set< Vector3i > & FluidBlocks = *m_Data->m_Buffer; + for( std::set< Vector3i >::iterator itr = FluidBlocks.begin(); itr != FluidBlocks.end(); ++itr ) { + const Vector3i & pos = *itr; + + if(UniqueSituation(pos)) + { + continue; + } - Vector3i & pos = *itr; char BlockID = m_World->GetBlock( pos.x, pos.y, pos.z ); if( IsAllowedBlock( BlockID ) ) // only care about own fluid { bool bIsFed = false; char Meta = m_World->GetBlockMeta( pos.x, pos.y, pos.z ); char Feed = Meta; - if( Meta & 8 ) // Falling fluid + if( BlockID == m_StationaryFluidBlock) Meta = 0; + if( Meta == 8 ) // Falling fluid { if( IsAllowedBlock( m_World->GetBlock(pos.x, pos.y+1, pos.z) ) ) // Block above is fluid { @@ -175,7 +320,7 @@ void cFluidSimulator::Simulate( float a_Dt ) { char DownID = m_World->GetBlock( pos.x, pos.y-1, pos.z ); bool bWashedAwayItem = CanWashAway( DownID ); - if( IsPassableForFluid(DownID) || bWashedAwayItem ) // free for fluid + if( (IsPassableForFluid(DownID) || bWashedAwayItem)&&!IsStationaryBlock(DownID) ) // free for fluid { if( bWashedAwayItem ) { @@ -185,15 +330,17 @@ void cFluidSimulator::Simulate( float a_Dt ) m_World->FastSetBlock( pos.x, pos.y-1, pos.z, m_FluidBlock, 8 ); // falling AddBlock( pos.x, pos.y-1, pos.z ); + ApplyUniqueToNearest(pos - Vector3i(0, 1, 0)); } - else // Not falling + if(IsSolidBlock(DownID)||( BlockID == m_StationaryFluidBlock)) // Not falling { if( Feed + m_FlowReduction < Meta ) { m_World->FastSetBlock( pos.x, pos.y, pos.z, m_FluidBlock, Feed + m_FlowReduction ); AddBlock( pos.x, pos.y, pos.z ); + ApplyUniqueToNearest(pos); } - else if( Meta < m_MaxHeight ) // max is the lowest, so it cannot spread + else if(( Meta < m_MaxHeight )||( BlockID == m_StationaryFluidBlock)) // max is the lowest, so it cannot spread { std::vector< Vector3i > Points = m_Data->GetLowestPoints( pos.x, pos.y, pos.z ); for( std::vector< Vector3i >::iterator itr = Points.begin(); itr != Points.end(); ++itr ) @@ -201,6 +348,9 @@ void cFluidSimulator::Simulate( float a_Dt ) Vector3i & p = *itr; char BlockID = m_World->GetBlock( p.x, p.y, p.z ); bool bWashedAwayItem = CanWashAway( BlockID ); + + if(!IsPassableForFluid(BlockID)) continue; + if( !IsAllowedBlock( BlockID ) ) { if( bWashedAwayItem ) @@ -210,10 +360,15 @@ void cFluidSimulator::Simulate( float a_Dt ) } if( p.y == pos.y ) + { m_World->FastSetBlock(p.x, p.y, p.z, m_FluidBlock, Meta + m_FlowReduction); + } else + { m_World->FastSetBlock(p.x, p.y, p.z, m_FluidBlock, 8); + } AddBlock( p.x, p.y, p.z ); + ApplyUniqueToNearest(p); } else // it's fluid { @@ -221,13 +376,14 @@ void cFluidSimulator::Simulate( float a_Dt ) if( PointMeta > Meta + m_FlowReduction ) { AddBlock( p.x, p.y, p.z ); + ApplyUniqueToNearest(p); } } } } } } - else // not fed + else// not fed { m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_AIR, 0 ); WakeUp( pos.x, pos.y, pos.z ); @@ -245,6 +401,11 @@ bool cFluidSimulator::IsPassableForFluid(char a_BlockID) || CanWashAway(a_BlockID); } +bool cFluidSimulator::IsStationaryBlock (char a_BlockID) +{ + return a_BlockID == m_StationaryFluidBlock; +} + bool cFluidSimulator::CanWashAway( char a_BlockID ) { switch( a_BlockID ) @@ -260,6 +421,15 @@ bool cFluidSimulator::CanWashAway( char a_BlockID ) }; } +bool cFluidSimulator::IsSolidBlock( char a_BlockID ) +{ + return !(a_BlockID == E_BLOCK_AIR + || a_BlockID == E_BLOCK_FIRE + || IsBlockLava(a_BlockID) + || IsBlockWater(a_BlockID) + || CanWashAway(a_BlockID)); +} + //TODO Not working very well yet :s Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over) { @@ -346,4 +516,109 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a return NONE; +} + +bool cFluidSimulator::UniqueSituation(Vector3i a_Pos) +{ + bool result = false; + + char BlockId = m_World->GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); + char Meta = m_World->GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); + + if(IsBlockWater(BlockId)) + { + + char UpperBlock = m_World->GetBlock( a_Pos.x, a_Pos.y + 1, a_Pos.z ); + if(IsBlockLava(UpperBlock)) + { + m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_STONE, 0); + } + + + if(BlockId != E_BLOCK_STATIONARY_WATER) + { + char DownBlockId = m_World->GetBlock( a_Pos.x, a_Pos.y-1, a_Pos.z ); + if(IsSolidBlock(DownBlockId)) + { + Vector3i LevelPoints [] = { + Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), + }; + int SourceBlocksCount = 0; + for(int i=0; i<4; i++) + { + if (m_World->GetBlock(LevelPoints[i].x, LevelPoints[i].y, LevelPoints[i].z)==E_BLOCK_STATIONARY_WATER) + { + SourceBlocksCount++; + } + } + if(SourceBlocksCount>=2) + { + m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_STATIONARY_WATER, 0); + } + } + + } + } + + if(IsBlockLava(BlockId)) + { + bool bWater = false; + + char UpperBlock = m_World->GetBlock( a_Pos.x, a_Pos.y + 1, a_Pos.z ); + if (IsBlockWater(UpperBlock)) + { + bWater = true; + } + else + { + Vector3i LevelPoints [] = { + Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), + }; + + for(int i=0; i<4; i++) + { + if (IsBlockWater(m_World->GetBlock(LevelPoints[i].x, LevelPoints[i].y, LevelPoints[i].z))) + { + bWater = true; + } + } + } + + + if(bWater) + { + if(BlockId == E_BLOCK_STATIONARY_LAVA) + { + m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_OBSIDIAN, 0); + } + else if (MetaSetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_COBBLESTONE, 0); + } + } + } + + return result; +} + +void cFluidSimulator::ApplyUniqueToNearest(Vector3i a_Pos) +{ + Vector3i NearPoints [] = { + Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), + Vector3i( a_Pos.x, a_Pos.y-1, a_Pos.z ) + }; + + for(int i=0; i<5; i++) + { + UniqueSituation(NearPoints[i]); + } } \ No newline at end of file -- cgit v1.2.3