Archived
1
0
Fork 0
This repository has been archived on 2025-09-02. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
skcblitz/world.h

565 lines
19 KiB
C
Raw Permalink Normal View History

2025-09-02 14:43:36 +02:00
uint map_f_chunkhash(void *ptr) {
ulong hash = (ulong)ptr;
hash = (hash & 0x3f) | (((hash >> 21) & 0x0f) << 12) | (((hash >> 42) & 0x3f) << 6);
return (uint)hash;
}
void world_init(window_t *win, double x, double y, double z, float yaw, float pitch) {
world_t *world = win->world = mem_zeros(sizeof(world_t), MEM_WORLD);
tbl_init(&world->global_geom, 4, 32, sizeof(geometry_t*), MEM_OBJECT);
world->obj_id = 0ULL;
// world->size_x = x;
// world->size_y = y;
// world->size_z = z;
// world->clamp_xz = (clamp & WORLD_CLAMP_XZ) ? 1 : 0;
// world->clamp_y = (clamp & WORLD_CLAMP_Y) ? 1 : 0;
world->n_chunks = world->n_objects = world->n_lights = world->s_lights = 0;
map_init(&world->chunk_map, map_f_chunkhash, map_f_inteq, 32, MEM_WORLD);
// world->chunks = mem_alloc(sizeof(chunk_t*) * x * y * z, MEM_WORLD);
// memset(world->chunks, 0, sizeof(chunk_t*) * x * y * z);
tbl_init(&world->chunk_table, 16384, 32, sizeof(chunk_t), MEM_CHUNK);
map_init(&world->obj_map, map_f_inthash, map_f_inteq, 32, MEM_OBJMAP);
world->light_dirty = world->chunks_dirty = 0;
world->dirty_first = world->dirty_queue = NULL;
world->gravity = 0.8;
world->friction = 0.8;
world->entity = &world->entity_base;
ent_init(world->entity, world, x, y, z, yaw, pitch);
cam_init(&world->camera, x, y + world->entity->eye, z, yaw, pitch);
sys_assert(world->post_buf = fb_init(&world->fb_post, &world->fbtex_post, &world->rbo_post, win, 0));
glCreateBuffers(1, &world->light_buf);
glNamedBufferData(world->light_buf, LIGHT_SSIZE * gdr.light_max, NULL, GL_DYNAMIC_DRAW);
}
void world_unload(world_t *world);
void world_delete(window_t *win) {
world_t *world = win->world;
// logd("t", "%d", world->obj_map.stored);
world_unload(world);
// chunk_delete(&world->global, 0);
// mem_free(world->chunks);
// tbl_clear(&world->chunk_table);
// map_clear(&world->obj_map);
glDeleteBuffers(1, &world->light_buf);
fb_remove(world->post_buf);
mem_free(world);
}
void chunk_dirty(chunk_t *chunk) {
if(!(chunk->dirty)) {
if(chunk->world->dirty_first)
chunk->world->dirty_queue->next_dirty = chunk;
else
chunk->world->dirty_first = chunk;
chunk->world->dirty_queue = chunk;
chunk->next_dirty = NULL;
chunk->world->chunks_dirty = 1;
chunk->dirty = 1;
}
}
chunk_t *chunk_get(world_t *world, int x, int y, int z, byte load) {
ulong id = CHUNK_ID(x, y, z);
// if(CHUNK_OUT(world, x, y, z))
// return &world->global;
// int offset = CHUNK_STRIDE(world, x, y, z);
chunk_t *chunk = map_get(&world->chunk_map, (void*)id);
if(chunk)
return chunk;
else if(!load)
return NULL;
sys_assert(chunk = (chunk_t*)tbl_push(&world->chunk_table));
chunk->x = x;
chunk->y = y;
chunk->z = z;
chunk->drawlist = NULL;
chunk->dirty = 0;
chunk->size = 1;
chunk->world = world;
tbl_init(&chunk->obj_table, 4, 32, sizeof(geometry_t), MEM_OBJECT);
tbl_init(&chunk->light_table, 2, 32, sizeof(light_t), MEM_LIGHTS);
lst_init(&chunk->poly_list, CHUNK_POLY_GROW, MEM_POLY);
chunk->poly_pool = mem_alloc(CHUNK_POLY_GROW * sizeof(polygon_t), MEM_POLY);
AABB_SET(chunk->box, (double)(int)(x * CHUNK_SIZE), (double)(int)(y * CHUNK_SIZE), (double)(int)(z * CHUNK_SIZE),
(double)(int)((x + 1) * CHUNK_SIZE), (double)(int)((y + 1) * CHUNK_SIZE), (double)(int)((z + 1) * CHUNK_SIZE))
map_put(&world->chunk_map, (void*)id, chunk);
world->n_chunks += 1;
return chunk;
}
void chunk_resize(chunk_t *chunk, int size) {
// table_t obj_table;
list_t poly_list;
polygon_t *poly_pool;
ulong pos = 0;
uint id;
geometry_t *geom;
// geometry_t *ngeom;
polygon_t *poly;
polygon_t *npoly;
// tbl_init(&obj_table, size * wld.chunk_cap / 32, 32, sizeof(geometry_t), MEM_OBJECT);
lst_init(&poly_list, size * CHUNK_POLY_GROW, MEM_POLY);
poly_pool = mem_alloc(size * CHUNK_POLY_GROW * sizeof(polygon_t), MEM_POLY);
// logd("TST", "chunk @ %d %d %d -> %d %d ---> %d %d", chunk->x, chunk->y, chunk->z, chunk->obj_table.size, chunk->poly_list.size, obj_table.size, poly_list.size);
while(geom = tbl_iter(&chunk->obj_table, &pos)) {
// ngeom = (geometry_t*)tbl_push(&obj_table);
// memcpy(ngeom, geom, sizeof(geometry_t));
// sys_assert(!map_put(&chunk->world->obj_map, (void*)geom->id, ngeom))
poly = &chunk->poly_pool[geom->polys];
geom->polys = 0xffffffff;
while(poly) {
id = lst_push(&poly_list);
if(geom->polys != 0xffffffff) {
npoly->next = id;
}
else {
geom->polys = id;
}
npoly = &poly_pool[id];
memcpy(npoly, poly, sizeof(polygon_t));
// npoly->id = id;
poly = (poly->next == 0xffffffff) ? NULL : &chunk->poly_pool[poly->next];
}
npoly->next = 0xffffffff;
}
// tbl_clear(&chunk->obj_table);
lst_free(&chunk->poly_list);
mem_free(chunk->poly_pool);
// memcpy(&chunk->obj_table, &obj_table, sizeof(table_t));
memcpy(&chunk->poly_list, &poly_list, sizeof(list_t));
chunk->poly_pool = poly_pool;
chunk->size = size;
}
// void chunk_resize_light(chunk_t *chunk, int size) {
// table_t light_table;
// ulong pos = 0;
// light_t *light;
// light_t *nlight;
// tbl_init(&light_table, size * wld.chunk_light / 32, 32, sizeof(light_t), MEM_LIGHTS);
// logd("TST", "chunk @ %d %d %d -> %d ---> %d", chunk->x, chunk->y, chunk->z, chunk->light_table.size, light_table.size);
// while(light = tbl_iter(&chunk->light_table, &pos)) {
// nlight = (light_t*)tbl_push(&light_table);
// memcpy(nlight, light, sizeof(light_t));
// sys_assert(!map_put(&chunk->world->obj_map, (void*)light->id, nlight))
// }
// tbl_clear(&chunk->light_table);
// memcpy(&chunk->light_table, &light_table, sizeof(table_t));
// chunk->light_size = size;
// }
void vec_tri_normal(vertex_t *result, vertex_b *vertices) {
vec3 u, v;
glm_vec3_sub(vertices[1].pos, vertices[0].pos, u);
glm_vec3_sub(vertices[2].pos, vertices[0].pos, v);
glm_vec3_crossn(u, v, v);
for(int n = 0; n < 3; n++) {
glm_vec3_copy(v, result[n].normal);
}
}
geometry_t *chunk_add(chunk_t *chunk, polygon_b *polys, int n_polys, float x, float y, float z) {
geometry_t *obj;
if(chunk->poly_list.stored + n_polys > chunk->poly_list.size)
chunk_resize(chunk, chunk->size + (((chunk->poly_list.stored + n_polys) - chunk->poly_list.size) + (CHUNK_POLY_GROW - 1)) / CHUNK_POLY_GROW);
sys_assert(obj = (geometry_t*)tbl_push(&chunk->obj_table));
// if(!obj) {
// chunk_resize(chunk, chunk->size + 1);
// obj = (geometry_t*)tbl_push(&chunk->obj_table);
// }
// logd("TST", "chunk @ %d %d %d -> %d %d", chunk->x, chunk->y, chunk->z, chunk->obj_table.stored, chunk->poly_list.stored);
vertex_b *bvertex;
vertex_t *nvertex;
uint id;
polygon_t *poly;
AABB_SET(obj->collision, x + polys[0].vertices[0].pos[0], y + polys[0].vertices[0].pos[1], z + polys[0].vertices[0].pos[2], x + polys[0].vertices[0].pos[0], y + polys[0].vertices[0].pos[1], z + polys[0].vertices[0].pos[2])
obj->polys = id = lst_push(&chunk->poly_list);
poly = &chunk->poly_pool[id];
for(int c = 0; c < n_polys; c++) {
if(c) {
id = lst_push(&chunk->poly_list);
poly->next = id;
poly = &chunk->poly_pool[id];
}
// poly->id = id;
poly->material = polys[c].material;
vec_tri_normal(poly->vertices, polys[c].vertices);
for(int v = 0; v < 3; v++) {
bvertex = &polys[c].vertices[v];
nvertex = &poly->vertices[v];
box_union(NULL, &obj->collision, x + bvertex->pos[0], y + bvertex->pos[1], z + bvertex->pos[2]);
nvertex->pos[0] = x + bvertex->pos[0] - chunk->box.x1;
nvertex->pos[1] = y + bvertex->pos[1] - chunk->box.y1;
nvertex->pos[2] = z + bvertex->pos[2] - chunk->box.z1;
// norm(nvertex->pos, nvertex->normal);
// VEC3_SET(nvertex->normal, 0.0f, 1.0f, 0.0f);
nvertex->coord[0] = bvertex->coord[0];
nvertex->coord[1] = bvertex->coord[1];
}
}
poly->next = 0xffffffff;
// obj->n_polys = n_polys;
// chunk->dirty = 1;
if(obj->collision.x2 - obj->collision.x1 >= (float)CHUNK_SIZE || obj->collision.y2 - obj->collision.y1 >= (float)CHUNK_SIZE ||
obj->collision.z2 - obj->collision.z1 >= (float)CHUNK_SIZE) {
obj->global = tbl_push(&chunk->world->global_geom);
*(obj->global) = obj;
}
else {
obj->global = NULL;
}
return obj;
}
ulong world_add(world_t *world, polygon_b *polys, int n_polys, float x, float y, float z) {
chunk_t *chunk;
geometry_t *obj;
ulong id;
chunk = chunk_get(world, NEG_OFFSET(x, CHUNK_SIZE), NEG_OFFSET(y, CHUNK_SIZE), NEG_OFFSET(z, CHUNK_SIZE), 1);
// if(!chunk)
// return 0;
obj = chunk_add(chunk, polys, n_polys, x, y, z);
// if(!obj)
// return 0;
id = world->obj_id += 1ULL;
map_put(&world->obj_map, (void*)id, obj);
obj->id = id;
obj->chunk_x = chunk->x;
obj->chunk_y = chunk->y;
obj->chunk_z = chunk->z;
world->n_objects += 1;
chunk_dirty(chunk);
return id;
}
void chunk_remove(chunk_t *chunk, geometry_t *geom) {
uint poly = geom->polys;
while(poly != 0xffffffff) {
lst_pop(&chunk->poly_list, poly);
poly = (&chunk->poly_pool[poly])->next;
}
if(geom->global)
tbl_pop(&chunk->world->global_geom, geom->global);
tbl_pop(&chunk->obj_table, geom);
if((chunk->size > 1) && (chunk->poly_list.stored <= (CHUNK_POLY_GROW * (chunk->size - 1))))
// && (chunk->obj_table.stored <= (((wld.chunk_cap / 32) * (chunk->size - 1)) * (32 * (chunk->size - 1)))))
chunk_resize(chunk, chunk->size - 1);
// chunk->dirty = 1;
}
void world_remove(world_t *world, ulong id) {
chunk_t *chunk;
geometry_t *obj;
obj = map_get(&world->obj_map, (void*)id);
if(!obj)
return;
chunk = chunk_get(world, obj->chunk_x, obj->chunk_y, obj->chunk_z, 0);
if(!chunk)
return;
chunk_remove(chunk, obj);
map_remove(&world->obj_map, (void*)id);
world->n_objects -= 1;
chunk_dirty(chunk);
}
// void chunk_remove_light(chunk_t *chunk, light_t *light) {
// tbl_pop(&chunk->light_table, light);
// }
ulong world_add_light(world_t *world, light_b *lamp, float x, float y, float z) {
chunk_t *chunk;
light_t *light;
ulong id;
chunk = chunk_get(world, NEG_OFFSET(x, CHUNK_SIZE), NEG_OFFSET(y, CHUNK_SIZE), NEG_OFFSET(z, CHUNK_SIZE), 1);
// if(!chunk)
// return 0;
sys_assert(light = (light_t*)tbl_push(&chunk->light_table));
// if(!light) {
// chunk_resize_light(chunk, chunk->light_size + 1);
// light = (light_t*)tbl_push(&chunk->light_table);
// }
id = world->obj_id += 1ULL;
map_put(&world->obj_map, (void*)id, light);
light_init_base(light, lamp);
light->id = id;
VEC3_SET(light->pos, x, y, z);
world->n_lights += 1;
world->light_dirty = 1;
return id;
}
void world_set_light(world_t *world, ulong id, light_b *lamp) {
light_t *light = map_get(&world->obj_map, (void*)id);
if(!light)
return;
light_init_base(light, lamp);
world->light_dirty = 1;
}
void world_remove_light(world_t *world, ulong id) {
chunk_t *chunk;
light_t *light;
light = map_get(&world->obj_map, (void*)id);
if(!light)
return;
chunk = chunk_get(world, NEG_OFFSET(light->pos[0], CHUNK_SIZE), NEG_OFFSET(light->pos[1], CHUNK_SIZE), NEG_OFFSET(light->pos[2], CHUNK_SIZE), 0);
if(!chunk)
return;
tbl_pop(&chunk->light_table, light);
map_remove(&world->obj_map, (void*)id);
world->n_lights -= 1;
// if((chunk->light_size > 1) && (chunk->light_table.stored <= (((wld.chunk_light / 32) * (chunk->light_size - 1)) * (32 * (chunk->light_size - 1)))))
// chunk_resize_light(chunk, chunk->light_size - 1);
world->light_dirty = 1;
}
void chunk_clear(chunk_t *chunk, byte unmap) {
ulong pos = 0;
geometry_t *geom;
uint poly;
while(geom = tbl_iter(&chunk->obj_table, &pos)) {
if(unmap)
map_remove(&chunk->world->obj_map, (void*)geom->id);
poly = geom->polys;
while(poly != 0xffffffff) {
lst_pop(&chunk->poly_list, poly);
poly = (&chunk->poly_pool[poly])->next;
}
if(geom->global)
tbl_pop(&chunk->world->global_geom, geom->global);
}
chunk->world->n_objects -= chunk->obj_table.stored;
chunk->world->n_lights -= chunk->light_table.stored;
if(chunk->light_table.stored)
chunk->world->light_dirty = 1;
tbl_clear(&chunk->obj_table);
tbl_clear(&chunk->light_table);
if(chunk->size > 1)
chunk_resize(chunk, 1);
// if(chunk->light_size > 1)
// chunk_resize_light(chunk, 1);
// chunk->dirty = 1;
chunk_dirty(chunk);
}
void chunk_update(chunk_t *chunk) {
if(chunk->dirty) {
drawlist_t *list = chunk->drawlist;
if(list) {
draw_destroy(list);
}
else if(chunk->obj_table.stored) {
chunk->drawlist = list = tbl_push(&gdr.drawlists);
}
if(chunk->obj_table.stored && list) {
draw_build(chunk, list);
}
else if(list) {
tbl_pop(&gdr.drawlists, list);
chunk->drawlist = NULL;
}
chunk->dirty = 0;
}
}
void chunk_delete(chunk_t *chunk, byte unmap) {
chunk_clear(chunk, unmap);
chunk_update(chunk);
tbl_clear(&chunk->obj_table);
tbl_clear(&chunk->light_table);
lst_free(&chunk->poly_list);
mem_free(chunk->poly_pool);
}
void chunk_unload(world_t *world, int x, int y, int z) {
// if(CHUNK_OUT(world, x, y, z))
// return;
ulong id = CHUNK_ID(x, y, z);
chunk_t *chunk = map_remove(&world->chunk_map, (void*)id);
if(chunk) {
chunk_delete(chunk, 1);
// world->chunks[offset] = NULL;
tbl_pop(&world->chunk_table, chunk);
world->n_chunks -= 1;
}
}
void world_update(world_t *world) {
if(world->chunks_dirty) {
int pos = 0;
chunk_t *chunk = world->dirty_first;
chunk_t *nchunk;
while(chunk) {
chunk_update(chunk);
if(chunk->obj_table.stored == 0 && chunk->light_table.stored == 0) {
nchunk = chunk->next_dirty;
chunk_unload(world, chunk->x, chunk->y, chunk->z);
chunk = nchunk;
}
else {
chunk = chunk->next_dirty;
}
}
world->dirty_first = world->dirty_queue = NULL;
world->chunks_dirty = 0;
}
if(world->light_dirty) {
light_calc(world);
world->light_dirty = 0;
}
}
void world_unload(world_t *world) {
ulong pos = 0;
chunk_t *chunk;
while(chunk = tbl_iter(&world->chunk_table, &pos)) {
chunk_delete(chunk, 0);
}
map_clear(&world->chunk_map);
tbl_clear(&world->global_geom);
tbl_clear(&world->chunk_table);
map_clear(&world->obj_map);
world->n_chunks = world->n_objects = world->n_lights = 0;
}
void vec_make_quad(polygon_b *polys, float u, float v, float w, float p, float q, int m, int n, int o) {
byte uflip = m < 0;
byte vflip = n < 0;
m = (m < 0) ? (-1 - m) : (m - 1);
n = (n < 0) ? (-1 - n) : (n - 1);
o -= 1;
for(int c = 0; c < 3; c++) {
polys[0].vertices[c].pos[o] = w;
polys[1].vertices[c].pos[o] = w;
}
polys[0].vertices[0].pos[m] = polys[1].vertices[2].pos[m] = u;
polys[0].vertices[0].pos[n] = polys[1].vertices[2].pos[n] = v;
polys[0].vertices[0].coord[0] = polys[1].vertices[2].coord[0] = uflip ? p : 0.0f;
polys[0].vertices[0].coord[1] = polys[1].vertices[2].coord[1] = vflip ? q : 0.0f;
polys[0].vertices[1].pos[m] = u + p;
polys[0].vertices[1].pos[n] = v;
polys[0].vertices[1].coord[0] = uflip ? 0.0f : p;
polys[0].vertices[1].coord[1] = vflip ? q : 0.0f;
polys[0].vertices[2].pos[m] = polys[1].vertices[0].pos[m] = u + p;
polys[0].vertices[2].pos[n] = polys[1].vertices[0].pos[n] = v + q;
polys[0].vertices[2].coord[0] = polys[1].vertices[0].coord[0] = uflip ? 0.0f : p;
polys[0].vertices[2].coord[1] = polys[1].vertices[0].coord[1] = vflip ? 0.0f : q;
polys[1].vertices[1].pos[m] = u;
polys[1].vertices[1].pos[n] = v + q;
polys[1].vertices[1].coord[0] = uflip ? p : 0.0f;
polys[1].vertices[1].coord[1] = vflip ? 0.0f : q;
}
void vec_make_dquad(polygon_b *polys, float x, float y, float z, float xsize, float ysize, float zsize, material_t *material, byte dir, byte shift) {
float offset = shift ? 1.0f : 0.0f;
switch(dir) {
case FACE_WEST:
vec_make_quad(polys, z, y, x, zsize, ysize, 3, 2, 1);
break;
case FACE_EAST:
vec_make_quad(polys, z, y, x + offset * xsize, zsize, ysize, -3, 2, 1);
break;
case FACE_DOWN:
vec_make_quad(polys, x, z, y, xsize, zsize, 1, 3, 2);
break;
case FACE_UP:
vec_make_quad(polys, x, z, y + offset * ysize, xsize, zsize, 1, 3, 2);
break;
case FACE_NORTH:
vec_make_quad(polys, x, y, z, xsize, ysize, 1, 2, 3);
break;
case FACE_SOUTH:
vec_make_quad(polys, x, y, z + offset * zsize, xsize, ysize, -1, 2, 3);
break;
}
polys[0].material = polys[1].material = material;
}
ulong world_add_floor(world_t *world, float x, float y, float z, float xsize, float zsize, material_t *material) {
polygon_b polys[2];
vec_make_dquad(polys, 0.0f, 0.0f, 0.0f, xsize, 0.0f, zsize, material, FACE_UP, 0);
return world_add(world, polys, 2, x, y, z);
}
ulong world_add_box(world_t *world, float x, float y, float z, float xsize, float ysize, float zsize, material_t *material) {
polygon_b polys[12];
for(int n = 0; n < 6; n++) {
vec_make_dquad(&polys[n << 1], 0.0f, 0.0f, 0.0f, xsize, ysize, zsize, material, n, 1);
}
return world_add(world, polys, 12, x, y, z);
}
void vec_make_gquad(polygon_b *polys, float x, float z, float p, float q, float y00, float y10, float y01, float y11) {
polys[0].vertices[0].pos X = polys[1].vertices[2].pos X = x;
polys[0].vertices[0].pos Y = polys[1].vertices[2].pos Y = y00;
polys[0].vertices[0].pos Z = polys[1].vertices[2].pos Z = z;
polys[0].vertices[0].coord[0] = polys[1].vertices[2].coord[0] = 0.0f;
polys[0].vertices[0].coord[1] = polys[1].vertices[2].coord[1] = 0.0f;
polys[0].vertices[1].pos X = x + p;
polys[0].vertices[1].pos Y = y10;
polys[0].vertices[1].pos Z = z;
polys[0].vertices[1].coord[0] = p;
polys[0].vertices[1].coord[1] = 0.0f;
polys[0].vertices[2].pos X = polys[1].vertices[0].pos X = x + p;
polys[0].vertices[2].pos Y = polys[1].vertices[0].pos Y = y11;
polys[0].vertices[2].pos Z = polys[1].vertices[0].pos Z = z + q;
polys[0].vertices[2].coord[0] = polys[1].vertices[0].coord[0] = p;
polys[0].vertices[2].coord[1] = polys[1].vertices[0].coord[1] = q;
polys[1].vertices[1].pos X = x;
polys[1].vertices[1].pos Y = y01;
polys[1].vertices[1].pos Z = z + q;
polys[1].vertices[1].coord[0] = 0.0f;
polys[1].vertices[1].coord[1] = q;
}
void world_add_graph(world_t *world, graph2_func *func, void *p, float x, float y, float z, float xsize, float ysize, float zsize,
material_t *material, uint xvals, uint zvals, float xoffset, float zoffset, float xrange, float zrange, uint frag) {
uint xfrags = (xvals + frag - 1) / frag;
uint zfrags = (zvals + frag - 1) / frag;
uint fdx = frag;
uint fdz = frag;
uint sx, sz, fx, fz, gx, gz;
polygon_b *polys = mem_alloc(sizeof(polygon_b) * 2 * frag * frag, MEM_POLY);
polygon_b *poly;
for(sx = 0; sx < xfrags; sx++) {
if(sx == xfrags - 1)
fdx = xvals - ((xfrags - 1) * frag);
for(sz = 0; sz < zfrags; sz++) {
if(sz == zfrags - 1)
fdz = zvals - ((zfrags - 1) * frag);
for(fx = 0; fx < fdx; fx++) {
gx = sx * frag + fx;
for(fz = 0; fz < fdz; fz++) {
gz = sz * frag + fz;
poly = &polys[(fx + fz * fdx) * 2];
poly[0].material = poly[1].material = material;
vec_make_gquad(poly, xsize * (float)fx / (float)xvals, zsize * (float)fz / (float)zvals, xsize / (float)xvals, zsize / (float)zvals,
func(p, xoffset + (float)gx / (float)xvals * xrange, zoffset + (float)gz / (float)zvals * zrange) * ysize,
func(p, xoffset + (float)(gx + 1) / (float)xvals * xrange, zoffset + (float)gz / (float)zvals * zrange) * ysize,
func(p, xoffset + (float)gx / (float)xvals * xrange, zoffset + (float)(gz + 1) / (float)zvals * zrange) * ysize,
func(p, xoffset + (float)(gx + 1) / (float)xvals * xrange, zoffset + (float)(gz + 1) / (float)zvals * zrange) * ysize);
// logi("t", "%d %d; %d %d; %d %d; %.1f %.1f -> %.1f", sx, sz, fx, fz, gx, gz,
// xoffset + (float)gx / (float)xvals * xrange, zoffset + (float)gz / (float)zvals * zrange, poly[0].vertices[0].pos Y);
}
}
world_add(world, polys, fdx * fdz * 2, x + (xsize * (float)(sx * frag) / (float)xvals), y, z + (zsize * (float)(sz * frag) / (float)zvals));
}
}
mem_free(polys);
}