#include "world.h" #include "game.h" // Very messy right now. Mostly been playing around. void buildWorldBVH(World* world) { Entity* entities = world->entities; bool grouped[WORLD_ENTITY_MAX]; WorldUID groupedList[WORLD_ENTITY_MAX]; int ungroupedCount = WORLD_ENTITY_MAX; for (int index = 0; index < WORLD_ENTITY_MAX; ++index) { grouped[index] = false; groupedList[index] = index; } world->bvhTestSize = 0; // This is going to be slow. Optimizjl;dsfz lajtklers (: while (ungroupedCount > 0) { BVHNode leaf; for (int leafIndex = 0; leafIndex < BVH_MAX; ++leafIndex) { int closest = -1; int closestGroupedIndex = 0; float closestDistance = world->size.x; // Find closest. for (int groupedIndex = WORLD_ENTITY_MAX - ungroupedCount; groupedIndex < WORLD_ENTITY_MAX; ++groupedIndex) { int index = groupedList[groupedIndex]; // First entity. if (leafIndex == 0) { closest = index; break; } float distance = 0.0; Vector3 min = entities[index].position; Vector3 max = min; for (int innerIndex = 0; innerIndex < leafIndex; ++innerIndex) { distance += Vector3Distance( entities[leaf.entities[innerIndex]].position, entities[index].position); min = Vector3Min(min, entities[leaf.entities[innerIndex]].position); min = Vector3Min(min, entities[index].position); max = Vector3Max(max, entities[leaf.entities[innerIndex]].position); max = Vector3Max(max, entities[index].position); } BoundingBox overlapBox = (BoundingBox){min, max}; distance /= (float)leafIndex; bool overlaps = false; // Check if overlap will be caused. for (int innerGroupedIndex = 0; innerGroupedIndex < WORLD_ENTITY_MAX - ungroupedCount; ++innerGroupedIndex) { int overlapIndex = groupedList[innerGroupedIndex]; bool isPartOf = false; for (int partOfIndex = 0; partOfIndex < leafIndex + 1; ++partOfIndex) { if (overlapIndex == leaf.entities[partOfIndex]) { isPartOf = true; break; } } if (isPartOf) { continue; } // TODO: Make use of entity bounding boxes. if (CheckCollisionBoxSphere( overlapBox, entities[overlapIndex].position, 0.5)) { overlaps = true; break; } } // Update distance. if (!overlaps && distance < closestDistance) { closestDistance = distance; closest = index; closestGroupedIndex = groupedIndex; } } if (closest == -1) { leaf.entities[leafIndex] = -1; } else { leaf.entities[leafIndex] = closest; grouped[closest] = true; // Bring grouped entities toward the front. WorldUID temp = groupedList[closestGroupedIndex]; groupedList[closestGroupedIndex] = groupedList[WORLD_ENTITY_MAX - ungroupedCount]; groupedList[WORLD_ENTITY_MAX - ungroupedCount] = temp; --ungroupedCount; } } // Get 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) { if (leaf.entities[index] == -1) { continue; } 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[world->bvhTestSize] = leaf; ++world->bvhTestSize; } // test for (int index = 0; index < WORLD_ENTITY_MAX; ++index) { if (!grouped[index]) { printf("%d\n", index); } } printf("size: %d\n", world->bvhTestSize); } 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; } double currentTime = GetTime(); buildWorldBVH(&world); printf("%lf\n", GetTime() - currentTime); 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); } // Draw BVH leafs. for (int index = 0; index < world->bvhTestSize; ++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(const World* world, float x, float y) { float mapX = (float)world->texture.width / world->size.x * x; float mapY = (float)world->texture.height / world->size.z * y; RayCollision result; for (int yOffset = -1; yOffset < 2; ++yOffset) { for (int xOffset = -1; xOffset < 2; ++xOffset) { int pixelX = mapX + xOffset; int pixelY = mapY + yOffset; if (pixelX < 0 || pixelX >= world->texture.width || pixelY < 0 || pixelY >= world->texture.height) { continue; } int verticeStart = (pixelY * (world->texture.width - 1) + pixelX) * 18; float* vertices = &world->heightmap.meshes[0].vertices[verticeStart]; // Cast to triangles at pixel. Really hacky indeed. Ray ray = (Ray){ .position = (Vector3){x, world->size.y * 2.0, y}, .direction = (Vector3){0.0, -1.0, 0.0} }; 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; } } } return 0.0; } // Abortions are good. Get more abortions.