Created
February 27, 2024 20:11
-
-
Save meowstr/0abe80e94c7917c7137b35509faf248c to your computer and use it in GitHub Desktop.
Revisions
-
meowstr created this gist
Feb 27, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,517 @@ #include "wavefront.hpp" #include "logging.hpp" #include <float.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // #define MAX_VERTEX_COUNT 4096 // #define MAX_OBJ_COUNT 16 #define MAX_VERTEX_COUNT 100000 #define MAX_OBJ_COUNT 1000 #define MAX_NAME_LENGTH 64 #define MAX_POLYGON_LENGTH 32 #define MAX_LINE_LENGTH 256 #define MAX_MATERIAL_GROUP_COUNT 64 #define MAX_MATERIAL_COUNT 32 static int starts_with( const char * line, const char * str ) { return strncmp( line, str, strlen( str ) ) == 0; } // line parsers [start] //////////////////////////////////////////////////////// static int parse_object_line( char * out_name, const char * line ) { snprintf( out_name, MAX_NAME_LENGTH, "%s", line + strlen( "o " ) ); return 0; } static int parse_material_lib_line( char * out_name, const char * line ) { snprintf( out_name, MAX_NAME_LENGTH, "%s", line + strlen( "mtllib " ) ); return 0; } static int parse_usemtl_line( char * out_name, const char * line ) { snprintf( out_name, MAX_NAME_LENGTH, "%s", line + strlen( "usemtl " ) ); return 0; } static int parse_position_line( float * out3, const char * line ) { int count = sscanf( line, "v %f %f %f", out3 + 0, out3 + 1, out3 + 2 ); return count != 3; } static int parse_normal_line( float * out3, const char * line ) { int count = sscanf( line, "vn %f %f %f", out3 + 0, out3 + 1, out3 + 2 ); return count != 3; } static int parse_uv_line( float * out2, const char * line ) { int count = sscanf( line, "vt %f %f", out2 + 0, out2 + 1 ); return count != 2; } static int parse_face_line( int * out_index_list, int * out_index_count, char * line ) { int len = strlen( line ); int count = 0; for ( int i = 0; i < len; i++ ) { if ( line[ i ] == '/' ) line[ i ] = ' '; } for ( int i = 0; i < len; i++ ) { if ( line[ i ] != ' ' ) continue; if ( count >= MAX_POLYGON_LENGTH * 3 ) { ERROR_LOG( "polygon limit exceeded" ); return 1; } out_index_list[ count ] = atoi( line + i ) - 1; count++; } *out_index_count = count; return count % 3 != 0; } static int parse_newmtl_line( char * out_name, const char * line ) { snprintf( out_name, MAX_NAME_LENGTH, "%s", line + strlen( "newmtl " ) ); return 0; } static int parse_ns_line( float * out, const char * line ) { int count = sscanf( line, "Ns %f", out ); return count != 1; } static int parse_ka_line( float * out3, const char * line ) { int count = sscanf( line, "Ka %f %f %f", out3 + 0, out3 + 1, out3 + 2 ); return count != 3; } static int parse_kd_line( float * out3, const char * line ) { int count = sscanf( line, "Kd %f %f %f", out3 + 0, out3 + 1, out3 + 2 ); return count != 3; } static int parse_ks_line( float * out3, const char * line ) { int count = sscanf( line, "Ks %f %f %f", out3 + 0, out3 + 1, out3 + 2 ); return count != 3; } static int parse_ke_line( float * out3, const char * line ) { int count = sscanf( line, "Ke %f %f %f", out3 + 0, out3 + 1, out3 + 2 ); return count != 3; } static int parse_ni_line( float * out, const char * line ) { int count = sscanf( line, "Ni %f", out ); return count != 1; } static int parse_d_line( float * out, const char * line ) { int count = sscanf( line, "d %f", out ); return count != 1; } static int parse_illum_line( int * out, const char * line ) { int count = sscanf( line, "illum %d", out ); return count != 1; } static int parse_map_kd_line( char * out_name, const char * line ) { snprintf( out_name, MAX_NAME_LENGTH, "%s", line + strlen( "map_Kd " ) ); return 0; } // line parsers [end] ////////////////////////////////////////////////////////// static void append_vertex( wavefront_t * out, float * pos_list, float * normal_list, float * uv_list, int * index_list, int index ) { if ( out->vertex_count >= MAX_VERTEX_COUNT ) { ERROR_LOG( "vertex limit exceeded" ); return; } int i = out->vertex_count; int pos_index = index_list[ index * 3 + 0 ]; int uv_index = index_list[ index * 3 + 1 ]; int normal_index = index_list[ index * 3 + 2 ]; out->pos_list[ i * 3 + 0 ] = pos_list[ pos_index * 3 + 0 ]; out->pos_list[ i * 3 + 1 ] = pos_list[ pos_index * 3 + 1 ]; out->pos_list[ i * 3 + 2 ] = pos_list[ pos_index * 3 + 2 ]; out->normal_list[ i * 3 + 0 ] = normal_list[ normal_index * 3 + 0 ]; out->normal_list[ i * 3 + 1 ] = normal_list[ normal_index * 3 + 1 ]; out->normal_list[ i * 3 + 2 ] = normal_list[ normal_index * 3 + 2 ]; out->uv_list[ i * 2 + 0 ] = uv_list[ uv_index * 2 + 0 ]; out->uv_list[ i * 2 + 1 ] = uv_list[ uv_index * 2 + 1 ]; out->vertex_count++; } static void append_face( wavefront_t * out, float * pos_list, float * normal_list, float * uv_list, int * index_list, int index_count ) { int poly_size = index_count / 3; // make triangle fan for ( int i = 0; i < poly_size - 2; i++ ) { int v1 = 0; int v2 = i + 1; int v3 = i + 2; append_vertex( out, pos_list, normal_list, uv_list, index_list, v1 ); append_vertex( out, pos_list, normal_list, uv_list, index_list, v2 ); append_vertex( out, pos_list, normal_list, uv_list, index_list, v3 ); } } void wavefront_t::compute_bounds( float * min_vec3, float * max_vec3 ) { min_vec3[ 0 ] = FLT_MAX; min_vec3[ 1 ] = FLT_MAX; min_vec3[ 2 ] = FLT_MAX; max_vec3[ 0 ] = -FLT_MAX; max_vec3[ 1 ] = -FLT_MAX; max_vec3[ 2 ] = -FLT_MAX; for ( int i = 0; i < vertex_count; i++ ) { min_vec3[ 0 ] = fmin( min_vec3[ 0 ], pos_list[ i * 3 + 0 ] ); min_vec3[ 1 ] = fmin( min_vec3[ 1 ], pos_list[ i * 3 + 1 ] ); min_vec3[ 2 ] = fmin( min_vec3[ 2 ], pos_list[ i * 3 + 2 ] ); max_vec3[ 0 ] = fmax( max_vec3[ 0 ], pos_list[ i * 3 + 0 ] ); max_vec3[ 1 ] = fmax( max_vec3[ 1 ], pos_list[ i * 3 + 1 ] ); max_vec3[ 2 ] = fmax( max_vec3[ 2 ], pos_list[ i * 3 + 2 ] ); } } static void print_header( wavefront_t * out ) { float bounds[ 3 ]; float min[ 3 ]; float max[ 3 ]; out->compute_bounds( min, max ); bounds[ 0 ] = max[ 0 ] - min[ 0 ]; bounds[ 1 ] = max[ 1 ] - min[ 1 ]; bounds[ 2 ] = max[ 2 ] - min[ 2 ]; INFO_LOG( "mesh description:" ); if ( out->obj_count > 0 ) { INFO_LOG( " name: %s", out->obj_name_list[ 0 ] ); } INFO_LOG( " vertex count: %d", out->vertex_count ); INFO_LOG( " object count: %d", out->obj_count ); INFO_LOG( " bounds: %.2f x %.2f x %.2f", bounds[ 0 ], bounds[ 1 ], bounds[ 2 ] ); } static void print_mtl_header( material_t * out, const char * name ) { INFO_LOG( "material description:" ); INFO_LOG( " name: %s", name ); INFO_LOG( " Ns: %f", out->ns ); INFO_LOG( " Ka: %f %f %f", out->ka[ 0 ], out->ka[ 1 ], out->ka[ 2 ] ); INFO_LOG( " Kd: %f %f %f", out->kd[ 0 ], out->kd[ 1 ], out->kd[ 2 ] ); INFO_LOG( " Ks: %f %f %f", out->ks[ 0 ], out->ks[ 1 ], out->ks[ 2 ] ); INFO_LOG( " Ke: %f %f %f", out->ke[ 0 ], out->ke[ 1 ], out->ke[ 2 ] ); INFO_LOG( " Ni: %f", out->ni ); INFO_LOG( " d: %f", out->d ); INFO_LOG( " illum: %d", out->illum ); INFO_LOG( " map_Kd: %s", out->map_kd ? out->map_kd : "(null)" ); } int load_wavefront( wavefront_t * out, res_t res ) { char line[ MAX_LINE_LENGTH ]; int cursor = 0; int errors = 0; // intialize output using c_string_t = const char *; out->pos_list = new float[ MAX_VERTEX_COUNT * 3 ]; out->normal_list = new float[ MAX_VERTEX_COUNT * 3 ]; out->uv_list = new float[ MAX_VERTEX_COUNT * 2 ]; out->vertex_count = 0; out->obj_offset_list = new int[ MAX_OBJ_COUNT ]; out->obj_name_list = new c_string_t[ MAX_OBJ_COUNT ]; out->obj_count = 0; out->material_lib_filename = nullptr; out->material_group_material_list = new c_string_t[ MAX_MATERIAL_GROUP_COUNT ]; out->material_group_offset_list = new int[ MAX_MATERIAL_GROUP_COUNT ]; out->material_group_count = 0; // local storage float * pos_list = new float[ MAX_VERTEX_COUNT * 3 ]; float * normal_list = new float[ MAX_VERTEX_COUNT * 3 ]; float * uv_list = new float[ MAX_VERTEX_COUNT * 2 ]; int pos_count = 0; int normal_count = 0; int uv_count = 0; while ( cursor < res.size ) { // load next line int i; for ( i = 0; i + cursor < res.size; i++ ) { char c = (char) res.data[ i + cursor ]; if ( c == '\n' ) break; if ( i >= MAX_LINE_LENGTH ) { ERROR_LOG( "line length exceeded" ); break; } line[ i ] = c; } cursor += i + 1; line[ i ] = '\0'; // parse o command if ( starts_with( line, "o " ) ) { if ( out->obj_count >= MAX_OBJ_COUNT ) { ERROR_LOG( "object limit exceeded" ); continue; } char * name = new char[ MAX_NAME_LENGTH ]; errors += parse_object_line( name, line ); out->obj_name_list[ out->obj_count ] = name; out->obj_count++; } // parse v command if ( starts_with( line, "v " ) ) { if ( pos_count >= MAX_VERTEX_COUNT * 3 ) { ERROR_LOG( "vertex limit exceeded" ); continue; } float pos[ 3 ]; errors += parse_position_line( pos, line ); pos_list[ pos_count * 3 + 0 ] = pos[ 0 ]; pos_list[ pos_count * 3 + 1 ] = pos[ 1 ]; pos_list[ pos_count * 3 + 2 ] = pos[ 2 ]; pos_count++; } // parse vn command if ( starts_with( line, "vn " ) ) { if ( normal_count >= MAX_VERTEX_COUNT * 3 ) { ERROR_LOG( "vertex limit exceeded" ); continue; } float normal[ 3 ]; errors += parse_normal_line( normal, line ); normal_list[ normal_count * 3 + 0 ] = normal[ 0 ]; normal_list[ normal_count * 3 + 1 ] = normal[ 1 ]; normal_list[ normal_count * 3 + 2 ] = normal[ 2 ]; normal_count++; } // parse vt command if ( starts_with( line, "vt " ) ) { if ( uv_count >= MAX_VERTEX_COUNT * 2 ) { ERROR_LOG( "vertex limit exceeded" ); continue; } float uv[ 2 ]; errors += parse_uv_line( uv, line ); uv_list[ uv_count * 2 + 0 ] = uv[ 0 ]; uv_list[ uv_count * 2 + 1 ] = uv[ 1 ]; uv_count++; } // parse f command if ( starts_with( line, "f " ) ) { int index_list[ MAX_POLYGON_LENGTH * 3 ]; int index_count = 0; errors += parse_face_line( index_list, &index_count, line ); append_face( out, pos_list, normal_list, uv_list, index_list, index_count ); } if ( starts_with( line, "mtllib " ) ) { char * name = new char[ MAX_NAME_LENGTH ]; errors += parse_material_lib_line( name, line ); out->material_lib_filename = name; } if ( starts_with( line, "usemtl " ) ) { if ( out->material_group_count >= MAX_MATERIAL_GROUP_COUNT ) { ERROR_LOG( "material group count exceeded" ); continue; } char * name = new char[ MAX_NAME_LENGTH ]; errors += parse_usemtl_line( name, line ); out->material_group_material_list[ out->material_group_count ] = name; out->material_group_offset_list[ out->material_group_count ] = out->vertex_count; out->material_group_count++; } } if ( errors ) { ERROR_LOG( "failed to load wavefront (.obj)" ); } print_header( out ); delete[] pos_list; delete[] normal_list; delete[] uv_list; return errors; } int load_material_lib( material_lib_t * out, res_t res ) { int errors = 0; int cursor = 0; char line[ MAX_LINE_LENGTH ]; using c_string_t = const char *; out->material_name_list = new c_string_t[ MAX_MATERIAL_COUNT ]; out->material_list = new material_t[ MAX_MATERIAL_COUNT ]; out->material_count = 0; // zero out all the materials memset( out->material_list, 0, MAX_MATERIAL_COUNT * sizeof( material_t ) ); while ( cursor < res.size ) { // load next line int i; for ( i = 0; i + cursor < res.size; i++ ) { char c = (char) res.data[ i + cursor ]; if ( c == '\n' ) break; if ( i >= MAX_LINE_LENGTH ) { ERROR_LOG( "line length exceeded" ); break; } line[ i ] = c; } cursor += i + 1; line[ i ] = '\0'; // parse newmtl command if ( starts_with( line, "newmtl " ) ) { if ( out->material_count >= MAX_MATERIAL_COUNT ) { ERROR_LOG( "material count exceeded" ); continue; } char * newmtl = new char[ MAX_NAME_LENGTH ]; errors += parse_newmtl_line( newmtl, line ); out->material_name_list[ out->material_count ] = newmtl; out->material_count++; } // ignore lines until we have a material if ( out->material_count <= 0 ) continue; // get current material material_t * mat = out->material_list + ( out->material_count - 1 ); if ( starts_with( line, "Ns " ) ) { errors += parse_ns_line( &mat->ns, line ); } if ( starts_with( line, "Ka " ) ) { errors += parse_ka_line( mat->ka, line ); } if ( starts_with( line, "Kd " ) ) { errors += parse_kd_line( mat->kd, line ); } if ( starts_with( line, "Ks " ) ) { errors += parse_ks_line( mat->ks, line ); } if ( starts_with( line, "Ke " ) ) { errors += parse_ke_line( mat->ke, line ); } if ( starts_with( line, "Ni " ) ) { errors += parse_ni_line( &mat->ni, line ); } if ( starts_with( line, "d " ) ) { errors += parse_d_line( &mat->d, line ); } if ( starts_with( line, "illum " ) ) { errors += parse_illum_line( &mat->illum, line ); } if ( starts_with( line, "map_Kd " ) ) { char * filename = new char[ MAX_NAME_LENGTH ]; errors += parse_map_kd_line( filename, line ); mat->map_kd = filename; } } if ( errors ) { ERROR_LOG( "failed to load wavefront (.mtl)" ); } for ( int i = 0; i < out->material_count; i++ ) { print_mtl_header( out->material_list + i, out->material_name_list[ i ] ); } return errors; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,47 @@ #pragma once #include "res.hpp" struct wavefront_t { float * pos_list; float * normal_list; float * uv_list; int vertex_count; int * obj_offset_list; const char ** obj_name_list; int obj_count; const char * material_lib_filename; int * material_group_offset_list; const char ** material_group_material_list; int material_group_count; void compute_bounds( float * min_vec3, float * max_vec3 ); }; struct material_t { float ns; // specular exponent float ka[ 3 ]; // ambient float kd[ 3 ]; // diffuse float ks[ 3 ]; // specular float ke[ 3 ]; // emmision float ni; // refraction index float d; // dissolve int illum; // illum model const char * map_kd; // diffuse texture map }; struct material_lib_t { const char ** material_name_list; material_t * material_list; int material_count; }; /// @threadsafe int load_wavefront( wavefront_t * out_mesh, res_t res ); /// @threadsafe int load_material_lib( material_lib_t * out_lib, res_t res );