1
0

First attempt at implementing a cLineBlockTracer class

Not yet tested, will probably have lots of bugs, if it is at all usable.
This commit is contained in:
madmaxoft 2013-08-04 13:25:48 +02:00
parent 106308796d
commit 5fe7008966
4 changed files with 459 additions and 1 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
@ -306,6 +306,10 @@
RelativePath="..\source\BlockID.h"
>
</File>
<File
RelativePath="..\source\BlockTracer.h"
>
</File>
<File
RelativePath="..\source\ByteBuffer.cpp"
>
@ -545,6 +549,14 @@
RelativePath="..\source\LinearUpscale.h"
>
</File>
<File
RelativePath="..\source\LineBlockTracer.cpp"
>
</File>
<File
RelativePath="..\source\LineBlockTracer.h"
>
</File>
<File
RelativePath="..\source\Log.cpp"
>

104
source/BlockTracer.h Normal file
View File

@ -0,0 +1,104 @@
// BlockTracer.h
// Declares the classes common for all blocktracers
#pragma once
// fwd: World.h
class cWorld;
class cBlockTracer abstract
{
public:
/** The callback class is used to notify the caller of individual events that are being traced.
*/
class cCallbacks abstract
{
public:
/** Called on each block encountered along the path, including the first block (path start)
When this callback returns true, the tracing is aborted.
*/
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
/** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded
When this callback returns true, the tracing is aborted.
*/
virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ) {}
/** Called when the path goes out of world, either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height)
The coords specify the exact point at which the path exited the world.
If this callback returns true, the tracing is aborted.
Note that some paths can go out of the world and come back again (parabola),
in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls
*/
virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) {}
/** Called when the path goes into the world, from either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height)
The coords specify the exact point at which the path entered the world.
If this callback returns true, the tracing is aborted.
Note that some paths can go out of the world and come back again (parabola),
in such a case this callback is followed by further OnNextBlock() calls
*/
virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) {}
/** Called when the path is sure not to hit any more blocks.
Note that for some shapes this might never happen (line with constant Y)
*/
virtual void OnNoMoreHits(void) {}
/** Called when the block tracing walks into a chunk that is not allocated.
This usually means that the tracing is aborted.
*/
virtual void OnNoChunk(void) {}
} ;
/// Creates the BlockTracer parent with the specified callbacks
cBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) :
m_World(&a_World),
m_Callbacks(&a_Callbacks)
{
}
/// Sets new world, returns the old one. Note that both need to be valid
cWorld & SetWorld(cWorld & a_World)
{
cWorld & Old = *m_World;
m_World = &a_World;
return Old;
}
/// Sets new callbacks, returns the old ones. Note that both need to be valid
cCallbacks & SetCallbacks(cCallbacks & a_NewCallbacks)
{
cCallbacks & Old = *m_Callbacks;
m_Callbacks = &a_NewCallbacks;
return Old;
}
protected:
/// The world upon which to operate
cWorld * m_World;
/// The callback to use for reporting
cCallbacks * m_Callbacks;
} ;

258
source/LineBlockTracer.cpp Normal file
View File

