diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/editor/view3d.cpp | 1 | ||||
| -rw-r--r-- | src/render/model.h | 687 |
2 files changed, 329 insertions, 359 deletions
diff --git a/src/editor/view3d.cpp b/src/editor/view3d.cpp index fd27cf3..a67e149 100644 --- a/src/editor/view3d.cpp +++ b/src/editor/view3d.cpp @@ -5,6 +5,7 @@ #include "../game/objlist.h" #include "../game/world/draw.h" #include "../game/world/bsp_draw.h" +#include "../game/world/model_draw.h" #include "../game.h" const I32 EDITORVIEW_TITLE_OFFSET = 15; diff --git a/src/render/model.h b/src/render/model.h index 83a85bb..da3213a 100644 --- a/src/render/model.h +++ b/src/render/model.h @@ -16,6 +16,28 @@ struct MODEL { LIST<U32> indices; }; +enum LINE_TYPES : I32 { + comment = ( '#' | ( ' ' << 8 ) ), + d = ( 'd' | ( ' ' << 8 ) ), + f = ( 'f' | ( ' ' << 8 ) ), + illum = ( 'i' | ( 'l' << 8 ) ), + Ka = ( 'K' | ( 'a' << 8 ) ), + Kd = ( 'K' | ( 'd' << 8 ) ), + Ke = ( 'K' | ( 'e' << 8 ) ), + Ks = ( 'K' | ( 's' << 8 ) ), + map_Kd = ( 'm' | ( 'a' << 8 ) ), + mtllib = ( 'm' | ( 't' << 8 ) ), + newmtl = ( 'n' | ( 'e' << 8 ) ), + Ni = ( 'N' | ( 'i' << 8 ) ), + Ns = ( 'N' | ( 's' << 8 ) ), + o = ( 'o' | ( ' ' << 8 ) ), + s = ( 's' | ( ' ' << 8 ) ), + v = ( 'v' | ( ' ' << 8 ) ), + vn = ( 'v' | ( 'n' << 8 ) ), + vt = ( 'v' | ( 't' << 8 ) ), + usemtl = ( 'u' | ( 's' << 8 ) ) +}; + static inline char* skip_space( char* s ) { while( *s == ' ' || *s == '\t' ) ++s; return s; @@ -32,391 +54,338 @@ static inline VEC3 read_vec3( char* s ) { return temp; } -namespace MTL { - enum LINE_TYPES : U16 { - comment = ( '#' | ( ' ' << 8 ) ), - newmtl = ( 'n' | ( 'e' << 8 ) ), - Ns = ( 'N' | ( 's' << 8 ) ), - Ka = ( 'K' | ( 'a' << 8 ) ), - Kd = ( 'K' | ( 'd' << 8 ) ), - Ks = ( 'K' | ( 's' << 8 ) ), - Ke = ( 'K' | ( 'e' << 8 ) ), - Ni = ( 'N' | ( 'i' << 8 ) ), - d = ( 'd' | ( ' ' << 8 ) ), - illum = ( 'i' | ( 'l' << 8 ) ), - map_Kd = ( 'm' | ( 'a' << 8 ) ) - }; - - struct MTLDATA { - STR name; - F32 Ns; - VEC3 Ka; - VEC3 Kd; - VEC3 Ks; - VEC3 Ke; - F32 Ni; - F32 d; - I32 illum; - STR map_Kd; - }; - - struct MTLLIB { - STR name; - LIST<MTLDATA> data; - }; - - inline MTLLIB mtl_from_file( const char* path, const char* name ) { - U64 mtl_size = 0; - STR mtl_path( path ); - mtl_path += name; - char* f_mtl = (char*)file_read( mtl_path.data, &mtl_size ); - if( !f_mtl ) { - dlog( "mtl_from_file() : failed to read mtl file %s\n", mtl_path.data ); - return {}; - } - - MTLLIB mtl; - mtl.name = name; - for( U64 i = 0; i < mtl_size - 1 && f_mtl[i]; ++i ) { - if( f_mtl[i] == '\n' ) - continue; - - I32 c0 = f_mtl[i]; - U16 c1 = f_mtl[i + 1]; - I32 first_two = c0 | c1 << 8; +static inline LINE_TYPES get_first_two( char* s ) { + I32 c0 = *s; + U16 c1 = *(s + 1); + return (LINE_TYPES)( c0 | c1 << 8 ); +} - switch( first_two ) { - case LINE_TYPES::newmtl: { - char* start = f_mtl + i + 7; - char* end = start; - while( *end && *end != '\n' ) ++end; - U32 len = end - start; - STR temp; - temp.reserve( len + 1 ); - memcpy( temp, start, len ); - temp.data[len] = 0; - mtl.data.resize( mtl.data.size + 1 ); - mtl.data[mtl.data.size - 1].name = temp.data; - } break; - case LINE_TYPES::d: - case LINE_TYPES::Ni: - case LINE_TYPES::Ns: { - char* s = f_mtl + i + 2; - s = skip_space( s ); - F32 temp = strtof( s, &s ); - switch( first_two ) { - case LINE_TYPES::d: - mtl.data[mtl.data.size - 1].d = temp; - break; - case LINE_TYPES::Ni: - mtl.data[mtl.data.size - 1].Ni = temp; - break; - case LINE_TYPES::Ns: - mtl.data[mtl.data.size - 1].Ns = temp; - break; - } - } break; - case LINE_TYPES::illum: { - char* s = f_mtl + i + 6; - mtl.data[mtl.data.size - 1].illum = (I32)strtol( s, &s, 10 ); - } break; - case LINE_TYPES::Ka: - case LINE_TYPES::Kd: - case LINE_TYPES::Ke: - case LINE_TYPES::Ks: { - char* s = f_mtl + i + 3; - switch( first_two ) { - case LINE_TYPES::Ka: - mtl.data[mtl.data.size - 1].Ka = read_vec3( s ); - break; - case LINE_TYPES::Kd: - mtl.data[mtl.data.size - 1].Kd = read_vec3( s ); - break; - case LINE_TYPES::Ke: - mtl.data[mtl.data.size - 1].Ke = read_vec3( s ); - break; - case LINE_TYPES::Ks: - mtl.data[mtl.data.size - 1].Ks = read_vec3( s ); - break; - } - } break; - case LINE_TYPES::map_Kd: { - char* start = f_mtl + i + 7; - char* end = start; - while( *end && *end != '\n' ) ++end; - U32 len = end - start; - STR temp; - temp.reserve( len + 1 ); - memcpy( temp, start, len ); - temp.data[len] = 0; - mtl.data[mtl.data.size - 1].map_Kd = temp.data; - } break; - case LINE_TYPES::comment: break; - default: dlog( "mtl_from_file() : unhandled char combo %c%c\n", c0, c1 ); break; - } +struct MTLDATA { + STR name; + F32 Ns; + VEC3 Ka; + VEC3 Kd; + VEC3 Ks; + VEC3 Ke; + F32 Ni; + F32 d; + I32 illum; + STR map_Kd; +}; - while( f_mtl[i] && f_mtl[i] != '\n' ) ++i; - } +struct MTLLIB { + STR name; + LIST<MTLDATA> data; +}; - free( (void*)f_mtl ); - return mtl; +inline I32 line_type_offset( LINE_TYPES first_two ) { + switch( first_two ) { + case d: + case f: + case o: + case s: + case v: return 2; + case Ka: + case Kd: + case Ke: + case Ks: + case Ni: + case Ns: + case vn: + case vt: return 3; + case illum: return 6; + case map_Kd: + case mtllib: + case newmtl: + case usemtl: return 7; + case comment: return 2; } -}; +} -namespace OBJ { - enum LINE_TYPES : U16 { - comment = ( '#' | ( ' ' << 8 ) ), - f = ( 'f' | ( ' ' << 8 ) ), - mtllib = ( 'm' | ( 't' << 8 ) ), - o = ( 'o' | ( ' ' << 8 ) ), - s = ( 's' | ( ' ' << 8 ) ), - v = ( 'v' | ( ' ' << 8 ) ), - vn = ( 'v' | ( 'n' << 8 ) ), - vt = ( 'v' | ( 't' << 8 ) ), - usemtl = ( 'u' | ( 's' << 8 ) ) - }; +inline void mtl_parse_line( char* file, U64 idx, MTLLIB* mtl ) { + LINE_TYPES first_two = get_first_two( file + idx ); + char* p = file + idx + line_type_offset( first_two ); + U64 mtl_idx = mtl->data.size - 1; + + switch( first_two ) { + case newmtl: { + char* end = p; + while( *end && *end != '\n' ) ++end; + U32 len = end - p; + STR temp; + temp.reserve( len + 1 ); + memcpy( temp.data, p, len ); + temp.data[len] = 0; + mtl->data.resize( mtl->data.size + 1 ); + mtl->data[mtl->data.size - 1].name = temp.data; + } break; + case d: mtl->data[mtl_idx].d = strtof( p, &p ); break; + case Ni: mtl->data[mtl_idx].Ni = strtof( p, &p ); break; + case Ns: mtl->data[mtl_idx].Ns = strtof( p, &p ); break; + case illum: mtl->data[mtl_idx].illum = (I32)strtol( p, &p, 10 ); break; + case Ka: mtl->data[mtl_idx].Ka = read_vec3( p ); break; + case Kd: mtl->data[mtl_idx].Kd = read_vec3( p ); break; + case Ke: mtl->data[mtl_idx].Ke = read_vec3( p ); break; + case Ks: mtl->data[mtl_idx].Ks = read_vec3( p ); break; + case map_Kd: { + char* end = p; + while( *end && *end != '\n' ) ++end; + U32 len = end - p; + STR temp; + temp.reserve( len + 1 ); + memcpy( temp.data, p, len ); + temp.data[len] = 0; + mtl->data[mtl_idx].map_Kd = temp.data; + } break; + case comment: break; + default: { + dlog( + "mtl_from_file() : unhandled char combo %c%c\n", + first_two & 0xFF, ( first_two >> 8 ) & 0xFF + ); + } break; + } +} - enum FACE_TYPES : U8 { - V = 0, - VVT = 1, - VVN = 2, - VVTVN = 3 - }; +inline MTLLIB mtl_from_file( const char* path, const char* name ) { + U64 mtl_size; + STR full_path( path ); + full_path += name; + char* f_mtl = (char*)file_read( full_path.data, &mtl_size ); + if( !f_mtl ) { + dlog( "mtl_from_file() : failed to read file %s\n", full_path.data ); + return {}; + } - constexpr U32 STR_LEN = 0x40; + MTLLIB mtl; + mtl.name = name; + for( U64 i = 0; i < mtl_size - 1 && f_mtl[i]; ++i ) { + if( f_mtl[i] == '\n' ) + continue; + mtl_parse_line( f_mtl, i, &mtl ); + while( f_mtl[i] && f_mtl[i] != '\n' ) ++i; + } - struct OBJFACE { - OBJ::FACE_TYPES type; - LIST<U32> indices; - }; + free( (void*)f_mtl ); + return mtl; +} - struct OBJDATA { - LIST<VEC2> uvs; - LIST<VEC3> normals; - LIST<VEC3> vertices; +enum FACE_TYPES : U8 { + V = 0, + VVT = 1, + VVN = 2, + VVTVN = 3 +}; - LIST<MTL::MTLLIB> mtllib; +constexpr U32 STR_LEN = 0x40; - struct UseMtl { - LIST<U32> face_start; - LIST<STR> mtl_name; - } usemtl; +struct OBJFACE { + FACE_TYPES type; + LIST<U32> indices; +}; - LIST<OBJFACE> faces; - }; +struct OBJDATA { + LIST<VEC2> uvs; + LIST<VEC3> normals; + LIST<VEC3> vertices; + LIST<MTLLIB> mtllib; + struct UseMtl { + LIST<U32> face_start; + LIST<STR> mtl_name; + } usemtl; + LIST<OBJFACE> faces; +}; - static inline U32 face_stride( FACE_TYPES t ) { - switch( t ) { - case FACE_TYPES::V: return 1; - case FACE_TYPES::VVT: return 2; - case FACE_TYPES::VVN: return 2; - case FACE_TYPES::VVTVN: return 3; - } - return 0; +static inline U32 face_stride( FACE_TYPES t ) { + switch( t ) { + case V: return 1; + case VVT: return 2; + case VVN: return 2; + case VVTVN: return 3; } + return 0; +} - inline OBJDATA obj_from_file( const char* path, const char* name ) { - char* f_obj = nullptr; - STR obj_path( path ); - U64 obj_size = 0; - obj_path += name; - obj_path += ".obj"; - - f_obj = (char*)file_read( obj_path.data, &obj_size ); - if( !f_obj ) { - dlog( "obj_from_file() : failed to read obj file %s\n", obj_path.data ); - return {}; - } +inline OBJFACE triangulate_face( OBJFACE ref, U32 idx, U32 stride, FACE_TYPES type ) { + OBJFACE tri{}; + tri.type = type; + for( U32 ind = 0; ind < stride; ++ind ) + tri.indices.push( ref.indices.data[ind] ); + for( U32 ind = 0; ind < stride; ++ind ) + tri.indices.push( ref.indices.data[idx * stride + ind] ); + for( U32 ind = 0; ind < stride; ++ind ) + tri.indices.push( ref.indices.data[( idx + 1 ) * stride + ind] ); + return tri; +} - OBJDATA obj; - for( U64 i = 0; i < obj_size - 1 && f_obj[i]; ++i ) { - if( f_obj[i] == (I32)'#' ) { - while( f_obj[i] && f_obj[i] != '\n' ) ++i; - continue; +inline void obj_parse_f_line( OBJDATA* obj, char* p ) { + FACE_TYPES type = (FACE_TYPES)0; + bool type_set = 0; + OBJFACE temp{}; + while( *p && *p != '\n' ) { + bool has_vt = 0; + bool has_vn = 0; + U32 v = strtoul( p, &p, 10 ); + temp.indices.push( --v ); + if( *p == '/' ) { + ++p; + if( *p != '/' ) { + U32 vt = strtoul( p, &p, 10 ); + temp.indices.push( --vt ); + has_vt = 1; } - - I32 c0 = f_obj[i]; - U16 c1 = f_obj[i + 1]; - I32 first_two = c0 | c1 << 8; - - switch( first_two ) { - case LINE_TYPES::f: { - FACE_TYPES type = (FACE_TYPES)0; - char* s = f_obj + i + 2; - bool type_set = 0; - OBJFACE temp{}; - - s = skip_space( s ); - while( *s && *s != '\n' ) { - bool has_vt = 0; - bool has_vn = 0; - - U32 v = strtoul( s, &s, 10 ); - temp.indices.push( --v ); - - if( *s == '/' ) { - ++s; - - if( *s != '/' ) { - U32 vt = strtoul( s, &s, 10 ); - temp.indices.push( --vt ); - has_vt = 1; - } - - if( *s == '/' ) { - ++s; - U32 vn = strtoul( s, &s, 10 ); - temp.indices.push( --vn ); - has_vn = 1; - } - } - - if( !type_set ) { - type_set = 1; - if( has_vt && has_vn ) type = FACE_TYPES::VVTVN; - else if( has_vt ) type = FACE_TYPES::VVT; - else if( has_vn ) type = FACE_TYPES::VVN; - else type = FACE_TYPES::V; - } - - while( *s && *s != ' ' && *s != '\n' ) ++s; - s = skip_space( s ); - } - temp.type = type; - U32 stride = face_stride( type ); - U32 count = temp.indices.size / stride; - if ( count <= 3 ) - obj.faces.push( temp ); - else { - for( U32 idx = 1; idx < count - 1; ++idx ) { - OBJFACE tri{}; - tri.type = type; - - for( U32 ind = 0; ind < stride; ++ind ) - tri.indices.push( temp.indices.data[ind] ); - for( U32 ind = 0; ind < stride; ++ind ) - tri.indices.push( temp.indices.data[idx * stride + ind] ); - for( U32 ind = 0; ind < stride; ++ind ) - tri.indices.push( temp.indices.data[( idx + 1 ) * stride + ind] ); - obj.faces.push( tri ); - } - } - } break; - case LINE_TYPES::v: - case LINE_TYPES::vn: { - bool is_v = first_two == LINE_TYPES::v; - U32 start = i + ( is_v ? 2 : 3 ); - char* s = f_obj + start; - if( is_v ) - obj.vertices.push( read_vec3( s ) ); - else - obj.normals.push( read_vec3( s ) ); - } break; - case LINE_TYPES::vt: { - char* s = f_obj + i + 3; - VEC2 temp{}; - s = skip_space( s ); - temp.x = strtof( s, &s ); - s = skip_space( s ); - temp.y = strtof( s, &s ); - obj.uvs.push( temp ); - } break; - case LINE_TYPES::mtllib: - case LINE_TYPES::usemtl: { - bool is_mtllib = first_two == LINE_TYPES::mtllib; - char* start = f_obj + i + 7; - char* end = start; - while( *end && *end != '\n' ) ++end; - U32 len = end - start; - STR temp; - temp.reserve( len + 1 ); - memcpy( temp, start, len ); - temp.data[len] = 0; - if( !is_mtllib ) { - if( obj.usemtl.mtl_name.size ) - obj.usemtl.face_start.push( obj.faces.size ); - obj.usemtl.mtl_name.push( temp.data ); - } else - obj.mtllib.push( MTL::mtl_from_file( path, temp.data ) ); - } break; - case LINE_TYPES::comment: - case LINE_TYPES::o: - case LINE_TYPES::s: break; - default: dlog( "obj_from_file() : unhandled char combo %c%c\n", c0, c1 ); break; + if( *p == '/' ) { + ++p; + U32 vn = strtoul( p, &p, 10 ); + temp.indices.push( --vn ); + has_vn = 1; } - - while( f_obj[i] && f_obj[i] != '\n' ) ++i; } - obj.usemtl.face_start.push( obj.faces.size ); - if ( !obj.usemtl.mtl_name.size ) - obj.usemtl.mtl_name.push( "default" ); - free( (void*)f_obj ); - return obj; + if( !type_set ) { + type_set = 1; + if( has_vt && has_vn ) type = VVTVN; + else if( has_vt ) type = VVT; + else if( has_vn ) type = VVN; + else type = V; + } + while( *p && *p != ' ' && *p != '\n' ) ++p; + p = skip_space( p ); } + temp.type = type; + U32 stride = face_stride( type ); + U32 count = temp.indices.size / stride; + if ( count <= 3 ) + obj->faces.push( temp ); + else { + for( U32 idx = 1; idx < count - 1; ++idx ) + obj->faces.push( triangulate_face( temp, idx, stride, type ) ); + } +} - inline MODEL obj_to_model( OBJDATA obj ) { - struct KEY { U32 v, vt, vn; }; - struct ENTRY { KEY key; U32 idx; }; - - MODEL model; - LIST<ENTRY> table; - table.reserve( obj.faces.size * 3 ); - for( U32 f = 0; f < obj.faces.size; ++f ) { - OBJFACE& face = obj.faces.data[f]; - U32 stride = face_stride( face.type ); - U32 count = face.indices.size / stride; - - for( U32 v = 0; v < count; ++v ) { - KEY key = { - 0xFFFFFFFF, - 0xFFFFFFFF, - 0xFFFFFFFF - }; - U32 base = v * stride; - key.v = face.indices.data[base + 0]; - if( face.type == FACE_TYPES::VVT || face.type == FACE_TYPES::VVTVN ) - key.vt = face.indices.data[base + 1]; - if( face.type == FACE_TYPES::VVN ) - key.vn = face.indices.data[base + 1]; - if( face.type == FACE_TYPES::VVTVN ) - key.vn = face.indices.data[base + 2]; - - U32 idx = 0; - bool found = 0; - // this should be fine until ~1m triangles, right? - for( U32 i = 0; i < table.size; ++i ) { - ENTRY& e = table.data[i]; - if( e.key.vn != key.vn - || e.key.vt != key.vt - || e.key.v != key.v - ) continue; +inline void obj_parse_line( const char* path, char* file, U64 idx, OBJDATA* obj ) { + LINE_TYPES first_two = get_first_two( file + idx ); + char* p = file + idx + line_type_offset( first_two ); + + switch( first_two ) { + case f: obj_parse_f_line( obj, p ); break; + case v: + case vn: { + if( first_two == v ) obj->vertices.push( read_vec3( p ) ); + else obj->normals.push( read_vec3( p ) ); + } break; + case vt: { + VEC2 temp{}; + p = skip_space( p ); + temp.x = strtof( p, &p ); + p = skip_space( p ); + temp.y = strtof( p, &p ); + obj->uvs.push( temp ); + } break; + case mtllib: + case usemtl: { + bool is_mtllib = first_two == mtllib; + char* end = p; + while( *end && *end != '\n' ) ++end; + U32 len = end - p; + STR temp; + temp.reserve( len + 1 ); + memcpy( temp.data, p, len ); + temp.data[len] = 0; + if( !is_mtllib ) { + if( obj->usemtl.mtl_name.size ) + obj->usemtl.face_start.push( obj->faces.size ); + obj->usemtl.mtl_name.push( temp.data ); + } else + obj->mtllib.push( mtl_from_file( path, temp.data ) ); + } break; + case comment: + case o: + case s: break; + default: { + dlog( + "obj_from_file() : unhandled char combo %c%c\n", + first_two & 0xFF, ( first_two >> 8 ) & 0xFF + ); + } break; + } +} - idx = e.idx; - found = 1; - break; - } +inline OBJDATA obj_from_file( const char* path, const char* name ) { + char* f_obj = nullptr; + STR obj_path( path ); + U64 obj_size = 0; + obj_path += name; + obj_path += ".obj"; + f_obj = (char*)file_read( obj_path.data, &obj_size ); + if( !f_obj ) { + dlog( "obj_from_file() : failed to read obj file %s\n", obj_path.data ); + return {}; + } + OBJDATA obj; + for( U64 i = 0; i < obj_size - 1 && f_obj[i]; ++i ) { + if( f_obj[i] == '\n' ) + continue; + obj_parse_line( path, f_obj, i, &obj ); + while( f_obj[i] && f_obj[i] != '\n' ) ++i; + } + obj.usemtl.face_start.push( obj.faces.size ); + if ( !obj.usemtl.mtl_name.size ) + obj.usemtl.mtl_name.push( "default" ); + free( (void*)f_obj ); + return obj; +} - if( !found ) { - MODEL_DATA data{}; - data.position = obj.vertices.data[key.v]; - if( face.type == FACE_TYPES::VVTVN - || face.type == FACE_TYPES::VVT ) - data.uv = obj.uvs.data[key.vt]; - if( face.type == FACE_TYPES::VVTVN - || face.type == FACE_TYPES::VVN ) - data.normal = obj.normals.data[key.vn]; - idx = model.vertices.size; - model.vertices.push( data ); - table.push({ key, idx }); - } - model.indices.push( idx ); +inline MODEL obj_to_model( OBJDATA obj ) { + struct KEY { U32 v, vt, vn; }; + struct ENTRY { KEY key; U32 idx; }; + MODEL model; + LIST<ENTRY> table; + table.reserve( obj.faces.size * 3 ); + for( U32 f = 0; f < obj.faces.size; ++f ) { + OBJFACE& face = obj.faces.data[f]; + U32 stride = face_stride( face.type ); + U32 count = face.indices.size / stride; + for( U32 v = 0; v < count; ++v ) { + KEY key = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + U32 base = v * stride; + key.v = face.indices.data[base + 0]; + if( face.type == VVT || face.type == VVTVN ) + key.vt = face.indices.data[base + 1]; + if( face.type == VVN ) + key.vn = face.indices.data[base + 1]; + if( face.type == VVTVN ) + key.vn = face.indices.data[base + 2]; + U32 idx = 0; + bool found = 0; + // this should be fine until ~1m triangles, right? + for( U32 i = 0; i < table.size; ++i ) { + ENTRY& e = table.data[i]; + if( e.key.vn != key.vn + || e.key.vt != key.vt + || e.key.v != key.v + ) continue; + idx = e.idx; + found = 1; + break; } + if( !found ) { + MODEL_DATA data{}; + data.position = obj.vertices.data[key.v]; + if( face.type == VVTVN || face.type == VVT ) + data.uv = obj.uvs.data[key.vt]; + if( face.type == VVTVN || face.type == VVN ) + data.normal = obj.normals.data[key.vn]; + idx = model.vertices.size; + model.vertices.push( data ); + table.push({ key, idx }); + } + model.indices.push( idx ); } - return model; } -}; + return model; +} inline MODEL model_from_obj( const I8* path, const I8* name ) { - OBJ::OBJDATA obj = OBJ::obj_from_file( path, name ); - return OBJ::obj_to_model( obj ); + OBJDATA obj = obj_from_file( path, name ); + return obj_to_model( obj ); } |
