AfterBurner/src/world.cpp

164 lines
4.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "afterburner.h"
#include <stdlib.h>
int debug = 0;
void *debug2 = NULL;
struct world *world_make(int width, int height, struct ref_point *points,
int count)
{
if(!points)
return NULL;
struct world *w = (struct world *)malloc(sizeof *w);
int *js = NULL;
size_t s;
if(!w) goto fail;
w->w = width;
w->h = height;
w->chunks_x = (width + CHUNK_SIZE - 1) / CHUNK_SIZE;
w->chunks_y = (height + CHUNK_SIZE - 1) / CHUNK_SIZE;
s = w->chunks_x * w->chunks_y * sizeof *w->chunks;
w->chunks = (struct chunk *)malloc(s);
if(!w->chunks) goto fail;
for(int i = 0; i < w->chunks_x * w->chunks_y; i++) {
w->chunks[i].points = NULL;
w->chunks[i].count = 0;
}
/* Do a first pass to count the number of points in each chunks */
for(int i = 0; i < count; i++) {
int cx = (int)points[i].pos.x / CHUNK_SIZE;
int cy = (int)points[i].pos.y / CHUNK_SIZE;
w->chunks[cy * w->chunks_x + cx].count++;
}
/* Allocate all chunks' data */
for(int i = 0; i < w->chunks_x * w->chunks_y; i++) {
if(w->chunks[i].count == 0)
continue;
w->chunks[i].points = (struct ref_point *)malloc(
w->chunks[i].count * sizeof(struct ref_point));
if(!w->chunks[i].points) goto fail;
}
/* Do a second pass to actually fill the chunks */
s = w->chunks_x * w->chunks_y * sizeof(int);
js = (int *)malloc(s);
if(!js) goto fail;
for(int i = 0; i < w->chunks_x * w->chunks_y; i++) {
js[i] = 0;
}
for(int i = 0; i < count; i++) {
int cx = (int)points[i].pos.x / CHUNK_SIZE;
int cy = (int)points[i].pos.y / CHUNK_SIZE;
int j = cy * w->chunks_x + cx;
w->chunks[j].points[js[j]++] = points[i];
}
free(js);
return w;
fail:
world_destroy(w);
return NULL;
}
void world_destroy(struct world *w)
{
if(!w) return;
if(w->chunks) {
for(int i = 0; i < w->chunks_x * w->chunks_y; i++)
free(w->chunks[i].points);
free(w->chunks);
}
free(w);
}
struct ref_point *world_select(struct world const *world, vec3 position,
GUNUSED vec3 forward, int *size, int extra_size)
{
int cx1 = (int)position.x / CHUNK_SIZE;
int cy1 = (int)position.y / CHUNK_SIZE;
// int fx = (int)forward.x >= 0 ? +1 : -1;
// int fy = (int)forward.y >= 0 ? +1 : -1;
int chunks[9];
int chunk_count = 0;
for(int dy = -1; dy <= +1; dy++)
for(int dx = -1; dx <= +1; dx++) {
int cy = cy1 + dy;
int cx = cx1 + dx;
if(cx < 0)
cx = world->chunks_x - 1;
if(cx >= world->chunks_x)
cx = 0;
if(cy < 0)
cy = world->chunks_y - 1;
if(cy >= world->chunks_y)
cy = 0;
chunks[chunk_count++] = cy * world->chunks_x + cx;
}
/* Count the points to be allocated */
int total_point_count = 0;
for(int i = 0; i < chunk_count; i++)
total_point_count += world->chunks[chunks[i]].count;
/* Allocate the array */
struct ref_point *output = (struct ref_point *)malloc(
(total_point_count + extra_size) * sizeof *output);
/* Fill the points in selected chunks. While doing this, we also rotate the
points. The player is constrained to be within the world boundary, but
it can still be on the edge and look outside. In this situation, we
rotate the points to give the illusion that the world wraps around. */
num world_size_x14 = num(world->w / 4);
num world_size_x34 = num(3 * world->w / 4);
num world_size_y14 = num(world->h / 4);
num world_size_y34 = num(3 * world->h / 4);
/* This simple test only works if the map is at least 4x as large as
the depth of view of the plane, (here 256 > 4×50). */
num wrap_x_minus = (position.x < world_size_x14)
? world_size_x34 : num(world->w);
num wrap_x_plus = (position.x > world_size_x34)
? world_size_x14 : num(0);
num wrap_y_minus = (position.y < world_size_y14)
? world_size_y34 : num(world->h);
num wrap_y_plus = (position.y > world_size_y34)
? world_size_y14 : num(0);
int k = 0;
for(int i = 0; i < chunk_count; i++) {
struct chunk *c = &world->chunks[chunks[i]];
for(int j = 0; j < c->count; j++) {
struct ref_point p = c->points[j];
if(p.pos.x < wrap_x_plus)
p.pos.x += num(world->w);
else if(p.pos.x > wrap_x_minus)
p.pos.x -= num(world->w);
if(p.pos.y < wrap_y_plus)
p.pos.y += num(world->h);
else if(p.pos.y > wrap_y_minus)
p.pos.y -= num(world->h);
output[k++] = p;
}
}
*size = total_point_count;
return output;
}