#include "world.h" #include "game.h" /* int64_t hashWorldPosition(Vector3 position, Vector3 size) */ /* { */ /* return (((int64_t)position.x) << 32) | (((int64_t)position.z) << 16) */ /* | ((int64_t)position.y); */ /* } */ /* void sortEntitiesUID(WorldUID entities[WORLD_ENTITY_MAX], const World* world) */ /* { */ /* // Lazy selection sort. */ /* for (int outer = 0; outer < WORLD_ENTITY_MAX - 1; ++outer) */ /* { */ /* int minIndex = outer; */ /* for (int inner = outer + 1; inner < WORLD_ENTITY_MAX; ++inner) */ /* { */ /* int64_t entityHash = hashWorldPosition( */ /* world->entities[entities[inner]].position, world->size); */ /* int64_t minHash = hashWorldPosition( */ /* world->entities[entities[minIndex]].position, world->size); */ /* if (entityHash < minHash) */ /* { */ /* minIndex = inner; */ /* } */ /* } */ /* WorldUID temp = entities[outer]; */ /* entities[outer] = entities[minIndex]; */ /* entities[minIndex] = temp; */ /* } */ /* for (int index = 0; index < WORLD_ENTITY_MAX; ++index) */ /* { */ /* PRINT_VECTOR3(world->entities[entities[index]].position); */ /* } */ /* } */ /* void buildWorldBVH(World* world) */ /* { */ /* WorldUID sorted[WORLD_ENTITY_MAX]; */ /* for (int index = 0; index < WORLD_ENTITY_MAX; ++index) */ /* { */ /* sorted[index] = index; */ /* } */ /* sortEntitiesUID(sorted, world); */ /* for (int index = 0; index < WORLD_ENTITY_MAX; index += BVH_MAX) */ /* { */ /* BVHNode leaf; */ /* leaf.branch1 = NULL; */ /* leaf.branch2 = NULL; */ /* for (int leafIndex = 0; leafIndex < BVH_MAX; ++leafIndex) */ /* { */ /* leaf.entities[leafIndex] = sorted[index + leafIndex]; */ /* } */ /* // Create bounding box. */ /* leaf.box.min = world->entities[leaf.entities[0]].position; */ /* leaf.box.max = world->entities[leaf.entities[0]].position; */ /* for (int index = 1; index < BVH_MAX; ++index) */ /* { */ /* leaf.box.min = Vector3Min( */ /* leaf.box.min, */ /* world->entities[leaf.entities[index]].position); */ /* leaf.box.max = Vector3Max( */ /* leaf.box.max, */ /* world->entities[leaf.entities[index]].position); */ /* } */ /* world->bvhTest[index / 4] = leaf; */ /* } */ /* } */ // Bottom up method because bottom up better. Just if politicians agreed ): // z curve broky, last metho was decent but was first come first serve and // would leave leafs toward the end with shitty options and end up being big. void buildWorldBVH(World* world) { Entity* entities = world->entities; bool grouped[WORLD_ENTITY_MAX]; BVHNode leafs[250]; // This is a mess thats not going to work. for (int index = 0; index < WORLD_ENTITY_MAX; ++index) { grouped[index] = false; } for (int leafIndex = 0; leafIndex < BVH_MAX; ++leafIndex) { for (int nodeIndex = 0; nodeIndex < 250; ++nodeIndex) { BVHNode* leaf = &leafs[nodeIndex]; // First entity. if (leafIndex == 0) { leaf->entities[0] = nodeIndex * 4; grouped[nodeIndex * 4] = true; continue; } // Find closest to entity one. // TODO: Average out the entities. int closest = -1; float closestDistance = world->size.x * world->size.z * 2.0; for (int index = 0; index < WORLD_ENTITY_MAX; ++index) { if (grouped[index]) { continue; } float distance = Vector3Distance( entities[leaf->entities[0]].position, entities[index].position); if (distance < closestDistance) { closestDistance = distance; closest = index; } } if (closest != -1) { leaf->entities[leafIndex] = closest; grouped[closest] = true; } // Last entity if (leafIndex == BVH_MAX - 1) { // Create bounding box. */ leaf->box.min = world->entities[leaf->entities[0]].position; leaf->box.max = world->entities[leaf->entities[0]].position; for (int index = 1; index < BVH_MAX; ++index) { leaf->box.min = Vector3Min( leaf->box.min, world->entities[leaf->entities[index]].position); leaf->box.max = Vector3Max( leaf->box.max, world->entities[leaf->entities[index]].position); } world->bvhTest[nodeIndex] = leafs[nodeIndex]; } } } // test for (int index = 0; index < WORLD_ENTITY_MAX; ++index) { if (!grouped[index]) { printf("%d\n", index); } } } World createWorld(int seed) { World world; world.size = WORLD_SIZE; // Heightmap image. int offsetX = FT_RANDOM16(seed); int offsetY = FT_RANDOM16(seed); Image image = GenImagePerlinNoise(WORLD_IMAGE_WIDTH, WORLD_IMAGE_HEIGHT, offsetX, offsetY, WORLD_IMAGE_SCALE); // Heightmap. Mesh mesh = GenMeshHeightmap(image, world.size); world.heightmap = LoadModelFromMesh(mesh); world.texture = LoadTextureFromImage(image); world.heightmap.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = world.texture; UnloadImage(image); // Entities. for (int index = 0; index < WORLD_ENTITY_MAX; ++index) { FT_RANDOM16(seed); Entity entity = createEntity(seed % ENTITY_COUNT, Vector3Zero()); entity.position.x = FT_RANDOM16(seed) % (int)world.size.x; entity.position.z = FT_RANDOM16(seed) % (int)world.size.z; entity.position.y = getWorldHeightAtLocation(world, entity.position.x, entity.position.z) + 1.0; world.entities[index] = entity; } buildWorldBVH(&world); return world; } void updateWorld(World* world, Game* game) { DrawModel(world->heightmap, Vector3Zero(), 1.0, WHITE); for (int index = 0; index < WORLD_ENTITY_MAX; ++index) { updateEntity(&world->entities[index], game); } for (int index = 0; index < 250; ++index) { Color colors[] = {RED, GREEN, BLUE, ORANGE, YELLOW, PINK}; DrawBoundingBox(world->bvhTest[index].box, colors[index % 6]); } } void freeWorld(World world) { UnloadTexture(world.texture); UnloadModel(world.heightmap); } float getWorldHeightAtLocation(World world, float x, float y) { float toMapX = (float)world.texture.width / world.size.x; float toMapY = (float)world.texture.height / world.size.z; int pixelX = x * toMapX; int pixelY = y * toMapY; int verticeStart = (pixelY * (world.texture.width - 1) + pixelX) * 18; float* vertices = &world.heightmap.meshes[0].vertices[verticeStart]; // Clamp x and y to prevent ray being out of bounds. Vector2 min = (Vector2){vertices[0], vertices[2]}; Vector2 max = (Vector2){vertices[0], vertices[2]}; for (int index = 0; index < 18; index += 3) { Vector2 vertex = (Vector2){vertices[index], vertices[index + 2]}; min = Vector2Min(min, vertex); max = Vector2Max(max, vertex); } // Cast to triangles at pixel. Really hacky indeed. Ray ray = (Ray){ .position = (Vector3){ Clamp(x, min.x, max.x), FLT_MAX_EXP, Clamp(y, min.y, max.y)}, .direction = (Vector3){0.0, -1.0, 0.0} }; RayCollision result = GetRayCollisionTriangle( ray, (Vector3){vertices[0], vertices[1], vertices[2]}, (Vector3){vertices[3], vertices[4], vertices[5]}, (Vector3){vertices[6], vertices[7], vertices[8]}); // Test other triangle. if (!result.hit) { result = GetRayCollisionTriangle( ray, (Vector3){vertices[9], vertices[10], vertices[11]}, (Vector3){vertices[12], vertices[13], vertices[14]}, (Vector3){vertices[15], vertices[16], vertices[17]}); } if (result.hit) { return result.point.y; } else // Fall back. { float height = 0.0; for (int index = 1; index < 18; index += 3) { height += vertices[index]; } return height / 6.0; } } // Abortions are good. Get more abortions.