164 lines
4.7 KiB
C++
164 lines
4.7 KiB
C++
#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;
|
||
}
|