From 7f01fd0483fa327b4682f237cda611e7d8565a44 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 16 Mar 2026 00:22:52 -0500 Subject: [PATCH] boids: initial --- Makefile | 4 +- include/boids.h | 53 ++++++++++++++++++ include/boids_scene.h | 12 +++++ include/line_art.h | 4 ++ main.lua | 1 + minecraft/gen/arrow.obj | 29 ++++++++++ src/boids.cpp | 102 +++++++++++++++++++++++++++++++++++ src/boids_scene.cpp | 116 ++++++++++++++++++++++++++++++++++++++++ src/line_art.cpp | 28 ++++++---- src/test.cpp | 11 +++- 10 files changed, 348 insertions(+), 12 deletions(-) create mode 100644 include/boids.h create mode 100644 include/boids_scene.h create mode 100644 minecraft/gen/arrow.obj create mode 100644 src/boids.cpp create mode 100644 src/boids_scene.cpp diff --git a/Makefile b/Makefile index 1c1f0f8..cb52a23 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,9 @@ OBJS = \ src/hud.o \ src/lighting.o \ src/collision_scene.o \ - src/line_art.o + src/line_art.o \ + src/boids.o \ + src/boids_scene.o all: test.so diff --git a/include/boids.h b/include/boids.h new file mode 100644 index 0000000..de3f0a7 --- /dev/null +++ b/include/boids.h @@ -0,0 +1,53 @@ +namespace boids { + + struct boid_configuration { + float vision_length; + + struct { + float coefficient; + } cohesion; + + struct { + float coefficient; + } alignment; + + struct { + float minimum_length; + float coefficient; + } separation; + + struct { + float coefficient; + } bounds; + }; + + struct boid_forces { + float vision_neighbor_count; + + struct { + // intermediate state + XMVECTOR center_of_mass; + XMVECTOR force; + } cohesion; + + struct { + // intermediate state + XMVECTOR average_velocity; + XMVECTOR force; + } alignment; + + struct { + // intermediate state + XMVECTOR force; + } separation; + }; + + struct boid { + XMVECTOR position; + XMVECTOR velocity; + }; + + extern boid_configuration configuration; + + boid_forces const & evaluate(boid const * const boids, int length, int boid_index); +} diff --git a/include/boids_scene.h b/include/boids_scene.h new file mode 100644 index 0000000..5419748 --- /dev/null +++ b/include/boids_scene.h @@ -0,0 +1,12 @@ +#pragma once + +namespace boids_scene { + + void update(int up, int down, int left, int right, + int w, int s, int a, int d, + int t, int g, int f, int h, + int i, int k, int j, int l); + + void load(); + void draw(); +} diff --git a/include/line_art.h b/include/line_art.h index aab9ff5..89857f5 100644 --- a/include/line_art.h +++ b/include/line_art.h @@ -1,5 +1,7 @@ #pragma once +#include "directxmath/directxmath.h" + namespace line_art { void load(); @@ -14,5 +16,7 @@ namespace line_art { void draw_capsule(XMMATRIX const & transform, XMVECTOR a, XMVECTOR b, float radius); void draw_cube(XMMATRIX const & transform, XMVECTOR const & position); void scene_start(XMMATRIX const & transform); + void draw_grid(XMMATRIX const & transform); void set_color(float r, float g, float b); + void set_colorv(XMFLOAT3 const & value); } diff --git a/main.lua b/main.lua index 0b24812..92400dc 100644 --- a/main.lua +++ b/main.lua @@ -120,6 +120,7 @@ function love.run() love.graphics.present() love.timer.sleep(0.001) + --love.timer.sleep(0.1) --local fps = love.timer.getFPS( ) --print(fps) end diff --git a/minecraft/gen/arrow.obj b/minecraft/gen/arrow.obj new file mode 100644 index 0000000..cd6a620 --- /dev/null +++ b/minecraft/gen/arrow.obj @@ -0,0 +1,29 @@ +# Blender 5.0.0 +# www.blender.org +o Cone +v 0.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 0.000000 +v 0.000000 -1.000000 1.000000 +v -1.000000 -1.000000 0.000000 +v 0.000000 1.000000 0.000000 +vn 0.6667 0.3333 -0.6667 +vn 0.6667 0.3333 0.6667 +vn -0.0000 -1.0000 -0.0000 +vn -0.6667 0.3333 0.6667 +vn -0.6667 0.3333 -0.6667 +vt 0.250000 0.490000 +vt 0.250000 0.250000 +vt 0.490000 0.250000 +vt 0.250000 0.010000 +vt 0.990000 0.250000 +vt 0.510000 0.250000 +vt 0.750000 0.490000 +vt 0.010000 0.250000 +vt 0.750000 0.010000 +s 0 +f 1/1/1 5/2/1 2/3/1 +f 2/3/2 5/2/2 3/4/2 +f 2/5/3 4/6/3 1/7/3 +f 3/4/4 5/2/4 4/8/4 +f 4/8/5 5/2/5 1/1/5 +f 2/5/3 3/9/3 4/6/3 diff --git a/src/boids.cpp b/src/boids.cpp new file mode 100644 index 0000000..f18a427 --- /dev/null +++ b/src/boids.cpp @@ -0,0 +1,102 @@ +#include + +#include "directxmath/directxmath.h" + +#include "boids.h" + +namespace boids { + + boid_forces forces; + boid_configuration configuration; + + static inline void reset_cohesion() + { + forces.cohesion.center_of_mass = XMVectorZero(); + forces.cohesion.force = XMVectorZero(); + } + + static inline void reset_alignment() + { + forces.alignment.average_velocity = XMVectorZero(); + forces.alignment.force = XMVectorZero(); + } + + static inline void reset_separation() + { + forces.separation.force = XMVectorZero(); + } + + inline constexpr bool vector_equal(XMVECTOR V1, XMVECTOR V2) + { + uint32_t CR; + XMVectorEqualR(&CR, V1, V2); + return XMComparisonAllTrue(CR); + } + + static inline XMVECTOR cohesion_force(boid const & this_boid, float const reciprocal_neighbor_count) + { + XMVECTOR center_of_mass = forces.cohesion.center_of_mass * reciprocal_neighbor_count; + XMVECTOR cohesion_vector = center_of_mass - this_boid.position; + if (vector_equal(cohesion_vector, XMVectorZero())) + return XMVectorZero(); + XMVECTOR cohesion_direction = XMVector3NormalizeEst(cohesion_vector); + return cohesion_direction * configuration.cohesion.coefficient; + } + + static inline XMVECTOR alignment_force(boid const & this_boid, float const reciprocal_neighbor_count) + { + XMVECTOR average_velocity = forces.alignment.average_velocity * reciprocal_neighbor_count; + XMVECTOR alignment_vector = average_velocity - this_boid.velocity; + if (vector_equal(alignment_vector, XMVectorZero())) + return XMVectorZero(); + XMVECTOR alignment_direction = XMVector3NormalizeEst(alignment_vector); + return alignment_direction * configuration.alignment.coefficient; + } + + boid_forces const & evaluate(boid const * const boids, int length, int boid_index) + { + forces.vision_neighbor_count = 0; + + reset_cohesion(); + reset_alignment(); + reset_separation(); + + boid const & this_boid = boids[boid_index]; + for (int i = 0; i < length; i++) { + if (i == boid_index) + continue; + + boid const & other_boid = boids[i]; + + XMVECTOR position_vector = this_boid.position - other_boid.position; + float position_length = XMVectorGetX(XMVector3LengthEst(position_vector)); + if (position_length < configuration.vision_length) { + forces.vision_neighbor_count += 1; + + // cohesion + forces.cohesion.center_of_mass += other_boid.position; + + // alignment + forces.alignment.average_velocity += other_boid.velocity; + } + + // separation + if (vector_equal(position_vector, XMVectorZero())) + continue; + if (position_length < configuration.separation.minimum_length) { + float reciprocal_position_length = 1.0f / position_length; + //float reciprocal_position_length = 1.0f; + XMVECTOR separation_direction = position_vector * reciprocal_position_length; + forces.separation.force += separation_direction * reciprocal_position_length * configuration.separation.coefficient; + } + } + + if (forces.vision_neighbor_count != 0) { + float reciprocal_vision_neighbor_count = 1.0f / forces.vision_neighbor_count; + forces.cohesion.force = cohesion_force(this_boid, reciprocal_vision_neighbor_count); + forces.alignment.force = alignment_force(this_boid, reciprocal_vision_neighbor_count); + } + + return forces; + } +} diff --git a/src/boids_scene.cpp b/src/boids_scene.cpp new file mode 100644 index 0000000..55af1d0 --- /dev/null +++ b/src/boids_scene.cpp @@ -0,0 +1,116 @@ +#include +#include + +#include "glad/gl.h" + +#include "line_art.h" +#include "boids.h" + +namespace boids_scene { + + const int boids_length = 20; + boids::boid boids[boids_length]; + + const XMFLOAT3 colors[] = { + {(float)0xb5 / 255.0, (float)0x7f / 255.0, (float)0x50 / 255.0}, + {(float)0x37 / 255.0, (float)0xff / 255.0, (float)0x8b / 255.0}, + {(float)0x51 / 255.0, (float)0xd6 / 255.0, (float)0xff / 255.0}, + {(float)0x54 / 255.0, (float)0x2e / 255.0, (float)0x71 / 255.0}, + {(float)0xff / 255.0, (float)0x84 / 255.0, (float)0x84 / 255.0}, + }; + + void update(int up, int down, int left, int right, + int w, int s, int a, int d, + int t, int g, int f, int h, + int i, int k, int j, int l) + { + float rate = 0.05f; + + float forward[4]; + float strafe[4]; + + forward[0] = (rate * up + -rate * down); + strafe[0] = (-rate * left + rate * right); + + forward[1] = (rate * w + -rate * s); + strafe[1] = (-rate * a + rate * d); + + forward[2] = (rate * t + -rate * g); + strafe[2] = (-rate * f + rate * h); + + forward[3] = (rate * i + -rate * k); + strafe[3] = (-rate * j + rate * l); + + //for (int i = 0; i < 4; i++) + //point_position[i] = XMVector3Transform(point_position[i], XMMatrixTranslation(strafe[i], forward[i], 0)); + } + + static inline float random() + { + float r = 1.0f / (float)RAND_MAX; + return (float)rand() * r * 2.0f - 1.0f; + } + + void load() + { + srand(0x12345678); + + for (int i = 0; i < boids_length; i++) { + boids[i].position = XMVectorSet(random() * 5, random() * 5, 0, 0); + boids[i].velocity = XMVectorSet(random(), random(), 0, 0); + } + + + boids::configuration.vision_length = 2.0f; + boids::configuration.cohesion.coefficient = 0.001f; + boids::configuration.alignment.coefficient = 0.01f; + boids::configuration.separation.minimum_length = 0.7f; + boids::configuration.separation.coefficient = 0.5f; + boids::configuration.bounds.coefficient = 0.02f; + } + + void draw() + { + XMMATRIX transform = line_art::view() * line_art::projection(); + + line_art::scene_start(transform); + + glLineWidth(1.0f); + + line_art::set_colorv(colors[4]); + for (int i = 0; i < boids_length; i++) + line_art::draw_sphere(transform, boids[i].position, 0.3); + + for (int i = 0; i < boids_length; i++) { + boids::boid_forces const & forces = evaluate(boids, boids_length, i); + + glLineWidth(4.0f); + line_art::set_colorv(colors[0]); + line_art::draw_line(transform, boids[i].position, boids[i].position + forces.cohesion.force); + line_art::set_colorv(colors[1]); + line_art::draw_line(transform, boids[i].position, boids[i].position + forces.alignment.force); + line_art::set_colorv(colors[2]); + line_art::draw_line(transform, boids[i].position, boids[i].position + forces.separation.force); + + boids[i].velocity += forces.cohesion.force + forces.alignment.force + forces.separation.force; + const float speed_limit = 2.0; + if (XMVectorGetX(XMVector3LengthEst(boids[i].velocity)) > speed_limit) { + boids[i].velocity = XMVector3NormalizeEst(boids[i].velocity) * speed_limit; + } + + if (XMVectorGetX(boids[i].position) > 5.0) + boids[i].velocity += XMVectorSetX(XMVectorZero(), -boids::configuration.bounds.coefficient); + + if (XMVectorGetX(boids[i].position) < -5.0) + boids[i].velocity += XMVectorSetX(XMVectorZero(), boids::configuration.bounds.coefficient); + + if (XMVectorGetY(boids[i].position) > 5.0) + boids[i].velocity += XMVectorSetY(XMVectorZero(), -boids::configuration.bounds.coefficient); + + if (XMVectorGetY(boids[i].position) < -5.0) + boids[i].velocity += XMVectorSetY(XMVectorZero(), boids::configuration.bounds.coefficient); + + boids[i].position += boids[i].velocity * 0.05f; + } + } +} diff --git a/src/line_art.cpp b/src/line_art.cpp index 0278692..548c9cb 100644 --- a/src/line_art.cpp +++ b/src/line_art.cpp @@ -169,7 +169,7 @@ namespace line_art { XMMATRIX projection() { - return XMMatrixOrthographicRH(10, 10, 0, 10); + return XMMatrixOrthographicRH(20, 20, 0, 10); } void set_transform(XMMATRIX const & transform) @@ -227,6 +227,18 @@ namespace line_art { glDrawElementsBaseVertex(GL_LINE_STRIP, 5, GL_UNSIGNED_SHORT, (void*)(cube_base_index), cube_base_vertex); } + void draw_grid(XMMATRIX const & transform) + { + // grid + glLineWidth(1.0f); + set_transform(transform); + glUniform3f(location.uniform.base_color, 0, 1, 0); + glUniform1i(location.uniform.use_grid_transform, 1); + glDrawArraysInstanced(GL_LINES, 0, 4, 7); + + glUniform1i(location.uniform.use_grid_transform, 0); + } + void scene_start(XMMATRIX const & transform) { glUseProgram(program); @@ -238,19 +250,15 @@ namespace line_art { glBindVertexArray(vertex_array_object); glBindVertexBuffer(0, per_vertex_buffer, 0, per_vertex_size); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer); - - // grid - glLineWidth(1.0f); - set_transform(transform); - glUniform3f(location.uniform.base_color, 0, 1, 0); - glUniform1i(location.uniform.use_grid_transform, 1); - glDrawArraysInstanced(GL_LINES, 0, 4, 7); - - glUniform1i(location.uniform.use_grid_transform, 0); } void set_color(float r, float g, float b) { glUniform3f(location.uniform.base_color, r, g, b); } + + void set_colorv(XMFLOAT3 const & value) + { + glUniform3fv(location.uniform.base_color, 1, (float const *)&value); + } } diff --git a/src/test.cpp b/src/test.cpp index 5950570..79142a4 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -20,6 +20,7 @@ #include "line_art.h" #include "collision_scene.h" #include "collision.h" +#include "boids_scene.h" struct line_location { struct { @@ -288,6 +289,7 @@ void load(const char * source_path) line_art::load(); collision_scene::load(); + boids_scene::load(); } void update_keyboard(int up, int down, int left, int right, @@ -298,10 +300,16 @@ void update_keyboard(int up, int down, int left, int right, //float forward = (0.1f * up + -0.1f * down); //float strafe = (-0.1f * left + 0.1f * right); //view::third_person::apply_translation(forward, strafe, 0); + /* collision_scene::update(up, down, left, right, w, s, a, d, t, g, f, h, i, k, j, l); + */ + boids_scene::update(up, down, left, right, + w, s, a, d, + t, g, f, h, + i, k, j, l); } void check_collisions(collision::Sphere const & sphere, XMVECTOR const & direction, @@ -560,7 +568,8 @@ void draw() } else { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - collision_scene::draw(); + //collision_scene::draw(); + boids_scene::draw(); } last_frame_time = current_time;