fix octree corruption bug because of node vector reallocation
This commit is contained in:
@ -4,8 +4,8 @@
|
||||
#include <raylib.h>
|
||||
|
||||
#define PRINT_TIMINGS
|
||||
// #define WEB // Disables multithreading
|
||||
#define BARNES_HUT // Use octree BH instead of uniform grid
|
||||
// #define WEB // Disables multithreading
|
||||
|
||||
// Window
|
||||
constexpr int INITIAL_WIDTH = 800;
|
||||
@ -31,7 +31,7 @@ constexpr float ROT_SPEED = 1.0;
|
||||
|
||||
// Physics Engine
|
||||
constexpr float SIM_SPEED = 4.0; // How large each update should be
|
||||
constexpr float TIMESTEP = 1.0 / 60; // Do 60 physics updates per second
|
||||
constexpr float TIMESTEP = 1.0 / 90; // Do 90 physics updates per second
|
||||
constexpr float MASS = 1.0; // Mass spring system
|
||||
constexpr float SPRING_CONSTANT = 5.0; // Mass spring system
|
||||
constexpr float DAMPENING_CONSTANT = 1.0; // Mass spring system
|
||||
|
||||
@ -20,7 +20,8 @@ public:
|
||||
: mass_center(Vector3Zero()), mass_total(0.0),
|
||||
children(-1, -1, -1, -1, -1, -1, -1, -1), mass_id(-1), leaf(true) {}
|
||||
|
||||
~OctreeNode() {}
|
||||
public:
|
||||
auto ChildCount() const -> int;
|
||||
};
|
||||
|
||||
class Octree {
|
||||
@ -35,8 +36,6 @@ public:
|
||||
Octree(Octree &&move) = delete;
|
||||
Octree &operator=(Octree &&move) = delete;
|
||||
|
||||
~Octree() {}
|
||||
|
||||
public:
|
||||
auto CreateNode(const Vector3 &box_min, const Vector3 &box_max) -> int;
|
||||
|
||||
@ -47,7 +46,9 @@ public:
|
||||
auto Insert(int node_idx, int mass_id, const Vector3 &pos, float mass)
|
||||
-> void;
|
||||
|
||||
auto CalculateForce(int node_idx, const Vector3 &pos) -> Vector3;
|
||||
auto CalculateForce(int node_idx, const Vector3 &pos) const -> Vector3;
|
||||
|
||||
auto Print() const -> void;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -106,8 +106,6 @@ public:
|
||||
MassSpringSystem(MassSpringSystem &move) = delete;
|
||||
MassSpringSystem &operator=(MassSpringSystem &&move) = delete;
|
||||
|
||||
~MassSpringSystem() {};
|
||||
|
||||
private:
|
||||
#ifdef BARNES_HUT
|
||||
auto BuildOctree() -> void;
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@ -23,6 +23,9 @@
|
||||
// - Click states to display them in the board
|
||||
// - Find shortest path to any winning state and mark it in the graph
|
||||
// - Also mark the next move along the path on the board
|
||||
// TODO: Smooth camera (on target change)
|
||||
// TODO: Mark the starting state
|
||||
// TODO: Mark the visited states
|
||||
|
||||
auto main(int argc, char *argv[]) -> int {
|
||||
// if (argc < 2) {
|
||||
@ -52,10 +55,10 @@ auto main(int argc, char *argv[]) -> int {
|
||||
std::chrono::duration<double, std::milli>(0);
|
||||
std::chrono::duration<double, std::milli> render_time_accumulator =
|
||||
std::chrono::duration<double, std::milli>(0);
|
||||
int loop_count = 0;
|
||||
long loop_count = 0;
|
||||
#endif
|
||||
float timestep_accumulator = 0.0;
|
||||
int update_accumulator = 0;
|
||||
double timestep_accumulator = 0.0;
|
||||
long update_accumulator = 0;
|
||||
while (!WindowShouldClose()) {
|
||||
timestep_accumulator += GetFrameTime();
|
||||
|
||||
@ -123,8 +126,9 @@ auto main(int argc, char *argv[]) -> int {
|
||||
<< render_time_accumulator / loop_count << "." << std::endl;
|
||||
std::cout << " - Physics updates avg: "
|
||||
<< static_cast<float>(update_accumulator) / loop_count
|
||||
<< "x per frame (" << timestep_accumulator << "s remaining)."
|
||||
<< std::endl;
|
||||
<< "x per frame ("
|
||||
<< static_cast<int>(timestep_accumulator / TIMESTEP)
|
||||
<< "x behind)." << std::endl;
|
||||
last_print_time = GetTime();
|
||||
physics_time_accumulator = std::chrono::duration<double, std::milli>(0);
|
||||
render_time_accumulator = std::chrono::duration<double, std::milli>(0);
|
||||
|
||||
@ -1,8 +1,20 @@
|
||||
#include "octree.hpp"
|
||||
#include "config.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <raymath.h>
|
||||
|
||||
auto OctreeNode::ChildCount() const -> int {
|
||||
int child_count = 0;
|
||||
for (int child : children) {
|
||||
if (child != -1) {
|
||||
++child_count;
|
||||
}
|
||||
}
|
||||
return child_count;
|
||||
}
|
||||
|
||||
auto Octree::CreateNode(const Vector3 &box_min, const Vector3 &box_max) -> int {
|
||||
OctreeNode node;
|
||||
node.box_min = box_min;
|
||||
@ -43,8 +55,8 @@ auto Octree::GetChildBounds(int node_idx, int octant)
|
||||
(node.box_min.y + node.box_max.y) / 2.0,
|
||||
(node.box_min.z + node.box_max.z) / 2.0);
|
||||
|
||||
Vector3 min;
|
||||
Vector3 max;
|
||||
Vector3 min = Vector3Zero();
|
||||
Vector3 max = Vector3Zero();
|
||||
|
||||
// If (octant & 1), the octant is to the right of the node region's x-axis
|
||||
// (see GetOctant). This means the left bound is the x-axis and the right
|
||||
@ -61,31 +73,34 @@ auto Octree::GetChildBounds(int node_idx, int octant)
|
||||
|
||||
auto Octree::Insert(int node_idx, int mass_id, const Vector3 &pos, float mass)
|
||||
-> void {
|
||||
OctreeNode &node = nodes[node_idx];
|
||||
// NOTE: Do not store a nodes[node_idx] reference beforehand as the nodes
|
||||
// vector might reallocate during this function
|
||||
|
||||
if (node.leaf && node.mass_id == -1) {
|
||||
if (nodes[node_idx].leaf && nodes[node_idx].mass_id == -1) {
|
||||
// We can place the particle in the empty leaf
|
||||
node.mass_id = mass_id;
|
||||
node.mass_center = pos;
|
||||
node.mass_total = mass;
|
||||
nodes[node_idx].mass_id = mass_id;
|
||||
nodes[node_idx].mass_center = pos;
|
||||
nodes[node_idx].mass_total = mass;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.leaf) {
|
||||
if (nodes[node_idx].leaf) {
|
||||
// The leaf is occupied, we need to subdivide
|
||||
int existing_id = node.mass_id;
|
||||
Vector3 existing_pos = node.mass_center;
|
||||
float existing_mass = node.mass_total;
|
||||
node.mass_id = -1;
|
||||
node.leaf = false;
|
||||
int existing_id = nodes[node_idx].mass_id;
|
||||
Vector3 existing_pos = nodes[node_idx].mass_center;
|
||||
float existing_mass = nodes[node_idx].mass_total;
|
||||
nodes[node_idx].mass_id = -1;
|
||||
nodes[node_idx].leaf = false;
|
||||
nodes[node_idx].mass_total = 0.0;
|
||||
|
||||
// Re-add the existing mass into a new empty leaf (see above)
|
||||
// Re-insert the existing mass into a new empty leaf (see above)
|
||||
int oct = GetOctant(node_idx, existing_pos);
|
||||
if (node.children[oct] == -1) {
|
||||
if (nodes[node_idx].children[oct] == -1) {
|
||||
auto [min, max] = GetChildBounds(node_idx, oct);
|
||||
node.children[oct] = CreateNode(min, max);
|
||||
nodes[node_idx].children[oct] = CreateNode(min, max);
|
||||
}
|
||||
Insert(node.children[oct], existing_id, existing_pos, existing_mass);
|
||||
Insert(nodes[node_idx].children[oct], existing_id, existing_pos,
|
||||
existing_mass);
|
||||
}
|
||||
|
||||
// Insert the new mass
|
||||
@ -97,23 +112,25 @@ auto Octree::Insert(int node_idx, int mass_id, const Vector3 &pos, float mass)
|
||||
Insert(nodes[node_idx].children[oct], mass_id, pos, mass);
|
||||
|
||||
// Update the center of mass
|
||||
node = nodes[node_idx];
|
||||
float new_mass = node.mass_total + mass;
|
||||
node.mass_center.x =
|
||||
(node.mass_center.x * node.mass_total + pos.x) / new_mass;
|
||||
node.mass_center.y =
|
||||
(node.mass_center.y * node.mass_total + pos.y) / new_mass;
|
||||
node.mass_center.z =
|
||||
(node.mass_center.z * node.mass_total + pos.z) / new_mass;
|
||||
node.mass_total = new_mass;
|
||||
float new_mass = nodes[node_idx].mass_total + mass;
|
||||
nodes[node_idx].mass_center.x =
|
||||
(nodes[node_idx].mass_center.x * nodes[node_idx].mass_total + pos.x) /
|
||||
new_mass;
|
||||
nodes[node_idx].mass_center.y =
|
||||
(nodes[node_idx].mass_center.y * nodes[node_idx].mass_total + pos.y) /
|
||||
new_mass;
|
||||
nodes[node_idx].mass_center.z =
|
||||
(nodes[node_idx].mass_center.z * nodes[node_idx].mass_total + pos.z) /
|
||||
new_mass;
|
||||
nodes[node_idx].mass_total = new_mass;
|
||||
}
|
||||
|
||||
auto Octree::CalculateForce(int node_idx, const Vector3 &pos) -> Vector3 {
|
||||
auto Octree::CalculateForce(int node_idx, const Vector3 &pos) const -> Vector3 {
|
||||
if (node_idx < 0) {
|
||||
return Vector3Zero();
|
||||
}
|
||||
|
||||
OctreeNode &node = nodes[node_idx];
|
||||
const OctreeNode &node = nodes[node_idx];
|
||||
if (node.mass_total == 0.0f) {
|
||||
return Vector3Zero();
|
||||
}
|
||||
@ -146,3 +163,11 @@ auto Octree::CalculateForce(int node_idx, const Vector3 &pos) -> Vector3 {
|
||||
|
||||
return force;
|
||||
}
|
||||
|
||||
auto Octree::Print() const -> void {
|
||||
std::cout << "Octree Start ===========================" << std::endl;
|
||||
for (const auto &node : nodes) {
|
||||
std::cout << "Center: " << node.mass_center << ", Mass: " << node.mass_total
|
||||
<< ", Direct Children: " << node.ChildCount() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "physics.hpp"
|
||||
#include "config.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
@ -249,15 +250,9 @@ auto MassSpringSystem::CalculateRepulsionForces() -> void {
|
||||
solve_octree(i);
|
||||
}
|
||||
#else
|
||||
threads.detach_blocks(
|
||||
0, mass_pointers.size(),
|
||||
[&](int start, int end) {
|
||||
for (int i = start; i < end; ++i) {
|
||||
solve_octree(i);
|
||||
}
|
||||
},
|
||||
256);
|
||||
threads.wait();
|
||||
BS::multi_future<void> loop_future =
|
||||
threads.submit_loop(0, mass_pointers.size(), solve_octree, 256);
|
||||
loop_future.wait();
|
||||
#endif
|
||||
|
||||
#else
|
||||
@ -333,15 +328,9 @@ auto MassSpringSystem::CalculateRepulsionForces() -> void {
|
||||
calculate_grid(i);
|
||||
}
|
||||
#else
|
||||
threads.detach_blocks(
|
||||
0, mass_pointers.size(),
|
||||
[&](int start, int end) {
|
||||
for (int i = start; i < end; ++i) {
|
||||
solve_grid(i);
|
||||
}
|
||||
},
|
||||
512);
|
||||
threads.wait();
|
||||
BS::multi_future<void> loop_future =
|
||||
threads.submit_loop(0, mass_pointers.size(), solve_grid, 512);
|
||||
loop_future.wait();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -16,8 +16,8 @@ auto StateManager::ResetState() -> void {
|
||||
current_state = generators[current_preset]();
|
||||
previous_state = current_state;
|
||||
if (edited) {
|
||||
// We also need to clear the graph, in case the state has been edited.
|
||||
// Then the graph would contain states that are impossible to reach.
|
||||
// We also need to clear the graph in case the state has been edited
|
||||
// because the graph could contain states that are impossible to reach now.
|
||||
ClearGraph();
|
||||
edited = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user