hermespy-rt

Minimalistic signal processing ray-tracer in C
git clone https://git.ea.contact/hermespy-rt
Log | Files | Refs

scene_fromSionna.c (15825B)


      1 /* vim: set tabstop=2:softtabstop=2:shiftwidth=2:noexpandtab */
      2 
      3 #include "../inc/vec3.h" /* for Vec3 */
      4 #include "../inc/common.h" /* for IN, OUT, PERROR_CLEANUP_EXIT */
      5 #include "../inc/scene.h" /* for Scene, Mesh, Material, scene_save */
      6 #include "../inc/materials.h" /* for g_hrt_materials, MATERIAL_CONCRETE */
      7 
      8 #include <stdio.h> /* for FILE, fopen, fclose, fseek, fgets, sscanf, printf */
      9 #include <stdlib.h> /* for malloc */
     10 #include <stdint.h> /* for uint8_t, uint32_t */
     11 #include <string.h> /* for strncmp, strrchr, strcmp, snprintf, strcpy, strlen, strncpy, strchr, strstr */
     12 
     13 /**
     14  * Hardcoded scenes
     15  */
     16 
     17 /* Box */
     18 float mesh_box_vs[] = {
     19   5.f, 5.f, 0.f,
     20   -5.f, 5.f, 0.f,
     21   -5.f, -5.f, 0.f,
     22   5.f, -5.f, 0.f,
     23   5.f, 5.f, 5.f,
     24   -5.f, 5.f, 5.f,
     25   -5.f, -5.f, 5.f,
     26   5.f, -5.f, 5.f,
     27 };
     28 uint32_t mesh_box_is[] = {
     29   0, 1, 2,
     30   0, 2, 3,
     31   0, 4, 5,
     32   0, 5, 1,
     33   1, 5, 6,
     34   1, 6, 2,
     35   2, 6, 7,
     36   2, 7, 3,
     37   3, 7, 4,
     38   3, 4, 0,
     39   4, 7, 6,
     40   4, 6, 5,
     41 };
     42 Mesh mesh_box = {
     43   .num_vertices = 8,
     44   .vs = (Vec3*)mesh_box_vs,
     45   .num_triangles = 12,
     46   .is = (uint32_t*)mesh_box_is,
     47   .material_index = MATERIAL_CONCRETE,
     48   .velocity = {0.f, 0.f, 0.f},
     49 };
     50 Scene scene_box = {
     51   .num_meshes = 1,
     52   .meshes = &mesh_box,
     53 };
     54 const char* scene_box_filename = "box.xml";
     55 
     56 /* Simple reflector */
     57 float mesh_simpleReflector_vs[] = {
     58   -0.5f, -0.5f, 0.f,
     59   0.5f, -0.5f, 0.f,
     60   0.5f, 0.5f, 0.f,
     61   -0.5f, 0.5f, 0.f,
     62 };
     63 uint32_t mesh_simpleReflector_is[] = {
     64   0, 1, 2,
     65   0, 2, 3,
     66 };
     67 Mesh mesh_simpleReflector = {
     68   .num_vertices = 4,
     69   .vs = (Vec3*)mesh_simpleReflector_vs,
     70   .num_triangles = 2,
     71   .is = (uint32_t*)mesh_simpleReflector_is,
     72   .material_index = MATERIAL_CONCRETE,
     73   .velocity = {0.f, 0.f, 0.f},
     74 };
     75 Scene scene_simpleReflector = {
     76   .num_meshes = 1,
     77   .meshes = &mesh_simpleReflector,
     78 };
     79 const char* scene_simpleReflector_filename = "simple_reflector.xml";
     80 
     81 /**
     82  * Read a Sionna scene
     83  */
     84 
     85 /* Read a binary PLY file
     86  *
     87  * The PLY file have the following header:
     88  * ply
     89  * format binary_little_endian 1.0
     90  * element vertex <num_vertices>
     91  * property float x
     92  * property float y
     93  * property float z
     94  * property float s
     95  * property float t
     96  * element face <num_triangles>
     97  * property list uchar int vertex_index
     98  * end_header
     99  * 
    100  * \param filepath Path to the PLY file
    101  * \return The mesh
    102  */
    103 Mesh readPly(const char* filepath) {
    104   FILE* f = fopen(filepath, "rb");
    105   if (f == NULL)
    106     PERROR_CLEANUP_EXIT("Error: cannot open file", 8);
    107 
    108   char buf[256];
    109   Mesh mesh = {
    110     .num_vertices = 0,
    111     .vs = NULL,
    112     .num_triangles = 0,
    113     .is = NULL,
    114     .material_index = 0,
    115     .velocity = {0.f, 0.f, 0.f},
    116   };
    117 
    118   /* read header */
    119   while(fgets(buf, 256, f)) {
    120     if (!strncmp(buf, "end_header", 10))
    121       break;
    122     if (!strncmp(buf, "element vertex ", 15)) {
    123       if (sscanf(buf, "element vertex %u", &mesh.num_vertices) != 1)
    124         PERROR_CLEANUP_EXIT("Error: cannot read number of vertices", 8);
    125     }
    126     else if (!strncmp(buf, "element face ", 13))
    127       if (sscanf(buf, "element face %u", &mesh.num_triangles) != 1)
    128         PERROR_CLEANUP_EXIT("Error: cannot read number of triangles", 8);
    129   }
    130 
    131   /* check if vertex and face elements are found */
    132   if (mesh.num_vertices == 0 || mesh.num_triangles == 0)
    133     PERROR_CLEANUP_EXIT("Error: PLY element vertex or element face not found", 8);
    134   /* check if the numbers are not too big */
    135   else if (mesh.num_vertices > 1000000 || mesh.num_triangles > 1000000)
    136     PERROR_CLEANUP_EXIT("Error: PLY element vertex or element face too big", 8);
    137 
    138   /* allocate memory for vertices and faces */
    139   mesh.vs = (Vec3*)malloc(mesh.num_vertices * sizeof(Vec3));
    140   mesh.is = (uint32_t*)malloc(3 * mesh.num_triangles * sizeof(uint32_t));
    141 
    142   /* read vertices */
    143   for (uint32_t i = 0; i != mesh.num_vertices; ++i) {
    144     if (fread(&mesh.vs[i], sizeof(Vec3), 1, f) != 1)
    145       PERROR_CLEANUP_EXIT("Error: cannot read vertex", 8, mesh.vs, mesh.is);
    146     /* skip s and t */
    147     if (fseek(f, 8, SEEK_CUR) != 0)
    148       PERROR_CLEANUP_EXIT("Error: cannot skip s and t", 8, mesh.vs, mesh.is);
    149   }
    150 
    151   /* read faces */
    152   for (uint32_t i = 0; i != mesh.num_triangles * 3; i += 3) {
    153     uint8_t n;
    154     if (fread(&n, sizeof(uint8_t), 1, f) != 1)
    155       PERROR_CLEANUP_EXIT("Error: cannot read number of vertices in face", 8, mesh.vs, mesh.is);
    156     if (n != 3)
    157       PERROR_CLEANUP_EXIT("Error: face is not a triangle", 8, mesh.vs, mesh.is);
    158     if (fread(&mesh.is[i], sizeof(uint32_t), 3, f) != 3)
    159       PERROR_CLEANUP_EXIT("Error: cannot read face", 8, mesh.vs, mesh.is);
    160   }
    161 
    162   fclose(f);
    163   return mesh;
    164 }
    165 
    166 /** Read a scene config CSV file.
    167  * 
    168  * The CSV file must be a text file with the following format:
    169  * name,material_index,velocity_x,velocity_y,velocity_z
    170  * 
    171  * The file can have any number of lines except the first one, which is the header.
    172  * If a mesh is not in the CSV file,
    173  * it will have the default (0) material index and velocity.
    174  *
    175  * \param filepath Path to the CSV file
    176  * \param num_meshes Output number of meshes in the CSV file
    177  * \param mesh_filenames Output array of mesh filenames
    178  * \param material_indicies Output array of material indicies
    179  * \param velocity Output array of velocity vectors. Size [num_meshes]
    180  */
    181 void readCsv(
    182   IN const char* filepath,
    183   OUT uint32_t* csv_num_meshes,
    184   OUT char*** csv_mesh_filenames,
    185   OUT uint32_t** csv_material_indicies,
    186   OUT Vec3** csv_velocities
    187 )
    188 {
    189   FILE* f = fopen(filepath, "r");
    190   if (!f)
    191     PERROR_CLEANUP_EXIT("Error: cannot open file", 8);
    192 
    193   /* Check header */
    194   char buf[256];
    195   if (!fgets(buf, 256, f))
    196     PERROR_CLEANUP_EXIT("Error: cannot read header", 8);
    197   if (strncmp(buf, "name,material_index,velocity_x,velocity_y,velocity_z\n", 48))
    198     PERROR_CLEANUP_EXIT("Error: invalid header", 8);
    199 
    200   /* Count number of lines */
    201   *csv_num_meshes = 0;
    202   while (fgets(buf, 256, f))
    203     ++(*csv_num_meshes);
    204   if (*csv_num_meshes == 0) {
    205     fclose(f);
    206     return;
    207   }
    208   /* Return to the beginning of the file */
    209   rewind(f);
    210   /* Skip header */
    211   if (!fgets(buf, 256, f))
    212     PERROR_CLEANUP_EXIT("Error: cannot read header", 8);
    213 
    214   /* Allocate memory for the arrays */
    215   *csv_mesh_filenames = (char**)malloc(*csv_num_meshes * sizeof(char*));
    216   *csv_material_indicies = (uint32_t*)malloc(*csv_num_meshes * sizeof(uint32_t));
    217   *csv_velocities = (Vec3*)malloc(*csv_num_meshes * sizeof(Vec3));
    218 
    219   /* Read lines */
    220   for (uint32_t i = 0; i != *csv_num_meshes; ++i) {
    221     /* Read line */
    222     if (!fgets(buf, 256, f))
    223       PERROR_CLEANUP_EXIT("Error: cannot read line", 8);
    224     /* Parse line */
    225     char name[50];
    226     int rc = sscanf(
    227       buf,
    228       "%49[^,],%u,%f,%f,%f\n",
    229       name,
    230       &(*csv_material_indicies)[i],
    231       &(*csv_velocities)[i].x,
    232       &(*csv_velocities)[i].y,
    233       &(*csv_velocities)[i].z
    234     );
    235     if (rc != 4)
    236       PERROR_CLEANUP_EXIT("Error: cannot parse line", 8);
    237     /* Copy name */
    238     (*csv_mesh_filenames)[i] = (char*)malloc(strlen(name) + 1);
    239     if (!csv_mesh_filenames[i])
    240       PERROR_CLEANUP_EXIT("Error: cannot allocate memory for mesh filename", 8);
    241     strcpy((*csv_mesh_filenames)[i], name);
    242   }
    243 }
    244 
    245 /** Read a Sionna scene XML file.
    246  * 
    247  * Extracts each shape's name, file path and material.
    248  * 
    249  * \param filepath Path to the XML file
    250  * \param xml_num_meshes Output number of meshes in the XML file
    251  * \param xml_mesh_filepaths Output array of mesh file paths (null-terminated strings)
    252  * \param xml_mesh_names Output array of mesh names (null-terminated strings)
    253  * \param xml_mesh_materials Output array of mesh materials (null-terminated strings)
    254  */
    255 void readXml(
    256   IN const char* filepath,
    257   OUT uint32_t* xml_num_meshes,
    258   OUT char*** xml_mesh_filepaths,
    259   OUT char*** xml_mesh_names,
    260   OUT char*** xml_mesh_materials
    261 )
    262 {
    263   FILE* f = fopen(filepath, "r");
    264   if (f == NULL) 
    265     PERROR_CLEANUP_EXIT("Error: cannot open the xml file", 8);
    266 
    267   fseek(f, 0, SEEK_END);
    268   size_t fsize = (size_t)ftell(f);
    269   fseek(f, 0, SEEK_SET);
    270 
    271   char* buf = (char*)malloc(fsize + 1);
    272   if (!buf)
    273     PERROR_CLEANUP_EXIT("Error: cannot allocate memory for the xml file", 8);  
    274   if (fread(buf, 1, fsize, f) != fsize)
    275     PERROR_CLEANUP_EXIT("Error: cannot read the xml file", 8, buf);
    276   buf[fsize] = '\0';
    277   fclose(f);
    278 
    279   /* Count the amount of "<shape" entries */
    280   char** shape_positions = NULL;
    281   *xml_num_meshes = 0;
    282   char* pos = buf, *end;
    283   while ((pos = strstr(pos, "<shape")) != NULL) {
    284     ++*xml_num_meshes;
    285     shape_positions = (char**)realloc(shape_positions, *xml_num_meshes * sizeof(char*));
    286     if (!shape_positions)
    287       PERROR_CLEANUP_EXIT("Error: cannot reallocate memory for shape positions", 8, buf);
    288     shape_positions[*xml_num_meshes - 1] = pos;
    289     pos += 6;
    290   }
    291   if (*xml_num_meshes == 0)
    292     PERROR_CLEANUP_EXIT("Error: no shapes found in the xml file", 8, buf);
    293   if (!shape_positions)
    294     PERROR_CLEANUP_EXIT("Error: cannot allocate memory for shape positions", 8, buf);
    295 
    296   /* Allocate memory for the mesh data */
    297   *xml_mesh_filepaths = (char**)malloc(*xml_num_meshes * sizeof(char*));
    298   *xml_mesh_names = (char**)malloc(*xml_num_meshes * sizeof(char*));
    299   *xml_mesh_materials = (char**)malloc(*xml_num_meshes * sizeof(char*));
    300   if (!*xml_mesh_filepaths || !*xml_mesh_names || !*xml_mesh_materials)
    301     PERROR_CLEANUP_EXIT("Error: cannot allocate memory for mesh data", 8, buf);
    302 
    303   /* For each "<shape" entry, extract the name, file path and material */
    304   char *e_name, *e_filepath, *e_material;
    305   for (uint32_t i = 0; i != *xml_num_meshes; ++i) {
    306     pos = shape_positions[i] + 6;
    307 
    308     /* Find mesh name */
    309     if ((pos = strstr(pos, "name=\"")) == NULL)
    310       PERROR_CLEANUP_EXIT("Error: cannot find mesh name", 8,
    311         buf, shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    312     pos += 6;
    313     end = strchr(pos, '\"');
    314     if (!end)
    315       PERROR_CLEANUP_EXIT("Error: cannot find mesh name end", 8,
    316         buf, shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    317     e_name = (char*)malloc(end - pos + 1);
    318     if (!e_name)
    319       PERROR_CLEANUP_EXIT("Error: cannot allocate memory for mesh name", 8,
    320         buf, shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    321     strncpy(e_name, pos, end - pos);
    322     e_name[end - pos] = '\0';
    323 
    324     /* Find mesh file path */
    325     if (!(pos = strstr(pos, "<string name=\"filename\""))) 
    326       PERROR_CLEANUP_EXIT("Error: cannot find mesh file path", 8, buf, e_name,
    327         shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    328     if (!(pos = strstr(pos, "value=\"")))
    329       PERROR_CLEANUP_EXIT("Error: cannot find mesh file path value", 8, buf, e_name,
    330         shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    331     pos += 7;
    332     end = strchr(pos, '\"');
    333     if (!end)
    334       PERROR_CLEANUP_EXIT("Error: cannot find mesh file path end", 8, buf, e_name,
    335         shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    336     e_filepath = (char*)malloc(end - pos + 1);
    337     if (!e_filepath)
    338       PERROR_CLEANUP_EXIT("Error: cannot allocate memory for mesh file path", 8, buf, e_name,
    339         shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    340     strncpy(e_filepath, pos, end - pos);
    341     e_filepath[end - pos] = '\0';
    342 
    343     /* Find mesh material */
    344     if (!(pos = strstr(pos, "id=\"mat-itu_")))
    345       PERROR_CLEANUP_EXIT("Error: cannot find mesh material", 8, buf, e_name, e_filepath,
    346         shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    347     pos += 12;
    348     end = strchr(pos, '\"');
    349     if (!end)
    350       PERROR_CLEANUP_EXIT("Error: cannot find mesh material end", 8, buf, e_name, e_filepath,
    351         shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    352     e_material = (char*)malloc(end - pos + 1);
    353     if (!e_material)
    354       PERROR_CLEANUP_EXIT("Error: cannot allocate memory for mesh material", 8, buf, e_name, e_filepath,
    355         shape_positions, *xml_mesh_filepaths, *xml_mesh_names, *xml_mesh_materials);
    356     strncpy(e_material, pos, end - pos);
    357     e_material[end - pos] = '\0';
    358 
    359     /* Save mesh data */
    360     (*xml_mesh_filepaths)[i] = e_filepath;
    361     (*xml_mesh_names)[i] = e_name;
    362     (*xml_mesh_materials)[i] = e_material;
    363   }
    364 
    365   free(shape_positions);
    366   free(buf);
    367 }
    368 
    369 /* Read a Sionna .xml scene. Assumes meshes are in a "meshes" directory.
    370  *
    371  * \param filepath Path to the .xml scene file
    372  * \param scene The scene to fill
    373  */
    374 void readScene(const char* filepath, Scene* scene) {
    375   /* Check if filepath ends with ".xml" */
    376   size_t filepath_len = strlen(filepath);
    377   if (filepath_len > 4 && strcmp(filepath + filepath_len - 4, ".xml") != 0)
    378     PERROR_CLEANUP_EXIT("Error: scene file must end with .xml", 8);
    379 
    380   /* Read the xml file */
    381   uint32_t xml_num_meshes;
    382   char** xml_mesh_filepaths;
    383   char** xml_mesh_names;
    384   char** xml_mesh_materials;
    385   readXml(
    386     filepath,
    387     &xml_num_meshes,
    388     &xml_mesh_filepaths,
    389     &xml_mesh_names,
    390     &xml_mesh_materials
    391   );
    392 
    393   /* Read the scene config CSV file */
    394   char* csv_path = (char*)malloc(filepath_len + 1);
    395   if (!csv_path)
    396     PERROR_CLEANUP_EXIT("Error: cannot allocate memory for csv_path", 8);
    397   strcpy(csv_path, filepath);
    398   strcpy(csv_path + filepath_len - 4, ".csv");
    399   uint32_t csv_num_meshes;
    400   char** csv_mesh_filenames;
    401   uint32_t* csv_material_indicies;
    402   Vec3* csv_velocities;
    403   readCsv(
    404     csv_path,
    405     &csv_num_meshes,
    406     &csv_mesh_filenames,
    407     &csv_material_indicies,
    408     &csv_velocities
    409   );
    410   free(csv_path);
    411 
    412   /* Read the meshes */
    413 
    414   /* Get the scene directory path */
    415   size_t scene_dir_len = strrchr(filepath, '/') - filepath + 1;
    416   /* if filepath is "/path/to/scene.xml", scene_dir is "/path/to/" */
    417   char* scene_dir = (char*)malloc(scene_dir_len + 7);
    418   if (!scene_dir)
    419     PERROR_CLEANUP_EXIT("Error: failed to allocate scene_dir", 8);
    420   strncpy(scene_dir, filepath, scene_dir_len);
    421 
    422   /* For each ply file from the xml file */
    423   scene->num_meshes = xml_num_meshes;
    424   scene->meshes = (Mesh*)malloc(xml_num_meshes * sizeof(Mesh));
    425   if (!scene->meshes && xml_num_meshes > 0)
    426     PERROR_CLEANUP_EXIT("Error: failed to allocate meshes array", 8);
    427   for (uint32_t i = 0; i != xml_num_meshes; ++i) {
    428     /* Get the mesh file path */
    429     size_t mesh_filepath_len = scene_dir_len + strlen(xml_mesh_filepaths[i]) + 1;
    430     char* mesh_filepath = (char*)malloc(mesh_filepath_len);
    431     snprintf(
    432       mesh_filepath,
    433       mesh_filepath_len,
    434       "%.*s%s",
    435       (int)scene_dir_len,
    436       scene_dir,
    437       xml_mesh_filepaths[i]
    438     );
    439     /* Read the mesh */
    440     scene->meshes[i] = readPly(mesh_filepath);
    441     free(mesh_filepath);
    442     /* Set the material index from the material name from xml */
    443     scene->meshes[i].material_index = get_material_index(xml_mesh_materials[i]);
    444     /* Set the material index and velocity from CSV if present */
    445     for (uint32_t j = 0; j != csv_num_meshes; ++j)
    446       if (strcmp(xml_mesh_names[i], csv_mesh_filenames[j]) == 0) {
    447         scene->meshes[i].material_index = csv_material_indicies[j];
    448         scene->meshes[i].velocity = csv_velocities[j];
    449         break;
    450       }
    451   }
    452 
    453   free(scene_dir);
    454 }
    455 
    456 
    457 /**
    458  * Main
    459  */
    460 
    461 int main(int argc, char *argv[]) {
    462   if (argc != 2) {
    463     printf("Usage: %s <scene.xml>\n", argv[0]);
    464     return 1;
    465   }
    466   const char* scene_filepath = argv[1];
    467   const char* scene_filename = strrchr(scene_filepath, '/');
    468   if (scene_filename == NULL) exit(8);
    469   else ++scene_filename;
    470 
    471   Scene* scene_ptr;
    472 
    473   /* read scene */
    474   /* check if the scene is a hardcoded scene */
    475   if (!strcmp(scene_filename, scene_box_filename))
    476     scene_ptr = &scene_box;
    477   else if (!strcmp(scene_filename, scene_simpleReflector_filename))
    478     scene_ptr = &scene_simpleReflector;
    479   else {
    480     scene_ptr = (Scene*)malloc(sizeof(Scene));
    481     readScene(scene_filepath, scene_ptr);
    482   }
    483 
    484   /* Save the scene as HRT */
    485   scene_save(scene_ptr, "scene.hrt");
    486 
    487   return 0;
    488 }