#include "terrain.h" #include "math/math_util.h" #include "math/vector.h" #include "util/util.h" #include "renderer/renderer.h" #include #include #include #define PLANE_SIZE 128 #define PLANE_MAX_HEIGHT 10 #define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(*a) static float GetHeight(int x, int y, SDL_Surface* surface) { if(x < 0 || x >= surface->w || y < 0 || y >= surface->h) return 0.0f; Uint32 pixel = ( (Uint32*)surface->pixels )[y * surface->w + x]; Uint8 r, g, b; SDL_GetRGB(pixel, surface->format, &r, &g, &b); float height = (float)r / 255.0f; return height * 40.0f; } static Vec3 GenerateNomal(int x, int y, SDL_Surface* surface) { float hLeft = GetHeight(x-1, y, surface); float hRight = GetHeight(x+1, y, surface); float hUp = GetHeight(x, y+1, surface); float hDown = GetHeight(x, y-1, surface); Vec3 normal = { hLeft - hRight, 2.0f, hDown - hUp}; return vec3_normalize(&normal); } GLfloat Terrain_GetHeightOfTerrain(Terrain *terrain, GLfloat x, GLfloat z) { GLfloat terrainX = x - terrain->position.x; GLfloat terrainZ = z - terrain->position.z; GLfloat gridSquareSize = (float)terrain->l / ( (float)PLANE_SIZE - 1 ); GLint gridX = (GLint) floor(terrainX / gridSquareSize); GLint gridZ = (GLint) floor(terrainZ / gridSquareSize); if(gridX >= PLANE_SIZE - 1 || gridX < 0 || gridZ >= PLANE_SIZE - 1 || gridZ < 0) { printf("called\n"); return 0; } GLfloat xCoord = fmod(terrainX, gridSquareSize) / gridSquareSize; GLfloat zCoord = fmod(terrainZ, gridSquareSize) / gridSquareSize; GLfloat answer; /* Determine in which triangle of the square are we. "Bary Centric Interpolation"*/ if(xCoord <= (1 - zCoord)){ /* 0, heights[gridX][gridZ], 0) */ Vec3 p1 = { 0, terrain->height[ gridX * PLANE_SIZE + gridZ ], 0 }; /* 1, heights[gridX + 1][gridZ], 0) */ Vec3 p2 = { 1, terrain->height[ (gridX + 1) * PLANE_SIZE + gridZ ], 0}; /* 0, heights[gridX][gridZ + 1], 1) */ Vec3 p3 = { 0, terrain->height[ gridX * PLANE_SIZE + (gridZ + 1) ], 1}; Vec2 pos = {xCoord, zCoord}; answer = baryCentric(&p1, &p2, &p3, &pos); } else { /* (1, heights[gridX + 1][gridZ], 0) */ Vec3 p1 = { 1, terrain->height[ (gridX + 1) * PLANE_SIZE + gridZ ], 0 }; /* (1, heights[gridX + 1][gridZ + 1], 1) */ Vec3 p2 = { 1, terrain->height[ (gridX + 1) * PLANE_SIZE + (gridZ + 1) ], 1}; /* (0, heights[gridX][gridZ + 1], 1) */ Vec3 p3 = { 0, terrain->height[ gridX * PLANE_SIZE + (gridZ + 1) ], 1}; Vec2 pos = {xCoord, zCoord}; answer = baryCentric(&p1, &p2, &p3, &pos); } return answer; } Terrain *Terrain_Create( int w, int l, const char* heightmap_path, Texture *blendmap, TerrainTexturePack *textures ) { Terrain *terrain = malloc( sizeof(Terrain) ); terrain->height = (GLfloat*) malloc( sizeof(GLfloat) * PLANE_SIZE * PLANE_SIZE); terrain->blendmap = blendmap; terrain->w = w; terrain->l = l; terrain->textures = *textures; SDL_Surface* surface = IMG_Load(heightmap_path); if(surface == NULL) Util_FatalError("Heightmap file could not be loaded\n"); vertex_t data[PLANE_SIZE * PLANE_SIZE]; int x, y; for(x = 0; x < PLANE_SIZE; x++) { for(y = 0; y < PLANE_SIZE; y++) { vertex_t* v = &data[y + x * PLANE_SIZE]; v->position = (Vec3){ (float)x / (float)PLANE_SIZE, 0.0f, (float)y / (float)PLANE_SIZE }; /* Heightmap cordinates */ int image_x = v->position.x * surface->w, image_y = v->position.z * surface->h; v->texCoord = (Vec2){ v->position.x, v->position.z }; GLfloat height = GetHeight(image_x, image_y, surface); terrain->height[y + x * PLANE_SIZE] = height; v->position.y = height; v->position.x *= w; v->position.z *= l; v->normal = GenerateNomal(image_x, image_y, surface); } } int runner = 0; GLushort indices[ (PLANE_SIZE-1) * (PLANE_SIZE-1) * 6 ]; for(x = 0; x < PLANE_SIZE-1; x++) { for(y = 0; y < PLANE_SIZE-1; y++) { indices[runner++] = PLANE_SIZE * x + y; indices[runner++] = PLANE_SIZE * x + y + 1; indices[runner++] = PLANE_SIZE * x + y + PLANE_SIZE; indices[runner++] = PLANE_SIZE * x + y + 1; indices[runner++] = PLANE_SIZE * x + y + PLANE_SIZE + 1; indices[runner++] = PLANE_SIZE * x + y + PLANE_SIZE; } } GLsizeiptr vertexBufferSize = NUM_ARRAY_ELEMENTS(data) * sizeof(vertex_t); GLsizeiptr indexBufferSize = NUM_ARRAY_ELEMENTS(indices) * sizeof(GLushort); SDL_FreeSurface(surface); terrain->mesh = mesh_new(data, vertexBufferSize, indices, indexBufferSize); return terrain; } void Terrain_Destroy( Terrain *terrain ) { if(terrain->height) free(terrain->height); free(terrain); }