1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
#include "world.h"
#include "game.h"
World createWorld(const Assets* assets)
{
World world;
world.size = (Vector3){1000.0, 100.0, 1000.0};
// Heightmap image.
Image image = GenImagePerlinNoise(100, 100, 0, 0, 5.0);
// 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);
int seed = 57;
// Entities.
for (int index = 0; index < WORLD_ENTITY_MAX; ++index)
{
FT_RANDOM16(seed);
Entity entity = createEntity(seed % ENTITY_COUNT, Vector3Zero());
FT_RANDOM16(seed);
entity.position.x = seed % (int)world.size.x;
FT_RANDOM16(seed);
entity.position.z = seed % (int)world.size.z;
entity.position.y = getWorldHeightAtLocation(world, entity.position.x,
entity.position.z) + 1.0;
world.entities[index] = entity;
}
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);
}
}
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.
|