@ -0,0 +1,258 @@
// LineBlockTracer.cpp
// Implements the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points
#include "Globals.h"
#include "LineBlockTracer.h"
#include "Vector3d.h"
#include "World.h"
#include "Chunk.h"
cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) :
super(a_World, a_Callbacks)
{
}
bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End)
{
cLineBlockTracer Tracer(a_World, a_Callbacks);
return Tracer.Trace(a_Start.x, a_Start.y, a_Start.z, a_End.x, a_End.y, a_End.z);
}
bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks &a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
{
cLineBlockTracer Tracer(a_World, a_Callbacks);
return Tracer.Trace(a_StartX, a_StartY, a_StartZ, a_EndX, a_EndY, a_EndZ);
}
bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
{
// Initialize the member veriables:
m_StartX = a_StartX;
m_StartY = a_StartY;
m_StartZ = a_StartZ;
m_EndX = a_EndX;
m_EndY = a_EndY;
m_EndZ = a_EndZ;
m_DirX = (m_StartX < m_EndX) ? 1 : -1;
m_DirY = (m_StartY < m_EndY) ? 1 : -1;
m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1;
// Check the start coords, adjust into the world:
if (m_StartY < 0)
{
if (m_EndY < 0)
{
// Nothing to trace
m_Callbacks->OnNoMoreHits();
return true;
}
FixStartBelowWorld();
m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
}
else if (m_StartY >= cChunkDef::Height)
{
if (m_EndY >= cChunkDef::Height)
{
m_Callbacks->OnNoMoreHits();
return true;
}
FixStartAboveWorld();
m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
}
m_CurrentX = (int)floor(m_StartX);
m_CurrentY = (int)floor(m_StartY);
m_CurrentZ = (int)floor(m_StartZ);
m_DiffX = m_EndX - m_StartX;
m_DiffY = m_EndY - m_StartY;
m_DiffZ = m_EndZ - m_StartZ;
// The actual trace is handled with ChunkMapCS locked by calling our Item() for the specified chunk
int BlockX = (int)floor(m_StartX);
int BlockZ = (int)floor(m_StartZ);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
return m_World->DoWithChunk(ChunkX, ChunkZ, *this);
}
void cLineBlockTracer::FixStartAboveWorld(void)
{
// We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on
// Therefore we use an EPS-offset from the height, as small as reasonably possible.
const double Height = (double)cChunkDef::Height - 0.00001;
CalcXZIntersection(Height, m_StartX, m_StartZ);
m_StartY = Height;
}
void cLineBlockTracer::FixStartBelowWorld(void)
{
CalcXZIntersection(0, m_StartX, m_StartZ);
m_StartY = 0;
}
void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ)
{
double Ratio = (m_StartY - a_Y) / (m_StartY - m_EndY);
a_IntersectX = m_StartX + (m_EndX - m_StartX) * Ratio;
a_IntersectZ = m_StartZ + (m_EndZ - m_StartZ) * Ratio;
}
bool cLineBlockTracer::MoveToNextBlock(void)
{
// Find out which of the current block's walls gets hit by the path:
static const double EPS = 0.00001;
double Coeff = 1;
enum eDirection
{
dirNONE,
dirX,
dirY,
dirZ,
} Direction = dirNONE;
if (abs(m_DiffX) > EPS)
{
double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX;
Coeff = (m_EndX - DestX) / m_DiffX;
Direction = dirX;
}
if (abs(m_DiffY) > EPS)
{
double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY;
double CoeffY = (DestY - m_StartY) / m_DiffY;
if (CoeffY < Coeff)
{
Coeff = CoeffY;
Direction = dirY;
}
}
if (abs(m_DiffZ) > EPS)
{
double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ;
double CoeffZ = (DestZ - m_StartZ) / m_DiffZ;
if (CoeffZ < Coeff)
{
Coeff = CoeffZ;
Direction = dirZ;
}
}
// Based on the wall hit, adjust the current coords
switch (Direction)
{
case dirX: m_CurrentX += m_DirX; break;
case dirY: m_CurrentY += m_DirY; break;
case dirZ: m_CurrentZ += m_DirZ; break;
case dirNONE: return false;
}
return true;
}
bool cLineBlockTracer::Item(cChunk * a_Chunk)
{
ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height)); // This should be provided by FixStartAboveWorld() / FixStartBelowWorld()
// This is the actual line tracing loop.
bool Finished = false;
while (true)
{
// Report the current block through the callbacks:
if (a_Chunk == NULL)
{
m_Callbacks->OnNoChunk();
return false;
}
if (a_Chunk->IsValid())
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
int RelX = FAST_FLOOR_DIV(m_CurrentX, cChunkDef::Width);
int RelZ = FAST_FLOOR_DIV(m_CurrentZ, cChunkDef::Width);
a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta);
if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta))
{
// The callback terminated the trace
return false;
}
}
else
{
if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ))
{
// The callback terminated the trace
return false;
}
}
// Move to next block
if (!MoveToNextBlock())
{
// We've reached the end
m_Callbacks->OnNoMoreHits();
return true;
}
// Update the current chunk
if (a_Chunk != NULL)
{
a_Chunk = a_Chunk->GetNeighborChunk(m_CurrentX, m_CurrentZ);
}
if ((m_CurrentY < 0) || (m_CurrentY >= cChunkDef::Height))
{
// We've gone out of the world, that's the end of this trace
double IntersectX, IntersectZ;
CalcXZIntersection(m_CurrentY, IntersectX, IntersectZ);
if (m_Callbacks->OnOutOfWorld(IntersectX, m_CurrentY, IntersectZ))
{
// The callback terminated the trace
return false;
}
m_Callbacks->OnNoMoreHits();
return true;
}
}
}

84
source/LineBlockTracer.h Normal file
View File

@ -0,0 +1,84 @@
// LineBlockTracer.h
// Declares the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points
#pragma once
#include "BlockTracer.h"
// fwd: Chunk.h
class cChunk;
// fwd: cChunkMap.h
typedef cItemCallback<cChunk> cChunkCallback;
class cLineBlockTracer :
public cBlockTracer,
public cChunkCallback
{
typedef cBlockTracer super;
public:
cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks);
/// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits())
bool Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ);
// Utility functions for simple one-line usage:
/// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits())
static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ);
/// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits())
static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End);
protected:
// The start point of the trace
double m_StartX, m_StartY, m_StartZ;
// The end point of the trace
double m_EndX, m_EndY, m_EndZ;
// The difference in coords, End - Start
double m_DiffX, m_DiffY, m_DiffZ;
// The increment at which the block coords are going from Start to End; either +1 or -1
int m_DirX, m_DirY, m_DirZ;
// The current block
int m_CurrentX, m_CurrentY, m_CurrentZ;
/// Adjusts the start point above the world to just at the world's top
void FixStartAboveWorld(void);
/// Adjusts the start point below the world to just at the world's bottom
void FixStartBelowWorld(void);
/// Calculates the XZ coords of an intersection with the specified Yconst plane; assumes that such an intersection exists
void CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ);
/// Moves m_Current to the next block on the line; returns false if no move is possible (reached the end)
bool MoveToNextBlock(void);
// cChunkCallback overrides:
virtual bool Item(cChunk * a_Chunk) override;
} ;