RSE-Model/RSEModel/src/obj_exporter.c

169 lines
5.8 KiB
C

/**
* @file obj_exporter.c
* @date 27/07/2022
* @author JackCarterSmith
* @copyright GPL-v3.0
* @brief Export datas to Waveform OBJ format.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "options.h"
#include <RSPModel_datatypes.h>
#include <RSPModel_errordefs.h>
#include "rlk/obj.h"
#include "obj_exporter.h"
static void mtlPathPatch(const char* out_file, const char* obj_name) ;
unsigned char exportOBJModel(T_RSPMODEL_OBJECT* hob_objects, const char *out_path, T_PROG_OPTIONS* p_opts) {
char objExport_path[128];
char mtlExport_path[128];
obj* objConstruct = NULL;
unsigned int i,j;
int surfID = 0, materialID = 0, tmpVertex = 0, tmpIndex = 0;
int indexOffset = 0; // Used to compensate reset of indices between face group
float vertexBuff[3] = {0}, textureBuff[2] = {0};
int indicesBuff[3] = {0};
if (hob_objects == NULL || out_path == NULL)
return RSPLIB_ERROR_ARGS_NULL;
if (p_opts->output_dir) {
strcpy(objExport_path, out_path);
#ifdef _WIN32
strcat(objExport_path, "-out\\");
#else
strcat(objExport_path, "-out/");
#endif
strcat(objExport_path, hob_objects->name);
} else {
strcpy(objExport_path, hob_objects->name);
}
strcpy(mtlExport_path, objExport_path);
strcat(objExport_path, ".obj\0");
strcat(mtlExport_path, ".mtl\0");
objConstruct = obj_create(NULL);
// Build face/surface material group
for ( i = 0; i < hob_objects->face_group_count; i++) {
surfID = obj_add_surf(objConstruct);
materialID = obj_add_mtrl(objConstruct);
// Build vertex container
for ( j = 0; j < hob_objects->object_parts[i].vertex_count; j++ ) {
tmpVertex = obj_add_vert(objConstruct);
vertexBuff[0] = ((float)1/1024) * -hob_objects->object_parts[i].vertices[j].x; // Invert X to fix mirror display
vertexBuff[1] = ((float)1/1024) * -hob_objects->object_parts[i].vertices[j].y; // Invert Y to render upside up
vertexBuff[2] = ((float)1/1024) * hob_objects->object_parts[i].vertices[j].z;
obj_set_vert_v(objConstruct, tmpVertex, vertexBuff);
}
// Build indices container and UV mapping
for ( j = 0; j < hob_objects->object_parts[i].face_count; j++ ) {
tmpIndex = obj_add_poly(objConstruct, surfID);
indicesBuff[0] = indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[0];
indicesBuff[1] = indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[1];
indicesBuff[2] = indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[2];
obj_set_poly(objConstruct, surfID, tmpIndex, indicesBuff);
if (hob_objects->object_parts[i].faces[j].flags_bits.fHasTexture) {
textureBuff[0] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[0].u;
textureBuff[1] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[0].v;
obj_set_vert_t(objConstruct, indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[0], textureBuff);
textureBuff[0] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[1].u;
textureBuff[1] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[1].v;
obj_set_vert_t(objConstruct, indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[1], textureBuff);
textureBuff[0] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[2].u;
textureBuff[1] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[2].v;
obj_set_vert_t(objConstruct, indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[2], textureBuff);
}
// Process 2 triangles if face is Quad
if (hob_objects->object_parts[i].faces[j].flags_bits.fIsQuad) {
tmpIndex = obj_add_poly(objConstruct, surfID);
indicesBuff[0] = indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[0];
indicesBuff[1] = indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[2];
indicesBuff[2] = indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[3];
obj_set_poly(objConstruct, surfID, tmpIndex, indicesBuff);
if (hob_objects->object_parts[i].faces[j].flags_bits.fHasTexture) {
textureBuff[0] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[3].u;
textureBuff[1] = ((float)1/4096) * hob_objects->object_parts[i].faces[j].tex_coords[3].v;
obj_set_vert_t(objConstruct, indexOffset + (int)hob_objects->object_parts[i].faces[j].indices[3], textureBuff);
}
}
}
indexOffset = obj_num_vert(objConstruct);
}
if (p_opts->export_mtl) {
obj_write(objConstruct, objExport_path, mtlExport_path, 8);
#if defined(__GNUC__) //TODO: review MSVC file management or include and rewrite obj lib?
if (p_opts->output_dir) mtlPathPatch(objExport_path, hob_objects->name);
#endif
} else obj_write(objConstruct, objExport_path, NULL, 8);
obj_delete(objConstruct);
return RSPLIB_SUCCESS;
}
static void mtlPathPatch(const char* out_file, const char* obj_name) {
FILE* obj = NULL;
char* memFile = NULL;
long fileSize,i,pos = 0,lines = 0;
char _path[128],b;
obj = fopen(out_file, "r");
if ( obj != NULL ) {
fseek(obj, 0, SEEK_END);
fileSize = ftell(obj);
fseek(obj, 0, SEEK_SET);
// Find the end of first line
for ( i = 0; i < fileSize + 1; i++) {
b = (char)fgetc(obj);
if (b == '\n') {
if (pos == 0) pos = i;
lines++;
}
}
// Prepare mtl path for output
strcpy(_path, obj_name);
strcat(_path, ".mtl");
memFile = malloc(fileSize - (pos + lines));
if ( memFile != NULL ) {
// Read the rest of file in memory
fseek(obj, pos, SEEK_SET);
fread(memFile, fileSize - (pos + lines), 1, obj);
fclose(obj);
// Begin rewrite file
obj = fopen(out_file, "w");
fprintf(obj, "mtllib %s", _path);
#if defined(_MSC_VER)
fwrite(memFile, fileSize - pos , 1, obj);
#elif defined(__GNUC__)
fwrite(memFile, fileSize - (pos + lines), 1, obj);
#endif
free(memFile);
}
fclose(obj);
}
}