aboutsummaryrefslogtreecommitdiffstats
path: root/src/world.c
blob: b444ff1e3fbbe37829480490b1f724d51ac53324 (plain) (blame)
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.