summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source/BlockTracer.h104
-rw-r--r--source/LineBlockTracer.cpp258
-rw-r--r--source/LineBlockTracer.h84
3 files changed, 446 insertions, 0 deletions
diff --git a/source/BlockTracer.h b/source/BlockTracer.h
new file mode 100644
index 000000000..4adc61f00
--- /dev/null
+++ b/source/BlockTracer.h
@@ -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;
+} ;
+
+
+
+
diff --git a/source/LineBlockTracer.cpp b/source/LineBlockTracer.cpp
new file mode 100644
index 000000000..c5939cb78
--- /dev/null
+++ b/source/LineBlockTracer.cpp
@@ -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;
+ }
+ }
+}
+
+
+
+
diff --git a/source/LineBlockTracer.h b/source/LineBlockTracer.h
new file mode 100644
index 000000000..4616cb191
--- /dev/null
+++ b/source/LineBlockTracer.h
@@ -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;
+} ;
+
+
+
+
+