diff --git a/CMakeLists.txt b/CMakeLists.txt index 950991e..0479c28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,12 +3,13 @@ # Written by JackCarterSmith, 2021 # This code is released under the RSE license. -cmake_minimum_required(VERSION 3.1) -cmake_policy(VERSION 3.1) +cmake_minimum_required(VERSION 3.12) +cmake_policy(VERSION 3.12) set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}) # define project add_definitions(-DCONF_NO_GL) +#add_definitions(-DNO_PNG_SUPPORT) if(DEFINED ENV{CI}) project(rse-map VERSION $ENV{CI_VERSION}.$ENV{CI_BUILD_NUMBER} DESCRIPTION "RogueSquadron Extractor - Map" LANGUAGES C) set(RSE_MAP_NAME $ENV{CI_OUTPUT_NAME}-${PROJECT_VERSION}) diff --git a/src/Terrain-Extractor.c b/src/Terrain-Extractor.c index 420a60f..f555efb 100644 --- a/src/Terrain-Extractor.c +++ b/src/Terrain-Extractor.c @@ -81,18 +81,17 @@ static unsigned int mainProcess(int args_cnt, char* args_value[], T_PROG_OPTIONS if (p_opts->output_dir) createSubDir(args_value[file_index]); +#ifndef NO_PNG_SUPPORT if (exportHeightmapPNG(terrainStruct, args_value[file_index], p_opts) != NO_ERROR) printf("[ERR] Failed to export heightmap to PNG format!\n"); else printf("[INFO] Successfully exported heightmap to PNG format.\n"); - /* - for ( i = 0; i < hobStruct->obj_count; i++ ) { - if (exportOBJModel(&(hobStruct->objects[i]), args_value[file_index], p_opts) != NO_ERROR) - printf("[ERR] Failed to export %s object in OBJ format!\n", hobStruct->objects[i].name); - else - printf("[INFO] Successfully exported %s object in OBJ format.\n", hobStruct->objects[i].name); - } - */ +#endif + + if (exportHeightmapOBJ(terrainStruct, args_value[file_index], p_opts) != NO_ERROR) + printf("[ERR] Failed to export terrain in OBJ format!\n"); + else + printf("[INFO] Successfully exported terrain in OBJ format.\n"); cleanUpResources(terrainStruct); } @@ -106,7 +105,6 @@ static unsigned char checkInputArgs(T_PROG_OPTIONS* opt_ptr, int p_arg_nbr, char // Set default options opt_ptr->raw = 0; - opt_ptr->output_dir = 1; if (p_arg_nbr > 1) { for ( i = 1; i < p_arg_nbr; i++) { @@ -124,9 +122,9 @@ static unsigned char checkInputArgs(T_PROG_OPTIONS* opt_ptr, int p_arg_nbr, char } else if (strcmp(p_args[i], "-vvv") == 0) { opt_ptr->god_mode = 1; printf("[OPTN] God damn it!\n"); - } else if (strcmp(p_args[i], "-no-subdir") == 0) { + } else if (strcmp(p_args[i], "-subdir") == 0) { opt_ptr->output_dir = 0; - printf("[OPTN] Export to current directory.\n"); + printf("[OPTN] Export to sub-directory.\n"); } else if (strcmp(p_args[i], "-neg") == 0) { opt_ptr->neg_heightmap = 1; printf("[OPTN] Negative heightmap output mode.\n"); @@ -158,8 +156,8 @@ static void createSubDir(char *dirName) { static void dispHelp() { printf("\n"); printf("Options:\n -h Print this message\n"); - printf(" -v -vv Activate verbose console output\n"); - printf(" -no-subdir Export models inside current folder\n"); + printf(" -v -vv Activate verbose console output\n"); + printf(" -subdir Export output to a sub-directory\n"); printf(" -neg Negative heightmap output\n"); printf("\n"); printf("Usage: RSE-Terrain_%s [options] \n", VERSION); diff --git a/src/hmp_export.c b/src/hmp_export.c index 3faaa55..1f211e7 100644 --- a/src/hmp_export.c +++ b/src/hmp_export.c @@ -1,6 +1,6 @@ /** * \file hmp_export.c - * \date 31/07/2022 + * \date 02/08/2022 * \author JackCarterSmith * \copyright GPL-v3.0 * \brief Export datas to heightmap PNG and Waveform OBJ format. @@ -9,21 +9,25 @@ #include #include #include +#ifndef NO_PNG_SUPPORT #include #include +#endif +#include "rlk/obj.h" #include "errors_types.h" #include "options.h" #include "hmp_struct.h" #include "hmp_export.h" +#ifndef NO_PNG_SUPPORT unsigned char exportHeightmapPNG(const T_TERRAIN* terrain, const char *out_path, T_PROG_OPTIONS* p_opts) { if (out_path == NULL || terrain == NULL) return ERROR_ARGS_NULL; char export_path[128]; FILE *_png_f = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; - size_t x,y; + size_t x,z; png_byte **row_ptrs = NULL; //int pixel_size = 3; //int depth = 8; //bit par color channel (RGB) @@ -54,25 +58,17 @@ unsigned char exportHeightmapPNG(const T_TERRAIN* terrain, const char *out_path, } // Set image attributes - png_set_IHDR(png_ptr, info_ptr, terrain->width * TILE_HEIGHT_TUNE, terrain->height * TILE_HEIGHT_TUNE, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_IHDR(png_ptr, info_ptr, terrain->width * TERRAIN_TILE_SAMPLING, terrain->height * TERRAIN_TILE_SAMPLING, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Store PNG datas - row_ptrs = png_malloc(png_ptr, terrain->height * TILE_HEIGHT_TUNE * sizeof(png_byte *)); - for ( y = 0; y < terrain->height * TILE_HEIGHT_TUNE; y++ ) { - png_byte *row = png_malloc(png_ptr, terrain->width * TILE_HEIGHT_TUNE * sizeof(unsigned char) * 3); - row_ptrs[y] = row; - for ( x = 0; x < terrain->width * TILE_HEIGHT_TUNE; x++ ) { - // Invert Y to display 0,0 origin at bottom left of image - *row++ = terrain->heightmap[x][terrain->height * TILE_HEIGHT_TUNE - 1 - y]; - *row++ = terrain->heightmap[x][terrain->height * TILE_HEIGHT_TUNE - 1 - y]; - *row++ = terrain->heightmap[x][terrain->height * TILE_HEIGHT_TUNE - 1 - y]; - - // Normal one struc - /* - *row++ = terrain->heightmap[x][y]; - *row++ = terrain->heightmap[x][y]; - *row++ = terrain->heightmap[x][y]; - */ + row_ptrs = png_malloc(png_ptr, terrain->height * TERRAIN_TILE_SAMPLING * sizeof(png_byte *)); + for ( z = 0; z < terrain->height * TERRAIN_TILE_SAMPLING; z++ ) { + png_byte *row = png_malloc(png_ptr, terrain->width * TERRAIN_TILE_SAMPLING * sizeof(unsigned char) * 3); + row_ptrs[z] = row; + for ( x = 0; x < terrain->width * TERRAIN_TILE_SAMPLING; x++ ) { + *row++ = terrain->heightmap[x][z]; + *row++ = terrain->heightmap[x][z]; + *row++ = terrain->heightmap[x][z]; } } @@ -80,12 +76,81 @@ unsigned char exportHeightmapPNG(const T_TERRAIN* terrain, const char *out_path, png_set_rows(png_ptr, info_ptr, row_ptrs); png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); - for ( y = 0; y < terrain->height * TILE_HEIGHT_TUNE; y++ ) { - png_free(png_ptr, row_ptrs[y]); + for ( z = 0; z < terrain->height * TERRAIN_TILE_SAMPLING; z++ ) { + png_free(png_ptr, row_ptrs[z]); } png_free(png_ptr, row_ptrs); png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(_png_f); - return EXIT_SUCCESS; + return NO_ERROR; +} +#endif + +unsigned char exportHeightmapOBJ(const T_TERRAIN* terrain_struct, const char *out_path, T_PROG_OPTIONS* p_opts) { + char objExport_path[128]; + char mtlExport_path[128]; + obj* objConstruct = NULL; + unsigned int i,j,stride; + int surfID = 0; + float vertexBuff[3] = {0}; + int indicesBuff[3] = {0}; + + if (terrain_struct == NULL || out_path == NULL) + return ERROR_ARGS_NULL; + + strcpy(objExport_path, out_path); + if (p_opts->output_dir) { + #ifdef _WIN32 + strcat(objExport_path, "-out\\"); + #else + strcat(objExport_path, "-out/"); + #endif + strcat(objExport_path, "heightmap"); + } else { + strcat(objExport_path, "-heightmap"); + } + strcpy(mtlExport_path, objExport_path); + strcat(objExport_path, ".obj\0"); + strcat(mtlExport_path, ".mtl\0"); + + objConstruct = obj_create(NULL); + + // Build face/surface material group + surfID = obj_add_surf(objConstruct); + obj_add_mtrl(objConstruct); + + // Build vertex container + for ( i = 0; i < terrain_struct->verticesmap_size; i++ ) { + vertexBuff[0] = terrain_struct->verticesmap[i].x; + vertexBuff[1] = terrain_struct->verticesmap[i].y; + vertexBuff[2] = terrain_struct->verticesmap[i].z; + obj_set_vert_v(objConstruct, obj_add_vert(objConstruct), vertexBuff); + } + + // Build indices container + // Each tile contains 2 triangles, build both of them. + // 1-2 2 + // |/ /| + // 3 3-4 + stride = terrain_struct->width * TERRAIN_TILE_SAMPLING; + for ( j = 0; j < terrain_struct->width * TERRAIN_TILE_SAMPLING - 1; j++ ) { + for ( i = 0; i < terrain_struct->width * TERRAIN_TILE_SAMPLING - 1; i++ ) { + indicesBuff[0] = j * stride + i + 1 - 1; //TODO: -1 needed to compensate the obj constructor. Really need to rewrite my own... + indicesBuff[1] = indicesBuff[0] + 1; + indicesBuff[2] = indicesBuff[0] + stride; + obj_set_poly(objConstruct, surfID, obj_add_poly(objConstruct, surfID), indicesBuff); + indicesBuff[0] = indicesBuff[1]; + indicesBuff[1] = indicesBuff[2] + 1; + //indicesBuff[2] = indicesBuff[0] + stride; + obj_set_poly(objConstruct, surfID, obj_add_poly(objConstruct, surfID), indicesBuff); + } + } + + obj_write(objConstruct, objExport_path, NULL, 8); + + obj_delete(objConstruct); + + return NO_ERROR; } diff --git a/src/hmp_export.h b/src/hmp_export.h index 162c7d5..daa1303 100644 --- a/src/hmp_export.h +++ b/src/hmp_export.h @@ -1,6 +1,6 @@ /** * \file hmp_export.h - * \date 31/07/2022 + * \date 02/08/2022 * \author JackCarterSmith * \copyright GPL-v3.0 * \brief Export datas to heightmap PNG and Waveform OBJ format. @@ -9,6 +9,9 @@ #ifndef SRC_HMP_EXPORT_H_ #define SRC_HMP_EXPORT_H_ +#ifndef NO_PNG_SUPPORT unsigned char exportHeightmapPNG(const T_TERRAIN* terrain, const char* out_path, T_PROG_OPTIONS* p_opts); +#endif +unsigned char exportHeightmapOBJ(const T_TERRAIN* terrain_struct, const char *out_path, T_PROG_OPTIONS* p_opts); #endif /* SRC_HMP_EXPORT_H_ */ diff --git a/src/hmp_parser.c b/src/hmp_parser.c index 20dbcb6..b9dc1a3 100644 --- a/src/hmp_parser.c +++ b/src/hmp_parser.c @@ -17,6 +17,7 @@ static void processTilesToHeightmap(T_TERRAIN* terrain, const T_TILE_INDICES* tiles_indices, const T_HMPFILE_TILE* tiles, const unsigned char negativeOutput); +static void processHeighmapToVertices(T_TERRAIN* terrain, const float h_scale); unsigned char parseHMPFile(const char* fileName, T_TERRAIN* hmp_struct, T_PROG_OPTIONS* p_opts) { unsigned char err = NO_ERROR; @@ -56,6 +57,7 @@ unsigned char parseHMPFile(const char* fileName, T_TERRAIN* hmp_struct, T_PROG_O printf("[DBG] > Height scale: %.8f\n", y_scale); printf("[DBG] > Tiles count: %d\n", ((T_HMPFILE_HEADER *)memFile)->tiles_count); printf("[DBG] > Tiles offset: 0x%X\n", tiles_offset); + printf("[DBG] > Terrain size: %dx%d\n", hmp_struct->width, hmp_struct->height); printf("\n"); } if (p_opts->god_mode) { @@ -75,6 +77,8 @@ unsigned char parseHMPFile(const char* fileName, T_TERRAIN* hmp_struct, T_PROG_O // Convert tiles datas to raw heightmap processTilesToHeightmap(hmp_struct, tiles_indices, tiles, p_opts->neg_heightmap); + // Convert tiles datas to terrain vertices + processHeighmapToVertices(hmp_struct, y_scale); free(tiles); free(tiles_indices); @@ -99,18 +103,19 @@ void cleanUpResources(T_TERRAIN* terrain) { if (terrain == NULL) return; - for ( i = 0; i < terrain->width * TILE_HEIGHT_TUNE; i++ ) + for ( i = 0; i < terrain->width * TERRAIN_TILE_SAMPLING; i++ ) free(terrain->heightmap[i]); free(terrain->heightmap); + free(terrain->verticesmap); free(terrain); } static void processTilesToHeightmap(T_TERRAIN* terrain, const T_TILE_INDICES* tiles_indices, const T_HMPFILE_TILE* tiles, const unsigned char negativeOutput) { T_TILE_INDICES tiles_idx; unsigned int i,j,k,l; - unsigned int heightmap_size_w = terrain->width * TILE_HEIGHT_TUNE; - unsigned int heightmap_size_h = terrain->height * TILE_HEIGHT_TUNE; + unsigned int heightmap_size_w = terrain->width * TERRAIN_TILE_SAMPLING; + unsigned int heightmap_size_h = terrain->height * TERRAIN_TILE_SAMPLING; // Build 2D array to contain height values terrain->heightmap = (unsigned char **)malloc(heightmap_size_w * sizeof(unsigned char *)); @@ -124,14 +129,52 @@ static void processTilesToHeightmap(T_TERRAIN* terrain, const T_TILE_INDICES* ti tiles_idx = tiles_indices[j * terrain->width + i]; // Get the 5x5 bytes height values for this tile - for ( k = 0; k < TILE_HEIGHT_TUNE; k++ ) { - for ( l = 0; l < TILE_HEIGHT_TUNE; l++ ) { - if (negativeOutput) - terrain->heightmap[i*TILE_HEIGHT_TUNE+k][j*TILE_HEIGHT_TUNE+l] = 255 - (tiles[tiles_idx].height_values[l][k] + 128); - else - terrain->heightmap[i*TILE_HEIGHT_TUNE+k][j*TILE_HEIGHT_TUNE+l] = tiles[tiles_idx].height_values[l][k] + 128; + for ( k = 0; k < TERRAIN_TILE_SAMPLING; k++ ) { + for ( l = 0; l < TERRAIN_TILE_SAMPLING; l++ ) { + if (negativeOutput) { + // Invert Z to set 0,0 origin at bottom left of terrain + terrain->heightmap[i*TERRAIN_TILE_SAMPLING+k][(heightmap_size_h-1) - (j*TERRAIN_TILE_SAMPLING+l)] = + tiles[tiles_idx].height_values[l][k] + 128; + //terrain->heightmap[i*TERRAIN_TILE_SAMPLING+k][j*TERRAIN_TILE_SAMPLING+l] = + // tiles[tiles_idx].height_values[l][k] + 128; + } else { + // Invert Z to set 0,0 origin at bottom left of terrain + terrain->heightmap[i*TERRAIN_TILE_SAMPLING+k][(heightmap_size_h-1) - (j*TERRAIN_TILE_SAMPLING+l)] = + 255 - (tiles[tiles_idx].height_values[l][k] + 128); + //terrain->heightmap[i*TERRAIN_TILE_SAMPLING+k][j*TERRAIN_TILE_SAMPLING+l] = + // 255 - (tiles[tiles_idx].height_values[l][k] + 128); + } } } } } } + +static void processHeighmapToVertices(T_TERRAIN* terrain, const float h_scale) { + unsigned int i,j,k; + float w_half,h_half,_h_scale; + + if (terrain->heightmap == NULL) return; + + terrain->verticesmap_size = terrain->width * TERRAIN_TILE_SAMPLING * terrain->height * TERRAIN_TILE_SAMPLING; + w_half = terrain->width * 2; // Terrain center defined as mesh center -- sould not be used for game application + h_half = terrain->height * 2; + _h_scale = h_scale * 2.0 / 10.0; // Convert read scale to display scale, don't known the real operation in game + + // Build vertex list of the terrain + terrain->verticesmap = (T_VERTEX *)malloc(terrain->verticesmap_size * sizeof(T_VERTEX)); + for ( j = 0; j < terrain->height * TERRAIN_TILE_SAMPLING; j++ ) { + for ( i = 0; i < terrain->width * TERRAIN_TILE_SAMPLING; i++ ) { + k = j * terrain->width * TERRAIN_TILE_SAMPLING + i; + terrain->verticesmap[k].x = TERRAIN_MESH_SCALE * (-w_half + i); + terrain->verticesmap[k].z = TERRAIN_MESH_SCALE * (-h_half + j); + terrain->verticesmap[k].y = terrain->heightmap[i][j] * _h_scale; + } + } +} + + + + + + diff --git a/src/hmp_struct.h b/src/hmp_struct.h index b82fe79..d30ddf1 100644 --- a/src/hmp_struct.h +++ b/src/hmp_struct.h @@ -34,6 +34,8 @@ typedef struct terrain { unsigned short height; unsigned char** heightmap; + + unsigned int verticesmap_size; T_VERTEX* verticesmap; } T_TERRAIN ; diff --git a/src/options.h b/src/options.h index 5376a2c..c619957 100644 --- a/src/options.h +++ b/src/options.h @@ -10,7 +10,10 @@ #define OPTIONS_H_ // Number of height values to take for computing terrain (default: 4) -#define TILE_HEIGHT_TUNE 4 +#define TERRAIN_TILE_SAMPLING 4 + +// Scale value for vertex grid constructor (default: 0.1) +#define TERRAIN_MESH_SCALE 0.1 /// Options structure typedef union u_prog_options {