boids: initial

This commit is contained in:
Zack Buhman 2026-03-16 00:22:52 -05:00
parent 04c265e253
commit 7f01fd0483
10 changed files with 348 additions and 12 deletions

View File

@ -33,7 +33,9 @@ OBJS = \
src/hud.o \ src/hud.o \
src/lighting.o \ src/lighting.o \
src/collision_scene.o \ src/collision_scene.o \
src/line_art.o src/line_art.o \
src/boids.o \
src/boids_scene.o
all: test.so all: test.so

53
include/boids.h Normal file
View File

@ -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);
}

12
include/boids_scene.h Normal file
View File

@ -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();
}

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "directxmath/directxmath.h"
namespace line_art { namespace line_art {
void load(); void load();
@ -14,5 +16,7 @@ namespace line_art {
void draw_capsule(XMMATRIX const & transform, XMVECTOR a, XMVECTOR b, float radius); void draw_capsule(XMMATRIX const & transform, XMVECTOR a, XMVECTOR b, float radius);
void draw_cube(XMMATRIX const & transform, XMVECTOR const & position); void draw_cube(XMMATRIX const & transform, XMVECTOR const & position);
void scene_start(XMMATRIX const & transform); void scene_start(XMMATRIX const & transform);
void draw_grid(XMMATRIX const & transform);
void set_color(float r, float g, float b); void set_color(float r, float g, float b);
void set_colorv(XMFLOAT3 const & value);
} }

View File

@ -120,6 +120,7 @@ function love.run()
love.graphics.present() love.graphics.present()
love.timer.sleep(0.001) love.timer.sleep(0.001)
--love.timer.sleep(0.1)
--local fps = love.timer.getFPS( ) --local fps = love.timer.getFPS( )
--print(fps) --print(fps)
end end

29
minecraft/gen/arrow.obj Normal file
View File

@ -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

102
src/boids.cpp Normal file
View File

@ -0,0 +1,102 @@
#include <stdio.h>
#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;
}
}

116
src/boids_scene.cpp Normal file
View File

@ -0,0 +1,116 @@
#include <stdio.h>
#include <stdlib.h>
#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;
}
}
}

View File

@ -169,7 +169,7 @@ namespace line_art {
XMMATRIX projection() XMMATRIX projection()
{ {
return XMMatrixOrthographicRH(10, 10, 0, 10); return XMMatrixOrthographicRH(20, 20, 0, 10);
} }
void set_transform(XMMATRIX const & transform) 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); 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) void scene_start(XMMATRIX const & transform)
{ {
glUseProgram(program); glUseProgram(program);
@ -238,19 +250,15 @@ namespace line_art {
glBindVertexArray(vertex_array_object); glBindVertexArray(vertex_array_object);
glBindVertexBuffer(0, per_vertex_buffer, 0, per_vertex_size); glBindVertexBuffer(0, per_vertex_buffer, 0, per_vertex_size);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer); 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) void set_color(float r, float g, float b)
{ {
glUniform3f(location.uniform.base_color, r, g, b); glUniform3f(location.uniform.base_color, r, g, b);
} }
void set_colorv(XMFLOAT3 const & value)
{
glUniform3fv(location.uniform.base_color, 1, (float const *)&value);
}
} }

View File

@ -20,6 +20,7 @@
#include "line_art.h" #include "line_art.h"
#include "collision_scene.h" #include "collision_scene.h"
#include "collision.h" #include "collision.h"
#include "boids_scene.h"
struct line_location { struct line_location {
struct { struct {
@ -288,6 +289,7 @@ void load(const char * source_path)
line_art::load(); line_art::load();
collision_scene::load(); collision_scene::load();
boids_scene::load();
} }
void update_keyboard(int up, int down, int left, int right, 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 forward = (0.1f * up + -0.1f * down);
//float strafe = (-0.1f * left + 0.1f * right); //float strafe = (-0.1f * left + 0.1f * right);
//view::third_person::apply_translation(forward, strafe, 0); //view::third_person::apply_translation(forward, strafe, 0);
/*
collision_scene::update(up, down, left, right, collision_scene::update(up, down, left, right,
w, s, a, d, w, s, a, d,
t, g, f, h, t, g, f, h,
i, k, j, l); 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, void check_collisions(collision::Sphere const & sphere, XMVECTOR const & direction,
@ -560,7 +568,8 @@ void draw()
} else { } else {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
collision_scene::draw(); //collision_scene::draw();
boids_scene::draw();
} }
last_frame_time = current_time; last_frame_time = current_time;