--- /dev/null
+clang
+%c -std=c++2a
+%cpp -std=c++2a
+-I./include
u32 count;
u32 capacity;
T *data;
-
+
Array() { this->init(); }
-
+
// NOTE(Brendan): This should only be called when the Array is being initialized for the first time. If it is called a second time, it will leak memory. I would add a check for that, but that defeats the whole point of initialization.
void init()
{
capacity = 0;
data = nullptr;
}
-
+
T& operator[](i32 elem)
{
#if defined(DEBUG)
#endif
return data[elem];
}
-
+
const T& operator[](i32 elem) const { return data[elem]; }
-
+
bool ensure_capacity(u32 min_capacity)
{
if (min_capacity <= capacity) return true;
-
+
u32 new_capacity = 0;
if (data != nullptr)
{
new_capacity = capacity;
while (new_capacity < min_capacity) new_capacity <<= 1;
-
+
data = (T*) realloc(data, new_capacity * sizeof(T));
}
else
new_capacity = min_capacity;
data = (T*) malloc(new_capacity * sizeof(T));
}
-
+
if (data == nullptr) return false;
-
+
capacity = new_capacity;
return true;
}
-
+
void push(const T& val)
{
if (!ensure_capacity(count + 1)) return;
data[count++] = val;
}
-
+
T pop()
{
return data[--count];
}
-
+
void clear()
{
count = 0;
}
-
+
void insert(u32 idx, const T& x)
{
if (!ensure_capacity(count + 1)) return;
-
+
count += 1;
for (u32 i = count; i > idx; i--)
data[i] = data[i - 1];
-
+
data[idx] = x;
}
-
+
void remove(const T& elem)
{
auto move = 0;
-
+
for (u32 i = 0; i < count - move; i++)
{
if (data[i + move] == elem) move += 1;
if (move != 0) data[i] = data[i + move];
}
-
+
count -= move;
}
-
+
void delete_at(u32 idx)
{
if (idx >= count) return;
-
+
for (u32 i = idx; i < count - 1; i++)
data[i] = data[i + 1];
-
+
count -= 1;
}
-
+
void fast_delete(u32 idx)
{
if (idx >= count) return;
-
+
data[idx] = data[count - 1];
count -= 1;
}
-
+
bool contains(const T& x) const
{
for (u32 i = 0; i < count; i++)
{
if (data[i] == x) return true;
}
-
+
return false;
}
-
+
u64 size_in_bytes() const
{
return sizeof(T) * count;
}
-
+
T* begin() const
{
return &data[0];
}
-
+
T* end() const
{
return &data[count];
uniform mat4 u_proj;
void main() {
- gl_Position = u_proj * vec4(a_pos_top_left * a_interp + a_pos_bottom_right * (vec2(1.0, 1.0) - a_interp), 0, 1);
+ gl_Position = u_proj * vec4(mix(a_pos_top_left, a_pos_bottom_right, a_interp), 0, 1);
- v_texcoord = a_texcoord_top_left * a_interp + a_texcoord_bottom_right * (vec2(1.0, 1.0) - a_interp);
+ v_texcoord = mix(a_texcoord_top_left, a_texcoord_bottom_right, a_interp);
}
\ No newline at end of file
persist const f32 repulsion_force = 100.0f; // :ArbitraryConstant
return repulsion_force * (1 - d) / d;
}
-
+
if (1 <= d && d < br.distance_range + 1)
{
f32 tmp_x = d - (br.distance_range / 2) - 1;
-
+
return (-4 * br.max_force / (br.distance_range * br.distance_range))
* (tmp_x * tmp_x)
+ br.max_force;
}
-
+
return 0;
}
// HACK(Brendan): There should be a way to not have to move the body to test collisions with it.
body->pos += d;
defer { body->pos -= d; };
-
+
For (other_bodies)
{
if (body == &it) continue;
if (bodies_collide(body, &it)) return false;
}
-
+
return true;
}
body_relations[i][j].distance_range = randf(4.0f, 7.0f);
}
}
-
+
other_bodies.init();
other_bodies.ensure_capacity(1024);
}
body_accumulate_move(Body* body, QuadTree<Body>* qt_bodies, f64 dt)
{
V2f force = { 0.0f, 0.0f };
-
+
other_bodies.clear();
qt_bodies->query(AABB { body->pos.x - 300, body->pos.y - 300, 600, 600 }, &other_bodies); // :ArbitraryConstant
-
+
For (other_bodies)
{
if (body == it) continue;
-
+
auto norm_dir = body->pos - it->pos;
auto d = v2f_mag(norm_dir) / 20.0f; // :ArbitraryConstant
norm_dir = v2f_norm(norm_dir);
-
+
auto br = body_relations[static_cast<i32>(body->body_type)][static_cast<i32>(it->body_type)];
f32 force_mag = get_force_magnitude_at_distance(br, d);
-
+
force += norm_dir * force_mag;
}
-
+
force += body->vel * -6.f; // :ArbitraryConstant
-
+
body->acc = force * (1.0f / body->mass);
body->vel += body->acc * dt;
}
body_apply_move(Body* body, f64 dt)
{
body->pos += body->vel * dt;
-}
\ No newline at end of file
+}
init_glfw()
{
logprint(LOG_LEVEL_INFO, "Initializing GLFW");
-
+
if (!glfwInit()) panic_and_die("Failed to initalize GLFW.");
glfwSetErrorCallback(glfw_error_handler);
-
+
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwMakeContextCurrent(window);
-
+
glfwSwapInterval(1);
glfwSetKeyCallback(window, glfw_key_handler);
glfwSetFramebufferSizeCallback(window, glfw_resize_handler);
-
+
// NOTE(Brendan): This may need to be changed if the screen orientation changes.
glEnable(GL_CULL_FACE);
glFrontFace(GL_CW);
glCullFace(GL_BACK);
-
+
glEnable(GL_TEXTURE_2D);
-
+
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
create_circle_mesh()
{
logprint(LOG_LEVEL_INFO, "Generating circle mesh");
-
+
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
-
+
V2f circle_points[CIRCLE_POINT_COUNT] = {};
foreach (i, 0, CIRCLE_POINT_COUNT)
{
circle_points[i].x = cos(t * 2 * PI);
circle_points[i].y = sin(t * 2 * PI);
}
-
+
GLuint vertex_buffer;
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(V2f), (void *) offsetof(V2f, x));
glBindBuffer(GL_ARRAY_BUFFER, -1);
-
+
u8 circle_indicies[CIRCLE_POINT_COUNT] = {};
foreach(i, 0, CIRCLE_POINT_COUNT) circle_indicies[i] = i;
-
+
GLuint index_buffer;
glGenBuffers(1, &index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(circle_indicies), &circle_indicies, GL_STATIC_DRAW);
-
+
glBindVertexArray(-1);
-
+
return vao;
}
// NOTE(Brendan): Need to initialize the array since it does not get constructed because I refuse to use the 'new' keyword. alloc<T> uses malloc under the hood and cannot initialize the result.
state->bodies.init();
state->bodies.ensure_capacity(PARTICLE_COUNT);
-
+
foreach (i, 0, PARTICLE_COUNT)
{
Body tmp_body;
tmp_body.body_type = static_cast<BodyType> ((rand() % 4));
state->bodies.push(tmp_body);
}
-
+
state->qt_body_allocator.init(PARTICLE_COUNT);
}
update(SimState* state, f64 dt)
{
persist const f64 step = 0.01;
-
+
state->qt_bodies.init(AABB { -(f32) window_width, -(f32)window_height, (f32) window_width * 2, (f32) window_height * 2 });
state->qt_body_allocator.reset();
For (state->bodies) state->qt_bodies.insert(&it, &state->qt_body_allocator);
-
+
For (state->bodies) body_accumulate_move(&it, &state->qt_bodies, step);
For (state->bodies) body_apply_move(&it, step);
}
glBindBuffer(GL_ARRAY_BUFFER, body_buffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, state->bodies.size_in_bytes(), state->bodies.data);
glBindBuffer(GL_ARRAY_BUFFER, -1);
-
+
glUseProgram(body_program);
-
+
mat4 ortho_mat;
mat4_ortho(&ortho_mat, 0, window_width, 0, window_height, 0.0f, 100.0f);
GLuint ortho_mat_loc = glGetUniformLocation(body_program, "u_proj");
glUniformMatrix4fv(ortho_mat_loc, 1, false, (f32 *) ortho_mat);
glViewport(0, 0, window_width, window_height);
-
+
// NOTE(Brendan): Clear the screen.
glClearColor(0.1, 0.1, 0.1, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
+
// NOTE(Brendan): Draw the bodies.
glBindVertexArray(circle_mesh);
glDrawElementsInstanced(GL_TRIANGLE_FAN, CIRCLE_POINT_COUNT, GL_UNSIGNED_BYTE, 0, state->bodies.count);
glBindVertexArray(-1);
-
+
font_set_projection_matrix(ortho_mat);
char fps_str[64];
snprintf(fps_str, 63, "FPS: %d", frame_rate);
font_print(0, 32, fps_str);
-
+
// NOTE(Brendan): Present the changes to the screen.
glfwSwapBuffers(window);
}
f64 last_time = glfwGetTime();
f64 curr_time = last_time;
f64 delta = 0.0;
-
+
f64 frame_delta = 0.0;
i32 frame_count = 0;
clock_t total_clock_delta = 0;
-
+
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
-
+
curr_time = glfwGetTime();
delta = curr_time - last_time;
last_time = curr_time;
-
+
clock_t before_update = clock();
update(state, delta);
clock_t after_update = clock();
total_clock_delta += (after_update - before_update);
-
+
frame_count += 1;
draw(state);
-
+
frame_delta += delta;
if (frame_delta >= 1.0)
{
// logprint(LOG_LEVEL_INFO, "AVG UPD CLK: %10.6f", (f64) (total_clock_delta / frame_count) / CLOCKS_PER_SEC);
-
+
total_clock_delta = 0;
frame_delta -= 1.0;
frame_rate = frame_count;
- logprint(LOG_LEVEL_INFO, "FPS: %d", frame_count);
frame_count = 0;
}
}
main(i32 argc, char* argv[])
{
srand(time(NULL));
-
+
init_glfw();
defer { deinit_glfw(); };
-
+
circle_mesh = create_circle_mesh();
-
+
init_font("res/font/Hack-Regular.ttf");
-
+
body_program = create_program(load_shader(GL_VERTEX_SHADER, "res/shaders/planet_vert.glsl"),
load_shader(GL_FRAGMENT_SHADER, "res/shaders/planet_frag.glsl"));
glUseProgram(body_program);
-
+
// :ArbitraryConstant
persist GLfloat planet_colors[4][4] =
{
};
GLuint planet_colors_loc = glGetUniformLocation(body_program, "u_planet_colors");
glUniform4fv(planet_colors_loc, 4, (GLfloat *) planet_colors);
-
+
auto state = alloc<SimState>();
sim_state_init(state);
-
+
{
glBindVertexArray(circle_mesh);
defer { glBindVertexArray(-1); };
-
+
glGenBuffers(1, &body_buffer);
glBindBuffer(GL_ARRAY_BUFFER, body_buffer);
glBufferData(GL_ARRAY_BUFFER, state->bodies.size_in_bytes(), state->bodies.data, GL_STREAM_DRAW);
-
+
foreach (i, 1, 4)
{
glEnableVertexAttribArray(i);
glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(Body), (void *) offsetof(Body, pos.x));
glVertexAttribPointer(2, 1, GL_FLOAT, false, sizeof(Body), (void *) offsetof(Body, mass));
glVertexAttribPointer(3, 1, GL_BYTE, false, sizeof(Body), (void *) offsetof(Body, body_type));
-
+
glBindBuffer(GL_ARRAY_BUFFER, -1);
}
-
+
nocheckin_init_other_bodies();
loop(state);
-
+
return 0;
}
{
FILE* font_file = fopen(font_loc, "rb");
if (font_file == NULL) panic_and_die("Font file not found: %s\n", font_loc);
-
+
fseek(font_file, 0, SEEK_END);
i32 font_file_size = ftell(font_file);
fseek(font_file, 0, SEEK_SET);
-
+
auto font_buffer = alloc<unsigned char>(font_file_size + 1);
fread(font_buffer, 1, font_file_size, font_file);
fclose(font_file);
-
+
auto font_pixels = alloc<u8>(FONT_INTERNAL_IMAGE_SIZE * FONT_INTERNAL_IMAGE_SIZE);
-
+
font_char_data = alloc<stbtt_packedchar>(FONT_CHAR_COUNT);
-
+
stbtt_pack_context spc;
stbtt_PackBegin(&spc, font_pixels, FONT_INTERNAL_IMAGE_SIZE, FONT_INTERNAL_IMAGE_SIZE, 0, 1, nullptr);
stbtt_PackFontRange(&spc, font_buffer, 0, 32, FONT_FIRST_CHAR, FONT_CHAR_COUNT, font_char_data);
stbtt_PackEnd(&spc);
-
+
free(font_buffer);
-
+
logprint(LOG_LEVEL_INFO, "Loaded font rasterization into bitmap.");
-
+
glGenTextures(1, &font_tex);
glBindTexture(GL_TEXTURE_2D, font_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, FONT_INTERNAL_IMAGE_SIZE, FONT_INTERNAL_IMAGE_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, font_pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
+
free(font_pixels);
-
+
logprint(LOG_LEVEL_INFO, "Mapped font bitmap to OpenGL texture.");
-
+
font_program = create_program(load_shader(GL_VERTEX_SHADER, "res/shaders/font_vert.glsl"),
load_shader(GL_FRAGMENT_SHADER, "res/shaders/font_frag.glsl"));
-
+
glUseProgram(font_program);
-
+
font_proj_mat_loc = glGetUniformLocation(font_program, "u_proj");
font_color_loc = glGetUniformLocation(font_program, "u_color");
-
+
auto font_texture_loc = glGetUniformLocation(font_program, "u_texture");
glUniform1i(font_texture_loc, 0);
-
+
glGenVertexArrays(1, &font_vao);
glBindVertexArray(font_vao);
-
+
GLuint font_interp_buffer;
glGenBuffers(1, &font_interp_buffer);
glBindBuffer(GL_ARRAY_BUFFER, font_interp_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(font_interp_data), font_interp_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(V2f), (void *) 0);
-
+
GLuint font_index_buffer;
glGenBuffers(1, &font_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_index_buffer);
u8 font_index_data[6] = { 0, 1, 2, 0, 2, 3 };
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(font_index_data), font_index_data, GL_STATIC_DRAW);
-
+
glGenBuffers(1, &font_buffer_data);
glBindBuffer(GL_ARRAY_BUFFER, font_buffer_data);
glBufferData(GL_ARRAY_BUFFER, FONT_BUFFER_SIZE, nullptr, GL_STREAM_DRAW);
-
+
foreach (i, 1, 5)
{
glEnableVertexAttribArray(i);
glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(stbtt_aligned_quad), (void *) offsetof(stbtt_aligned_quad, x1));
glVertexAttribPointer(3, 2, GL_FLOAT, false, sizeof(stbtt_aligned_quad), (void *) offsetof(stbtt_aligned_quad, s0));
glVertexAttribPointer(4, 2, GL_FLOAT, false, sizeof(stbtt_aligned_quad), (void *) offsetof(stbtt_aligned_quad, s1));
-
+
glBindBuffer(GL_ARRAY_BUFFER, -1);
-
+
glBindVertexArray(-1);
}
i32 msg_len = strlen(msg);
stbtt_aligned_quad* quads = (stbtt_aligned_quad *) alloca(msg_len * sizeof(stbtt_aligned_quad));
i32 quad_num = 0;
-
+
while (*msg)
{
stbtt_GetPackedQuad(font_char_data,
FONT_INTERNAL_IMAGE_SIZE, FONT_INTERNAL_IMAGE_SIZE,
*msg - FONT_FIRST_CHAR,
&x, &y, &quads[quad_num], 0);
-
+
msg++;
quad_num++;
}
-
+
glBindBuffer(GL_ARRAY_BUFFER, font_buffer_data);
glBufferSubData(GL_ARRAY_BUFFER, 0, msg_len * sizeof(stbtt_aligned_quad), quads);
glBindBuffer(GL_ARRAY_BUFFER, -1);
-
+
glBindTexture(GL_TEXTURE_2D, font_tex);
glUseProgram(font_program);
glUniform4f(font_color_loc, r, g, b, a);
glBindVertexArray(font_vao);
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (void *) 0, msg_len);
glBindVertexArray(-1);
-}
\ No newline at end of file
+}