/** * @file terrain_export.c * @date 23/08/2022 * @author JackCarterSmith * @copyright GPL-v3.0 * @brief Export datas to heightmap PNG and Waveform OBJ format. * */ #include #include #include #include "options.h" #include #include #include #include #include "obj/obj.h" #include "terrain_export.h" unsigned char exportHeightmapPNG(const T_RSPTERRAIN_HEIGHTMAP* heightmap, const char *out_path, T_PROG_OPTIONS* p_opts) { char export_path[1024]; FILE *_png_f = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; size_t x,z; png_byte **row_ptrs = NULL; //int pixel_size = 3; //int depth = 8; //bit par color channel (RGB) if (heightmap == NULL || out_path == NULL) return RSPLIB_ERROR_ARGS_NULL; #ifdef _WIN32 if (p_opts->output_dir) snprintf(export_path, 1024, "%s-out\\heightmap.png", out_path); else snprintf(export_path, 1024, "%s-heightmap.png", out_path); #else if (p_opts->output_dir) snprintf(export_path, 1024, "%s-out/heightmap.png", out_path); else snprintf(export_path, 1024, "%s-heightmap.png", out_path); #endif // Open export file and add PNG header _png_f = fopen(export_path, "wb"); if (_png_f == NULL) return RSPLIB_ERROR_MEMORY; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(_png_f); return RSPLIB_ERROR_MEMORY; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(_png_f); return RSPLIB_ERROR_MEMORY; } // Set image attributes png_set_IHDR(png_ptr, info_ptr, heightmap->width * RSPTERRAINLIB_TILE_SAMPLING, heightmap->height * RSPTERRAINLIB_TILE_SAMPLING, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Store PNG datas in buffer row_ptrs = png_malloc(png_ptr, heightmap->height * RSPTERRAINLIB_TILE_SAMPLING * sizeof(png_byte *)); for ( z = 0; z < heightmap->height * RSPTERRAINLIB_TILE_SAMPLING; z++ ) { png_byte *row = png_malloc(png_ptr, heightmap->width * RSPTERRAINLIB_TILE_SAMPLING * sizeof(unsigned char) * 3); row_ptrs[z] = row; for ( x = 0; x < heightmap->width * RSPTERRAINLIB_TILE_SAMPLING; x++ ) { *row++ = heightmap->heightmap[x][z]; *row++ = heightmap->heightmap[x][z]; *row++ = heightmap->heightmap[x][z]; } } // Write pixels datas png_init_io(png_ptr, _png_f); png_set_rows(png_ptr, info_ptr, row_ptrs); png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); // Clean up for ( z = 0; z < heightmap->height * RSPTERRAINLIB_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 RSPLIB_SUCCESS; } unsigned char exportHeightmapOBJ(const T_RSPTERRAIN_MESH* terrain_mesh, const char *out_path, T_PROG_OPTIONS* p_opts) { char export_path[1024]; char objExport_name[128]; char mtlExport_name[128]; obj* objConstruct = NULL; unsigned int i,j; int surfID = 0; float vertexBuff[3] = {0}; int indicesBuff[3] = {0}; if (terrain_mesh == NULL || out_path == NULL) return RSPLIB_ERROR_ARGS_NULL; #ifdef _WIN32 if (p_opts->output_dir) { snprintf(export_path, 1024, "%s-out\\", out_path); snprintf(objExport_name, 128, "heightmap.obj"); snprintf(mtlExport_name, 128, "heightmap.mtl"); } else { snprintf(objExport_name, 128, "%s-heightmap.obj", out_path); snprintf(mtlExport_name, 128, "%s-heightmap.mtl", out_path); } #else if (p_opts->output_dir) { snprintf(export_path, 1024, "%s-out/", out_path); snprintf(objExport_name, 128, "heightmap.obj"); snprintf(mtlExport_name, 128, "heightmap.mtl"); } else { snprintf(objExport_name, 128, "%s-heightmap.obj", out_path); snprintf(mtlExport_name, 128, "%s-heightmap.mtl", out_path); } #endif 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_mesh->vertices_count; i++ ) { vertexBuff[0] = terrain_mesh->verticesmap[i].x; vertexBuff[1] = terrain_mesh->verticesmap[i].y; vertexBuff[2] = terrain_mesh->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 for ( j = 0; j < terrain_mesh->height - 1; j++ ) { for ( i = 0; i < terrain_mesh->width - 1; i++ ) { indicesBuff[0] = j * terrain_mesh->width + i; indicesBuff[1] = indicesBuff[0] + 1; indicesBuff[2] = indicesBuff[0] + terrain_mesh->width; 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); } } if (p_opts->export_mtl) { if (p_opts->output_dir) obj_write(objConstruct, objExport_name, mtlExport_name, export_path, 8); else obj_write(objConstruct, objExport_name, mtlExport_name, NULL, 8); } else { if (p_opts->output_dir) obj_write(objConstruct, objExport_name, NULL, export_path, 8); else obj_write(objConstruct, objExport_name, NULL, NULL, 8); } obj_delete(objConstruct); return NO_ERROR; }