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 }