Skip to content

Commit

Permalink
Top down tree rebuild (#41)
Browse files Browse the repository at this point in the history
Efficient top down rebuild of dynamic tree using SAH and bin sort.
  • Loading branch information
erincatto authored Jul 14, 2023
1 parent e28d936 commit 7664b7d
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 67 deletions.
15 changes: 14 additions & 1 deletion include/box2d/dynamic_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ typedef struct b2DynamicTree
int32_t nodeCount;
int32_t nodeCapacity;
int32_t freeList;
int32_t insertionCount;
int32_t proxyCount;
} b2DynamicTree;

/// Constructing the tree initializes the node pool.
Expand Down Expand Up @@ -99,6 +99,19 @@ float b2DynamicTree_GetAreaRatio(const b2DynamicTree* tree);
/// Build an optimal tree. Very expensive. For testing.
void b2DynamicTree_RebuildBottomUp(b2DynamicTree* tree);

struct b2ProxyMap
{
void* userData;
};

/// Get the number of proxies created
int32_t b2DynamicTree_GetProxyCount(const b2DynamicTree* tree);

/// Rebuild a the tree top down using the surface area heuristic. The provide map array must have length equal
/// to the proxy count. This map allows you to update your proxy indices since this operation invalidates the original indices.
/// See b2DynamicTree_GetProxyCount.
void b2DynamicTree_RebuildTopDownSAH(b2DynamicTree* tree, struct b2ProxyMap* mapArray, int32_t mapCount);

/// Shift the world origin. Useful for large worlds.
/// The shift formula is: position -= newOrigin
/// @param newOrigin the new origin with respect to the old origin
Expand Down
39 changes: 35 additions & 4 deletions samples/collection/sample_dynamic_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ class DynamicTree : public Sample
m_moveFraction = 0.0f;
m_moveDelta = 0.1f;
m_proxies = nullptr;
m_mapArray = nullptr;
m_proxyCount = 0;
m_proxyCapacity = 0;
m_wx = 0.5f;
m_wy = 0.5f;

m_rowCount = 10;
m_columnCount = 10;
m_rowCount = 20;
m_columnCount = 20;
memset(&m_tree, 0, sizeof(m_tree));
BuildTree();
m_timeStamp = 0;
Expand All @@ -47,23 +48,26 @@ class DynamicTree : public Sample
m_endPoint = {0.0f, 0.0f};
m_queryDrag = false;
m_rayDrag = false;

m_topDown = false;
m_validate = true;
}

~DynamicTree()
{
free(m_proxies);
free(m_mapArray);
b2DynamicTree_Destroy(&m_tree);
}

void BuildTree()
{
b2DynamicTree_Destroy(&m_tree);
free(m_proxies);
free(m_mapArray);

m_proxyCapacity = m_rowCount * m_columnCount;
m_proxies = static_cast<Proxy*>(malloc(m_proxyCapacity * sizeof(Proxy)));
m_mapArray = static_cast<struct b2ProxyMap*>(malloc(m_proxyCapacity * sizeof(struct b2ProxyMap)));
m_proxyCount = 0;

float y = -4.0f;
Expand Down Expand Up @@ -95,12 +99,14 @@ class DynamicTree : public Sample

y += m_wy;
}

m_topDown = false;
}

void UpdateUI() override
{
ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
ImGui::SetNextWindowSize(ImVec2(230.0f, 220.0f));
ImGui::SetNextWindowSize(ImVec2(240.0f, 250.0f));
ImGui::Begin("Tree Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);

bool changed = false;
Expand Down Expand Up @@ -131,6 +137,18 @@ class DynamicTree : public Sample
{
}

if (ImGui::Button("Rebuild Top Down"))
{
assert(m_proxyCount == b2DynamicTree_GetProxyCount(&m_tree));
b2DynamicTree_RebuildTopDownSAH(&m_tree, m_mapArray, m_proxyCount);
for (int32_t i = 0; i < m_proxyCount; ++i)
{
Proxy* proxy = static_cast<Proxy*>(m_mapArray[i].userData);
proxy->proxyId = i;
}
m_topDown = true;
}

ImGui::Separator();

ImGui::Text("mouse button 1: ray cast");
Expand Down Expand Up @@ -245,6 +263,17 @@ class DynamicTree : public Sample
b2DynamicTree_Validate(&m_tree);
}

if (m_topDown)
{
g_draw.DrawString(5, m_textLine, "top down");
m_textLine += m_textIncrement;
}
else
{
g_draw.DrawString(5, m_textLine, "incremental");
m_textLine += m_textIncrement;
}

m_timeStamp += 1;
}

Expand All @@ -256,6 +285,7 @@ class DynamicTree : public Sample
b2DynamicTree m_tree;
int m_rowCount, m_columnCount;
Proxy* m_proxies;
struct b2ProxyMap* m_mapArray;
int m_proxyCapacity;
int m_proxyCount;
int m_timeStamp;
Expand All @@ -270,6 +300,7 @@ class DynamicTree : public Sample
bool m_rayDrag;
bool m_queryDrag;
bool m_validate;
bool m_topDown;
};

static bool QueryCallback(int32_t proxyId, void* userData, void* context)
Expand Down
27 changes: 20 additions & 7 deletions samples/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,13 +498,26 @@ int main(int, char**)
{
#if defined(_WIN32)
// Enable memory-leak reports
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
//_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
//_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
//_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
//_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
{
// Get the current bits
//int tmp = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);

// Clear the upper 16 bits and OR in the desired frequency
//tmp = (tmp & 0x0000FFFF) | _CRTDBG_CHECK_EVERY_16_DF;

// Set the new bits
//_CrtSetDbgFlag(tmp);

//_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_CRT_DF | _CRTDBG_LEAK_CHECK_DF);
//_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
//_CrtSetDbgFlag(_CRTDBG_DELAY_FREE_MEM_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
}
_CrtSetAllocHook(MyAllocHook);
//_CrtSetBreakAlloc(196);
#endif
Expand Down
Loading

0 comments on commit 7664b7d

Please sign in to comment.