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