Pre-Release master integration #1

Merged
JackCarterSmith merged 4 commits from develop into master 2022-08-03 22:31:49 +02:00
16 changed files with 804 additions and 289 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/rlk"]
path = src/rlk
url = https://github.com/rlk/obj.git

View File

@ -1,71 +1,85 @@
# CMakeLists.txt
# Written by JackCarterSmith, 2021
####################################################
# Written by JackCarterSmith, 2022
# This code is released under the RSE license.
####################################################
cmake_minimum_required(VERSION 3.1)
cmake_policy(VERSION 3.1)
# CMake requirement and general configuration
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)
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})
else()
project(rse-map VERSION 1.0.0 DESCRIPTION "RogueSquadron Extractor - Map" LANGUAGES C)
set(RSE_MAP_NAME RSE_Map-${PROJECT_VERSION})
if(DEFINED ENV{MS_COMPATIBLE})
set(CMAKE_GNUtoMS ON) # Enable compatibility level to exported libraries
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/config.h @ONLY)
include(CheckIncludeFile)
include(CheckCSourceCompiles)
# needed packages
add_definitions(-DCONF_NO_GL) # Used for obj-lib to not compile GL part
#add_definitions(-DNO_PNG_SUPPORT) # Can be used to disable code support for PNG exporting
# Project definition
if(DEFINED ENV{CI}) # Jenkins CI integration mode
project(rse-terrain VERSION $ENV{CI_VERSION}.$ENV{CI_BUILD_NUMBER} DESCRIPTION "RogueSquadron Extractor - Terrain" LANGUAGES C)
set(RSE_TER_NAME $ENV{CI_OUTPUT_NAME}-${PROJECT_VERSION})
else() # Standalone project mode, should not be used for release.
project(rse-terrain VERSION 1.0.0 DESCRIPTION "RogueSquadron Extractor - Terrain" LANGUAGES C)
set(RSE_TER_NAME RSE_Terrain-${PROJECT_VERSION})
endif()
# Push compile infos to source
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/config.h @ONLY)
# Import needed packages and references their include path
find_package(ZLIB 1.2.11 EXACT REQUIRED)
include_directories(${ZLIB_INCLUDE_DIR})
find_package(PNG 1.6.37 EXACT REQUIRED)
include_directories(${PNG_INCLUDE_DIR})
#find_package(GLEW REQUIRED)
#find_package(GLEW REQUIRED) # Enable when GL rendering is ready
#include_directories(${GLEW_INCLUDE_DIR})
# define src/headers files
FILE(GLOB_RECURSE RSE_MAP_SRCS src/*.c)
FILE(GLOB_RECURSE RSE_MAP_HRDS src/*.h)
SOURCE_GROUP("Source Files" FILES ${RSE_MAP_SRCS})
SOURCE_GROUP("Header Files" FILES ${RSE_MAP_HRDS})
# Define src/headers files
FILE(GLOB_RECURSE RSE_TER_SOURCES src/*.c)
FILE(GLOB_RECURSE RSE_TER_HEADERS src/*.h)
SOURCE_GROUP("Source Files" FILES ${RSE_TER_SOURCES})
SOURCE_GROUP("Header Files" FILES ${RSE_TER_HEADERS})
# begin building RSE-Map
#set(CMAKE_BUILD_TYPE Debug)
# Building instructions for RSE-Terrain
if(DEFINED ENV{RSE-WS})
set(CMAKE_BUILD_TYPE DEBUG)
endif()
#include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(rse-map ${RSE_MAP_SRCS} ${RSE_MAP_HRDS})
#set_property(TARGET rse-map PROPERTY C_STANDARD 99)
set_target_properties(rse-map PROPERTIES OUTPUT_NAME ${RSE_MAP_NAME})
add_executable(rse-terrain ${RSE_TER_SOURCES} ${RSE_TER_HEADERS}) # Set the inputs for the compiler (srcs&hrds)
set_property(TARGET rse-terrain PROPERTY C_STANDARD 90)
set_target_properties(rse-terrain PROPERTIES OUTPUT_NAME ${RSE_TER_NAME}) # Define the executable file name
# Link externals libraries to the linker
if(MSVC)
# msvc does not append 'lib' - do it here to have consistent name
#set_target_properties(rse-map PROPERTIES PREFIX "lib")
set_target_properties(rse-map PROPERTIES IMPORT_PREFIX "lib")
endif()
if(MSVC)
target_link_libraries(rse-map ${ZLIB_LIBRARIES} ${PNG_LIBRARIES})
#set_target_properties(rse-terrain PROPERTIES PREFIX "lib")
set_target_properties(rse-terrain PROPERTIES IMPORT_PREFIX "lib")
target_link_libraries(rse-terrain ${ZLIB_LIBRARIES} ${PNG_LIBRARIES} ${GLEW_LIBRARIES})
else()
target_link_libraries(rse-map ${ZLIB_LIBRARIES} ${PNG_LIBRARIES} m)
target_link_libraries(rse-terrain ${ZLIB_LIBRARIES} ${PNG_LIBRARIES} ${GLEW_LIBRARIES} m)
endif()
# add GPG signature command
# GPG signature custom command
#add_custom_command(
# OUTPUT ""
# COMMAND gpg --batch --detach-sign
# -o ${RSE_MAP_NAME}_${CI_SYS_TARGET}.gpg
# ${RSE_MAP_NAME}
# DEPENDS ${RSE_MAP_NAME}
# -o ${RSE_TER_NAME}_${CI_SYS_TARGET}.gpg
# ${RSE_TER_NAME}
# DEPENDS ${RSE_TER_NAME}
# VERBATIM
#)
# install executable
install(TARGETS rse-map
# Install project executable
install(TARGETS rse-terrain
RUNTIME DESTINATION bin
)

View File

@ -1,14 +1,71 @@
# RogueSquadron Extractor - MAP module
# RogueSquadron Extractor - TERRAIN module
Inspired by the work of **dpethes** (https://github.com/dpethes/rerogue) :clap:
This set of git repos are a compilation of tools coded in C to make RS modding far more than a dream!
The collection consist of few independants modules, each of them deals with specific data like sound, textures, heightmaps, etc...
All modules are independants. This is the **'MAP'** module.
All modules are independants. This is the **'TERRAIN'** module.
:exclamation: **Master branch is ugly for now and should not be used, please take only released versions.** :exclamation:
[![Build Status](https://ci.jcsmith.fr/job/JCS-Prod/job/RSE-Terrain/job/master/badge/icon)](https://ci.jcsmith.fr/job/JCS-Prod/job/RSE-Terrain/job/master/)
## MAP MODULE
## TERRAIN MODULE
It's extract texture datas from Rogue Squadron 3D (PC) game map files.
It's extract terrain datas from Rogue Squadron 3D (PC) game map files (hmp).
This module can do:
- Interpolate 3D model mesh of the terrain,
- Export it as OBJ model file (pretty heavy (~15MB), not to be use direcly for display),
- Export an heightmap-like PNG,
- Multiple inputs files.
## TODO
- Add textures to terrain.
- Adding LOD method to optimize datas manipulation and rendering.
- Rewrite OBJ lib...
- Discover all last unknowns fields, etc.
### Using
`RSE-Terrain_"version" [options] <hob files...>` or you can simply drag&drop HOB files on it.
A futur main program can extract all HOB files directly from DAT file.
Due to issue with copyrights, I can't provide samples... You need to extract HOB files yourself.
<img src="https://repo.jcsmith.fr/pictures/rse-terrain.gif" width="620" height="400" />
### Options
- -h Print this message
- -v,-vv Activate verbose/debug output mode respectively
- -subdir Export outputs to a sub-directory
- -neg Negative heightmap output
### Dependencies
- obj-lib: as obj file exporter. (https://github.com/rlk/obj)
### Compiling
:mega: **MSVC compatibility is in progress. Not working for now but you can try to fix error by yourself.**
You can compile on both Windows (MinGW) or native Linux system thanks to CMake.
To compile, just clone (**don't forget git submodules**) and launch cmake:
```shell
cmake .
make
make install
```
We can also use cross-compilation (after installing `mingw64` and `cmake` packages on your distrib):
```shell
mkdir build && cd build
cmake -DGNU_HOST=x86_64-w64-mingw32 \
-DCMAKE_TOOLCHAIN_FILE=../mingw_cross_toolchain.cmake \
..
cmake --build .
```

View File

@ -1,72 +0,0 @@
#include "Image_Exporter.h"
int saveToPNG(RS_IMAGE *img, char *tex_path, char *hmt_fileName) {
if (tex_path == NULL || img == NULL) return EXIT_FAILURE;
char export_path[128];
FILE *_png_f = NULL;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
size_t x,y;
png_byte **row_ptrs = NULL;
PIXEL_A *pixel = NULL;
//int pixel_size = 3;
//int depth = 8; //bit par color channel (RGB)
if (_options & OUTPUT_DIR) {
strcpy(export_path, hmt_fileName);
#ifdef _WIN32
strcat(export_path, "-out\\");
#else
strcat(export_path, "-out/");
#endif
strcat(export_path, tex_path);
} else {
strcpy(export_path, tex_path);
}
strcat(export_path, ".png");
_png_f = fopen(export_path, "wb");
if (_png_f == NULL) return EXIT_FAILURE;
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(_png_f);
return EXIT_FAILURE;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(_png_f);
return EXIT_FAILURE;
}
// Set image attributes
png_set_IHDR(png_ptr, info_ptr, img->width, img->height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
// Init PNG datas
row_ptrs = png_malloc(png_ptr, img->height * sizeof(png_byte *));
for (y=0; y<img->height; y++) {
png_byte *row = png_malloc(png_ptr, img->width*sizeof(PIXEL_A));
row_ptrs[y] = row;
for (x=0; x<img->width; x++) {
pixel = pixelAt(img, x , y);
if(pixel == NULL) return EXIT_FAILURE;
*row++ = pixel->_red;
*row++ = pixel->_green;
*row++ = pixel->_blue;
*row++ = pixel->_alpha;
}
}
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);
for (y=0; y<img->height; y++) {
png_free(png_ptr, row_ptrs[y]);
}
png_free(png_ptr, row_ptrs);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(_png_f);
return EXIT_SUCCESS;
}

View File

@ -1,12 +0,0 @@
#ifndef IMAGE_EXPORTER_H_
#define IMAGE_EXPORTER_H_
#include "options.h"
#include "RS_images.h"
#include <zlib.h>
#include <png.h>
int saveToPNG(RS_IMAGE *img, char *tex_name, char *hmt_fileName);
#endif

View File

@ -1,133 +0,0 @@
/*
================================================================================
Name : Map-Extractor.c
Author : JackCarterSmith
License : GPL-v3.0
Description : DAT textures extractor to PNG format with enhanced function in C
================================================================================
*/
#include "Map-Extractor.h"
int _options; // Global options settings variable
int main(int argc, char *argv[]) {
// Init buffer vars
HMT_FILE *hmt_fdatas = NULL;
int file_index;
printf("\n*** RogueSquadron Extractor (RSE) - MAP module - v%s ***\n", VERSION);
// Check if filenames arguments exist
if (argc < 2) {
printf("\n[ERR] No input file/commands specified!\n");
dispHelp();
return EXIT_FAILURE; //TODO: implement own error codes system
}
_options = checkArgs(argv, argc); // Analyse program arguments
if (_options == -1) return EXIT_SUCCESS;
// Do the work
for (file_index=(_options >> 8) & 0xFF; file_index<argc; file_index++) { // Manage multiple inputs files
hmt_fdatas = extractDatasFromHMT(argv[file_index]);
if (hmt_fdatas == NULL) return EXIT_FAILURE;
if (exportTextures(hmt_fdatas, argv[file_index]) == EXIT_FAILURE) return EXIT_FAILURE;
purgeHMTFromMemory(hmt_fdatas); // Clean up memory (because I'm a good boy)
}
return EXIT_SUCCESS;
}
int checkArgs(char *args[], int arg_nbr) {
int _o = 0x0002; // Default options parameters
char test[256];
int i;
if (arg_nbr > 1) {
for (i=1; i<arg_nbr; i++) {
strcpy(test, args[i]);
if (args[i][0] != '-') break;
if (strcmp(args[i], "-h") == 0) {
dispHelp();
return -1;
} else if (strcmp(args[i], "-v") == 0) {
_o |= VERBOSE_ENABLED;
printf("[OPTN] Verbose enabled.\n");
} else if (strcmp(args[i], "-no-subdir") == 0) {
_o &= ~OUTPUT_DIR;
printf("[OPTN] Extract to current directory.\n");
} else {
printf("[ERR] Unknown option: %s\n", args[i]);
}
}
_o = (i << 8) | (_o & 0x00FF);
}
return _o;
}
void createSubDir(char *dirName) {
if (dirName == NULL) return;
char _dir[260]; //TODO: Change directory management
strcpy(_dir, dirName);
strcat(_dir, "-out");
#ifdef _WIN32
CreateDirectory(_dir, NULL);
#else
mkdir(_dir, 0755);
#endif
}
HMT_FILE *extractDatasFromHMT(char *hmt_filename) {
FILE *_hmtFile = NULL;
HMT_FILE *hmt_fdatas = NULL;
_hmtFile = fopen(hmt_filename, "rb");
if (_hmtFile != NULL) {
printf("\n=============================================\n[INFO] - Parsing file: %s ...\n", hmt_filename);
hmt_fdatas = parseHMTFile(_hmtFile);
if (hmt_fdatas == NULL) printf("[ERR] Failed to parse datas from %s\n", hmt_filename);
} else {
printf("\n[ERR] Input file %s not found!\n", hmt_filename);
}
fclose(_hmtFile);
return hmt_fdatas;
}
int exportTextures(HMT_FILE *hmt_f, char *filename) {
int i;
if(hmt_f->texture_count > 0) {
if (_options & OUTPUT_DIR) createSubDir(filename);
for (i=0; i<hmt_f->texture_count; i++) {
switch (hmt_f->textures_list[i].image.type_) {
case 0:
case 1:
case 3:
case 4:
case 5:
if (saveToPNG(&(hmt_f->textures_list[i].image), hmt_f->textures_list[i].name, filename)) {
printf("[ERR] Failed saving image file: %s\n", hmt_f->textures_list[i].name);
return EXIT_FAILURE;
} else printf("[INFO] Saved image file: %s\n", hmt_f->textures_list[i].name);
break;
default:
printf("[WARN] Can't export %s ! Image type %d not currently supported! (WIP)\n", hmt_f->textures_list[i].name, hmt_f->textures_list[i].image.type_);
}
}
}
return EXIT_SUCCESS;
}
void dispHelp() {
printf("\n");
printf("Options:\n -h Print this message\n -v Activate verbose console output\n -no-subdir Extract textures inside current folder\n");
printf("\n");
printf("Usage: RSE-Texture_%s [options] <hmt files...>\n", VERSION);
printf("\n");
}

View File

@ -1,26 +0,0 @@
#ifndef MAP_EXTRACTOR_H_
#define MAP_EXTRACTOR_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "config.h"
#include "options.h"
#include "HMT_Parser.h"
#include "RS_images.h"
#include "Image_Exporter.h"
void createSubDir(char *dirName);
int checkArgs(char *args[], int arg_nbr);
HMT_FILE *extractDatasFromHMT(char* hmt_filename);
int exportTextures(HMT_FILE *hmt_f, char *filename);
void dispHelp();
#endif

168
src/Terrain-Extractor.c Normal file
View File

@ -0,0 +1,168 @@
/**
* \file Terrain-Extractor.c
* \date 31/07/2022
* \author JackCarterSmith
* \copyright GPL-v3.0
* \brief Terrain file (hmp) parser with option to export to both Waveform OBJ format and grey-scale PNG heightmap.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "errors_types.h"
#include "config.h"
#include "options.h"
#include "hmp_struct.h"
#include "hmp_parser.h"
#include "hmp_export.h"
/*
* Internal functions declarations
*/
static unsigned int mainProcess(int args_cnt, char* args_value[], T_PROG_OPTIONS* opt_ptr);
static void createSubDir(char *dirName);
static unsigned char checkInputArgs(T_PROG_OPTIONS* opt_ptr, int p_arg_nbr, char* p_args[]);
static void dispHelp();
/*
* - MAIN -
*/
int main(int argc, char *argv[]) {
T_PROG_OPTIONS _opts;
unsigned char p;
// Hello world!
printf("\n*** RogueSquadron Extractor (RSE) - TERRAIN module - v%s ***\n", VERSION);
// Check for arguments
if (argc < 2) {
printf("\n[ERR] No input file/commands specified!\n");
dispHelp();
return ERROR_ARGS_NULL;
}
// Create options for programs according to user's arguments.
p = checkInputArgs(&_opts, argc, argv);
if ( p == ERROR_GENERIC ) return NO_ERROR;
else if ( p != NO_ERROR ) return p;
return mainProcess(argc, argv, &_opts);
}
/*
* Private functions definition
*/
static unsigned int mainProcess(int args_cnt, char* args_value[], T_PROG_OPTIONS* p_opts) {
unsigned short file_index;
T_TERRAIN* terrainStruct = NULL;
// Manage multiple inputs files
for ( file_index = p_opts->input_files_cnt; file_index < args_cnt; file_index++)
{
printf("\n=============================================\n[INFO] - Parsing file: %s ...\n", args_value[file_index]);
terrainStruct = calloc(1, sizeof(T_TERRAIN));
// Parse data from HOB file and put in T_HOB structure.
if (parseHMPFile(args_value[file_index], terrainStruct, p_opts) != NO_ERROR) {
printf("[ERR] Failed to parse datas from %s\n", args_value[file_index]);
free(terrainStruct);
return ERROR_PROCESS;
}
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");
#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);
}
return NO_ERROR;
}
static unsigned char checkInputArgs(T_PROG_OPTIONS* opt_ptr, int p_arg_nbr, char* p_args[]) {
char test[256];
int i;
// Set default options
opt_ptr->raw = 0;
if (p_arg_nbr > 1) {
for ( i = 1; i < p_arg_nbr; i++) {
strcpy(test, p_args[i]);
if (p_args[i][0] != '-') break;
if (strcmp(p_args[i], "-h") == 0) {
dispHelp();
return ERROR_GENERIC;
} else if (strcmp(p_args[i], "-v") == 0) {
opt_ptr->verbose_mode = 1;
printf("[OPTN] Verbose enabled.\n");
} else if (strcmp(p_args[i], "-vv") == 0) {
opt_ptr->verbose_mode = 1;
opt_ptr->debug_mode = 1;
printf("[OPTN] Debug enabled.\n");
} else if (strcmp(p_args[i], "-vvv") == 0) {
opt_ptr->verbose_mode = 1;
opt_ptr->debug_mode = 1;
opt_ptr->god_mode = 1;
printf("[OPTN] God damn it!\n");
} else if (strcmp(p_args[i], "-subdir") == 0) {
opt_ptr->output_dir = 0;
printf("[OPTN] Export to sub-directory.\n");
} else if (strcmp(p_args[i], "-neg") == 0) {
opt_ptr->inverted_HM = 1;
printf("[OPTN] Negative heightmap output mode.\n");
} else {
printf("[ERR] Unknown option: %s\n", p_args[i]);
}
}
opt_ptr->input_files_cnt = i;
return NO_ERROR;
}
return ERROR_ARGS_NULL;
}
static void createSubDir(char *dirName) {
if (dirName == NULL) return;
char _dir[260]; //TODO: Change directory management
strcpy(_dir, dirName);
strcat(_dir, "-out");
#ifdef _WIN32
CreateDirectory(_dir, NULL);
#else
mkdir(_dir, 0755);
#endif
}
static void dispHelp() {
printf("\n");
printf("Options:\n -h Print this message\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] <hmp_file>\n", VERSION);
printf("\n");
}

28
src/errors_types.h Normal file
View File

@ -0,0 +1,28 @@
/*
* errors_types.h
*
* Created on: 26 juil. 2022
* Author: JackCarterSmith
*/
//#include "error.h" //TODO: use it as base for error ID
#ifndef SRC_ERRORS_TYPES_H_
#define SRC_ERRORS_TYPES_H_
#ifdef NO_ERROR
#undef NO_ERROR
#endif
#define NO_ERROR 0
#define ERROR_GENERIC 1
#define ERROR_MEMORY 2
#define ERROR_IO 3
#define ERROR_PROCESS 4
#define ERROR_ARGS_NULL 10
#define ERROR_ARGS_RANGE 11
#define ERROR_REALITY_BROKED -1
#endif /* SRC_ERRORS_TYPES_H_ */

156
src/hmp_export.c Normal file
View File

@ -0,0 +1,156 @@
/**
* \file hmp_export.c
* \date 02/08/2022
* \author JackCarterSmith
* \copyright GPL-v3.0
* \brief Export datas to heightmap PNG and Waveform OBJ format.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef NO_PNG_SUPPORT
#include <zlib.h>
#include <png.h>
#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,z;
png_byte **row_ptrs = NULL;
//int pixel_size = 3;
//int depth = 8; //bit par color channel (RGB)
strcpy(export_path, out_path);
if (p_opts->output_dir) {
#ifdef _WIN32
strcat(export_path, "-out\\");
#else
strcat(export_path, "-out/");
#endif
strcat(export_path, "heightmap.png");
} else {
strcat(export_path, "-heightmap.png");
}
_png_f = fopen(export_path, "wb");
if (_png_f == NULL) return ERROR_MEMORY;
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(_png_f);
return ERROR_MEMORY;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(_png_f);
return ERROR_MEMORY;
}
// Set image attributes
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 * 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];
}
}
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);
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 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;
}

17
src/hmp_export.h Normal file
View File

@ -0,0 +1,17 @@
/**
* \file hmp_export.h
* \date 02/08/2022
* \author JackCarterSmith
* \copyright GPL-v3.0
* \brief Export datas to heightmap PNG and Waveform OBJ format.
*/
#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_ */

180
src/hmp_parser.c Normal file
View File

@ -0,0 +1,180 @@
/**
* \file hmp_parser.c
* \date 31/07/2022
* \author JackCarterSmith
* \copyright GPL-v3.0
* \brief Decode terrain file (hmp) structure.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "errors_types.h"
#include "options.h"
#include "hmp_struct.h"
#include "hmp_parser.h"
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;
long fileSize;
FILE* fStream = NULL;
char* memFile = NULL;
float y_scale = 1.0;
unsigned int tiles_offset = 0;
T_TILE_INDICES* tiles_indices = NULL;
T_HMPFILE_TILE* tiles = NULL;
if (hmp_struct != NULL && fileName != NULL) {
// Open file
fStream = fopen(fileName, "rb");
if (fStream != NULL) {
// Determine file size in bytes
fseek(fStream, 0, SEEK_END);
fileSize = ftell(fStream);
fseek(fStream, 0, SEEK_SET);
if (p_opts->verbose_mode) printf("[DBG] > Input file size: %ld bytes\n\n", fileSize);
memFile = malloc(fileSize + 1);
if (memFile != NULL) {
// Copy file in RAM
fread(memFile, fileSize, 1, fStream);
fclose(fStream);
// Get header infos
y_scale = ((T_HMPFILE_HEADER *)memFile)->height_scale;
tiles_offset = ((T_HMPFILE_HEADER *)memFile)->tiles_start_offset;
hmp_struct->width = ((T_HMPFILE_HEADER *)memFile)->width_BLK;
hmp_struct->height = ((T_HMPFILE_HEADER *)memFile)->height_BLK;
if (p_opts->verbose_mode) {
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) {
printf("[DBG] > Unknown0: %d\n", ((T_HMPFILE_HEADER *)memFile)->unknown0);
printf("[DBG] > Unknown1: %d\n", ((T_HMPFILE_HEADER *)memFile)->unknown1);
printf("\n");
}
// Get tiles indices
tiles_indices = malloc(hmp_struct->width * hmp_struct->height * sizeof(unsigned short));
memcpy(tiles_indices, memFile + sizeof(T_HMPFILE_HEADER),
hmp_struct->width * hmp_struct->height * sizeof(unsigned short));
// Get tiles datas
tiles = malloc(((T_HMPFILE_HEADER *)memFile)->tiles_count * sizeof(T_HMPFILE_TILE));
memcpy(tiles, memFile + tiles_offset, ((T_HMPFILE_HEADER *)memFile)->tiles_count * sizeof(T_HMPFILE_TILE));
// Convert tiles datas to raw heightmap
processTilesToHeightmap(hmp_struct, tiles_indices, tiles, p_opts->inverted_HM);
// Convert tiles datas to terrain vertices
processHeighmapToVertices(hmp_struct, y_scale);
free(tiles);
free(tiles_indices);
free(memFile);
} else {
fclose(fStream);
err = ERROR_MEMORY;
printf("[ERR] Can't allocate enough memory for file processing!\n");
}
} else {
err = ERROR_IO;
printf("[ERR] Input file %s not found!\n", fileName);
}
} else err = ERROR_ARGS_NULL;
return err;
}
void cleanUpResources(T_TERRAIN* terrain) {
unsigned int i;
if (terrain == NULL) return;
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 inv_output) {
T_TILE_INDICES tiles_idx;
unsigned int i,j,k,l;
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 *));
for ( i = 0; i < heightmap_size_w; i++ ) {
terrain->heightmap[i] = (unsigned char *)malloc(heightmap_size_h * sizeof(unsigned char));
}
// Select tile
for ( i = 0; i < terrain->width; i++ ) {
for ( j = 0; j < terrain->height; j++ ) {
tiles_idx = tiles_indices[j * terrain->width + i];
// Get the 5x5 bytes height values for this tile
for ( k = 0; k < TERRAIN_TILE_SAMPLING; k++ ) {
for ( l = 0; l < TERRAIN_TILE_SAMPLING; l++ ) {
if (inv_output) {
// 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;
}
}
}

18
src/hmp_parser.h Normal file
View File

@ -0,0 +1,18 @@
/**
* \file hmp_parser.h
* \date 31/07/2022
* \author JackCarterSmith
* \copyright GPL-v3.0
* \brief Decode terrain file (hmp) structure.
*/
#include "hmp_struct.h"
#ifndef SRC_HOB_PARSER_H_
#define SRC_HOB_PARSER_H_
unsigned char parseHMPFile(const char* fileName, T_TERRAIN* hmp_struct, T_PROG_OPTIONS* p_opts);
void cleanUpResources(T_TERRAIN* terrain);
#endif /* SRC_HOB_PARSER_H_ */

87
src/hmp_struct.h Normal file
View File

@ -0,0 +1,87 @@
/*
* hmp_struct.h
*
* Created on: 31 juil. 2022
* Author: JackCarterSmith
*/
#ifndef SRC_HMP_STRUCT_H_
#define SRC_HMP_STRUCT_H_
/*
* long = 64bits??? (8 Bytes)
* int = 32bits (4 Bytes)
* short = 16bits (2 Bytes)
* car = 8bits (1 Bytes)
*/
#if defined(_MSC_VER)
#define PACK
#elif defined(__GNUC__)
#define PACK __attribute__((packed))
#endif
///////////////////////////////////////////////////////////////////////////////
// HMP file structure
///////////////////////////////////////////////////////////////////////////////
typedef struct vector3 { float x,y,z; } T_VECTOR3;
typedef T_VECTOR3 T_VERTEX;
typedef struct terrain {
unsigned short width; // Dimension of the height/vertices map
unsigned short height;
unsigned char** heightmap;
unsigned int verticesmap_size;
T_VERTEX* verticesmap;
} T_TERRAIN ;
///////////////////////////////////////////////////////////////////////////////
// Declaration of Memory Mapped Structure
// Caution: the place of variable is important for correct mapping!
///////////////////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
#pragma pack(push, 1)
#endif
typedef struct PACK hmpfile_header {
unsigned int reserved0; //12B of zeros
unsigned int reserved1;
unsigned int reserved2;
float reserved3; // Always 0x3F000000
float height_scale;
float reserved4; // Always 0x3F000000
unsigned short tiles_count;
unsigned short unknown0;
unsigned int tiles_start_offset;
unsigned int unknown1; // Offset to some datas?
unsigned short width_BLK;
unsigned short height_BLK;
} T_HMPFILE_HEADER;
typedef unsigned short T_TILE_INDICES;
typedef struct PACK hmpfile_tile {
unsigned short texmap_id;
unsigned char unknown0;
unsigned char low_height; // LOD application? Clipping? Terrain render quadrants?
unsigned char high_height;
unsigned char height_values[5][5]; // first and last row/column overlap with a neighboring tile, "glue" for tiles, need to be identical to avoid "hill" effect
} T_HMPFILE_TILE;
#if defined(_MSC_VER)
#pragma pack(pop)
#endif
#endif /* SRC_HMP_STRUCT_H_ */

View File

@ -1,9 +1,38 @@
/**
* \file options.h
* \date 29/07/2022
* \author JackCarterSmith
* \copyright GPL-v3.0
* \brief Shared options structure definition and declaration.
*/
#ifndef OPTIONS_H_
#define OPTIONS_H_
#define VERBOSE_ENABLED 0x0001
#define OUTPUT_DIR 0x0002
// Number of height values to take for computing terrain (default: 4)
#define TERRAIN_TILE_SAMPLING 4
extern int _options;
// Scale value for vertex grid constructor (default: 0.1)
#define TERRAIN_MESH_SCALE 0.1
#endif
/// Options structure
typedef union u_prog_options {
struct {
unsigned char verbose_mode:1; //!< Output simple details about ID and other "light" things.
unsigned char output_dir:1; //!< Export extracted datas to a sub-directory.
unsigned char inverted_HM:1; //!< Enable negative heightmap output.
unsigned char reserved0:6; //!< For future use.
unsigned char debug_mode:1; //!< Output all values of faces, indices and vertices and others "heavy" things.
unsigned char god_mode:1; //!< Dev only. Output experimental values.
unsigned char reserved1:6; //!< For future use.
unsigned short input_files_cnt; //!< Internal files counters.
};
unsigned int raw; //!< Raw options access for bit-masking or memory copy/compare.
} T_PROG_OPTIONS ;
#endif /* OPTIONS_H_ */

1
src/rlk Submodule

@ -0,0 +1 @@
Subproject commit 48a6916526d043691bb3f9e38676fbc99995da10