diff --git a/README.md b/README.md index 00fe738..4c6462c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ +(This fork adds support for morph targets and extras, as well as keeping it up to date with new compiler releases. NOTE: morph targets dont support sparse accessors) + # glTF parser for Jai codebase -Note: **Jai beta 0.1.087 is required.** +Note: **Jai beta 0.2.022 is required.** This project is a glTF 2.0 parser written in Jai, aiming to replace the use of some C/C++ libraries. All glTF types are fully documented, so it comes nicely with IDE autocompletion, reducing back and forth with the [specification](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html). @@ -82,8 +84,8 @@ nodes counts, etc...): - [x] Skins - [x] Cameras - [x] Parse `glb` files -- [ ] Morth targets -- [ ] Extras data +- [x] Morth targets +- [x] Extras data Also, we supports some glTF extensions: diff --git a/module.jai b/module.jai index 3223e34..551c2af 100644 --- a/module.jai +++ b/module.jai @@ -70,6 +70,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { node.skin = get_int(node_parsed, "skin", -1); node.mesh = get_int(node_parsed, "mesh", -1); node.camera = get_int(node_parsed, "camera", -1); + node.extras = cast(*void, get_object(node_parsed, "extras")); matrix, has_matrix := get_array(node_parsed, "matrix"); if has_matrix { @@ -92,7 +93,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { for val: translation node.translation[it_index] = parse_float(val); } - node.local_transform = get_local_transform(*node); + node.local_transform = get_local_transform(node); children, has_children := get_array(node_parsed, "children"); if has_children { @@ -119,6 +120,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { buffer.byte_length = get_int(buffer_parsed, "byteLength"); buffer.uri = get_string(buffer_parsed, "uri"); + buffer.extras = cast(*void, get_object(buffer_parsed, "extras")); } @@ -138,6 +140,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { buffer_view.buffer = get_int(buffer_view_parsed, "buffer", -1); buffer_view.byte_length = get_int(buffer_view_parsed, "byteLength"); buffer_view.byte_offset = get_int(buffer_view_parsed, "byteOffset"); + buffer_view.extras = cast(*void, get_object(buffer_view_parsed, "extras")); stride, has_stride := get_int(buffer_view_parsed, "byteStride"); if has_stride { @@ -163,6 +166,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { accessor.buffer_view = get_int(accessor_parsed, "bufferView", -1); accessor.byte_offset = get_int(accessor_parsed, "byteOffset"); accessor.count = get_int(accessor_parsed, "count"); + accessor.extras = cast(*void, get_object(accessor_parsed, "extras")); comp_type := get_int(accessor_parsed, "componentType"); accessor.component_type = cast(GLTF_Component_Type) comp_type; @@ -216,6 +220,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { defer array_add(*data.scenes, scene); scene.name = get_string(scene_parsed, "name"); + scene.extras = cast(*void, get_object(scene_parsed, "extras")); nodes := get_array(scene_parsed, "nodes"); for nodes array_add(*scene.nodes, parse_int(it)); @@ -233,6 +238,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { defer array_add(*data.skins, skin); skin.name = get_string(it, "name"); + skin.extras = cast(*void, get_object(it, "extras")); skin.skeleton = get_int(it, "skeleton", -1); skin.inverse_bind_matrices = get_int(it, "inverseBindMatrices", -1); @@ -253,6 +259,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { defer array_add(*data.meshes, mesh); mesh.name = get_string(mesh_parsed, "name"); + mesh.extras = cast(*void, get_object(mesh_parsed, "extras")); primitives := get_array(mesh_parsed, "primitives"); for prim_parsed: primitives { @@ -279,6 +286,21 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { primitive.texcoord_0_accessor = get_int(attributes, "TEXCOORD_0", -1); primitive.texcoord_1_accessor = get_int(attributes, "TEXCOORD_1", -1); primitive.texcoord_2_accessor = get_int(attributes, "TEXCOORD_2", -1); + + primitive.extras = cast(*void, get_object(prim_parsed, "extras")); + + targets, has_targets := get_array(prim_parsed, "targets"); + if has_targets { + for target_parsed: targets { + morph_target: GLTF_Morph_Target; + + morph_target.position_accessor = get_int(target_parsed, "POSITION", -1); + morph_target.normal_accessor = get_int(target_parsed, "NORMAL", -1); + morph_target.tangent_accessor = get_int(target_parsed, "TANGENT", -1); + + array_add(*primitive.targets, morph_target); + } + } } } @@ -310,6 +332,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { image.uri = get_string(image_parsed, "uri"); image.mime_type = get_string(image_parsed, "mimeType"); image.data = get_string(image_parsed, "data"); + image.extras = cast(*void, get_object(image_parsed, "extras")); image.buffer_view = get_int(image_parsed, "bufferView", -1); } @@ -340,6 +363,8 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { if has_min { sampler.min_filter = cast(GLTF_Min_Filter) parse_int(min_filter); } + + sampler.extras = cast(*void, get_object(sampler_parsed, "extras")); } @@ -354,6 +379,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { defer array_add(*data.animations, animation); animation.name = get_string(animation_sampler, "name"); + animation.extras = cast(*void, get_object(animation_sampler, "extras")); samplers := get_array(animation_sampler, "samplers"); for sampler_parsed: samplers { @@ -408,6 +434,7 @@ gltf_parse_string :: (gltf_buffer: string) -> GLTF_Data { defer array_add(*data.materials, material); material.name = get_string(material_parsed, "name"); + material.extras = cast(*void, get_object(material_parsed, "extras")); // // Metallic Roughness. @@ -766,8 +793,111 @@ read_buffer_from_accessor :: (gltf_data: *GLTF_Data, accessor: GLTF_Accessor, li } } - - +// Arrays allocate memory +get_extra :: (extras: GLTF_Extras, name: string, $T: Type) -> bool, T +{ + #run { + SUPPORTED_TYPES :: Type.[ + float, + float64, + bool, + string, + [] float, + [] float64, + [] bool, + [] string + ]; + is_supported: bool; + for SUPPORTED_TYPES { + if T == it { + is_supported = true; + break; + } + } + assert(is_supported, "Type % is an unsupported type for get_extra\nSupported types:\n%\n", T, SUPPORTED_TYPES); + } + + extra: T; + if !extras return false, extra; + + extra_json: JSON_Value; + success: bool; + + extras_object := cast(*Table(string, JSON_Value), extras); + success, extra_json = table_find(extras_object, name); + if !success return false, extra; + + if extra_json.type == { + case .NULL; + return false, extra; + case .OBJECT; + return false, extra; + } + + #if T == { + case float; + if extra_json.type != .NUMBER return false, extra; + extra = get_as(extra_json, float); + case float64; + if extra_json.type != .NUMBER return false, extra; + extra = get_as(extra_json, float64); + case bool; + if extra_json.type != .BOOLEAN return false, extra; + extra = get_as(extra_json, bool); + case string; + if extra_json.type != .STRING return false, extra; + extra = get_as(extra_json, string); + // Maybe I can do some Type_Info and Type_Info_Array checking here + // but just doing a resizable call and setting it back to the result + // looks more concise? + case [] float; + if extra_json.type != .ARRAY return false, extra; + json_arr := get_as(extra_json, [] JSON_Value); + if json_arr.count < 1 || json_arr[0].type != .NUMBER + return false, extra; + arr := resizable(extra); + array_reserve(*arr, json_arr.count); + for json_arr { + array_add(*arr, cast(float, it.number)); + } + extra = arr; + case [] float64; + if extra_json.type != .ARRAY return false, extra; + json_arr := get_as(extra_json, [] JSON_Value); + if json_arr.count < 1 || json_arr[0].type != .NUMBER + return false, extra; + arr := resizable(extra); + array_reserve(*arr, json_arr.count); + for json_arr { + array_add(*arr, it.number); + } + extra = arr; + case [] bool; + if extra_json.type != .ARRAY return false, extra; + json_arr := get_as(extra_json, [] JSON_Value); + if json_arr.count < 1 || json_arr[0].type != .BOOLEAN + return false, extra; + arr := resizable(extra); + array_reserve(*arr, json_arr.count); + for json_arr { + array_add(*arr, it.boolean); + } + extra = arr; + case [] string; + if extra_json.type != .ARRAY return false, extra; + json_arr := get_as(extra_json, [] JSON_Value); + if json_arr.count < 1 || json_arr[0].type != .STRING + return false, extra; + arr := resizable(extra); + array_reserve(*arr, json_arr.count); + for json_arr { + array_add(*arr, it.str); + } + extra = arr; + } + + return success, extra; +} /* * @@ -826,6 +956,8 @@ GLTF_Node :: struct { weights := -1; //The index of the light referenced by this node. light := -1; + // Application-specific data. + extras : GLTF_Extras; } // A buffer points to binary geometry, animation, or skins. @@ -838,6 +970,8 @@ GLTF_Buffer :: struct { byte_length : int; // Buffer's data once gltf_load_data was called. data : string; + // Application-specific data. + extras : GLTF_Extras; } // A view into a buffer generally representing a subset of the buffer. @@ -854,6 +988,8 @@ GLTF_Buffer_View :: struct { // The hint representing the intended GPU buffer type // to use with this buffer view. target: GLTF_Target; + // Application-specific data. + extras : GLTF_Extras; } /// A typed view into a buffer view that contains raw binary data. @@ -877,6 +1013,8 @@ GLTF_Accessor :: struct { // @todo: Would be better with tagged union for this. min : GLTF_Min_Max_Value; max : GLTF_Min_Max_Value; + // Application-specific data. + extras : GLTF_Extras; } // Refer to a minimum or maximum value. @@ -896,6 +1034,8 @@ GLTF_Scene :: struct { name : string; // The indices of each root node. nodes : [..] int; + // Application-specific data. + extras : GLTF_Extras; } /// Joints and matrices defining a skin. @@ -909,6 +1049,8 @@ GLTF_Skin :: struct { skeleton := -1; // Indices of skeleton nodes, used as joints in this skin. joints : [..] int; + // Application-specific data. + extras : GLTF_Extras; } @@ -959,6 +1101,8 @@ GLTF_Metallic_Roughness :: struct { // The metallic-roughness texture. has_metallic_roughness := false; metallic_roughness_texture : GLTF_Texture_Info; + // Application-specific data. + extras : GLTF_Extras; } // The material appearance of a primitive. @@ -1003,6 +1147,8 @@ GLTF_Material :: struct { // @Note: from khr_materials_transmission extension. has_transmission := false; transmission_texture : GLTF_Texture_Info; + // Application-specific data. + extras : GLTF_Extras; } // The material’s alpha rendering mode enumeration specifying @@ -1031,6 +1177,8 @@ GLTF_Texture :: struct { // When undefined, an extension or other mechanism should supply // an alternate texture source, otherwise behavior is undefined. source := -1; + // Application-specific data. + extras : GLTF_Extras; } /// Image data used to create a texture. @@ -1047,6 +1195,8 @@ GLTF_Image :: struct { // The image's data calculated from the buffer/buffer_view. // Only there if glb file is loaded. data : string; + // Application-specific data. + extras : GLTF_Extras; } GLTF_Wrap_Mode :: enum u32 { @@ -1079,6 +1229,8 @@ GLTF_Sampler :: struct { wrap_s := GLTF_Wrap_Mode.REPEAT; // T (U) wrapping mode. wrap_t := GLTF_Wrap_Mode.REPEAT; + // Application-specific data. + extras : GLTF_Extras; } GLTF_Attributes :: struct { @@ -1200,6 +1352,8 @@ GLTF_Animation :: struct { // An animation sampler combines timestamps with a sequence of output // values and defines an interpolation algorithm. samplers: [..] GLTF_Animation_Sampler; + // Application-specific data. + extras : GLTF_Extras; } // Geometry to be rendered with the given material. @@ -1211,6 +1365,10 @@ GLTF_Primitive :: struct { indices_accessor := -1; // The index of the material to apply to this primitive when rendering. material := -1; + // An array of morph targets. + targets: [..] GLTF_Morph_Target; + // Application-specific data. + extras : GLTF_Extras; } // A set of primitives to be rendered. @@ -1220,6 +1378,8 @@ GLTF_Mesh :: struct { name : string; // An array of primitives, each defining geometry to be rendered. primitives : [..] GLTF_Primitive; + // Application-specific data. + extras : GLTF_Extras; } // Metadata about the glTF asset. @@ -1230,6 +1390,8 @@ GLTF_Asset :: struct { generator : string; // A copyright message suitable for display to credit the content creator. copyright : string; + // Application-specific data. + extras : GLTF_Extras; } // A camera’s projection. @@ -1238,6 +1400,8 @@ GLTF_Asset :: struct { GLTF_Camera :: struct { name : string; type : enum { PERSPECTIVE; ORTHOGRAPHIC; }; + // Application-specific data. + extras : GLTF_Extras; // A perspective camera containing properties to create a // perspective projection matrix. @@ -1326,6 +1490,14 @@ GLTF_Light_Spot :: struct { outer_cone_angle := PI / 4; } +GLTF_Morph_Target :: struct { + position_accessor := -1; + normal_accessor := -1; + tangent_accessor := -1; +} + +GLTF_Extras :: *void; + #scope_file; get_buffer_data :: (gltf_data: *GLTF_Data, accessor: GLTF_Accessor, $type: Type) -> [..]type { @@ -1586,6 +1758,11 @@ get_string :: (json_val: JSON_Value, key: string) -> string { return copy_string(str); } +get_object :: (json_val: JSON_Value, key: string) -> *JSON_Object { + val := get(json_val, key, .OBJECT); + return val.object; +} + get :: (json_val : JSON_Value, key : string, expected_type: JSON_Type) -> JSON_Value, bool { @@ -1593,7 +1770,7 @@ get :: (json_val : JSON_Value, assert(json_val.type == .OBJECT); table := json_val.object; - val, success := table_find(table, key); + success, val := table_find(table, key); // Default value when value was not found. if !success { @@ -1630,4 +1807,4 @@ crash :: (msg: string, args: ..Any) { #import "Hash_Table"; #import "Math"; -#import, dir "modules/jaison"; +#import, dir "modules/jaison"; \ No newline at end of file diff --git a/modules/jaison/generic.jai b/modules/jaison/generic.jai index 415b217..edf0c5d 100644 --- a/modules/jaison/generic.jai +++ b/modules/jaison/generic.jai @@ -97,7 +97,7 @@ print_val :: (using val: JSON_Value) { for array print_val(it); print("]"); case .OBJECT; - print("%", < T { } else if T == JSON_Object { return #string END assert(val.type == .OBJECT, "Expected a % but got %", T, val.type); - return < JSON_Value, remainder: string, success: boo result.array, remainder, success = parse_array(remainder); case #char "{"; obj := cast(*JSON_Object) alloc(size_of(JSON_Object)); - < result: JSON_Object, remainder: string, success -#import "Compiler"; +#import "Compiler"; \ No newline at end of file diff --git a/modules/jaison/module.jai b/modules/jaison/module.jai index d65da4d..240485e 100644 --- a/modules/jaison/module.jai +++ b/modules/jaison/module.jai @@ -224,4 +224,4 @@ unescape :: (str: string) -> result: string, success: bool { #import "Hash_Table"; #import "IntroSort"; -#import, dir "../unicode_utils"; +#import, dir "../unicode_utils"; \ No newline at end of file diff --git a/modules/jaison/typed.jai b/modules/jaison/typed.jai index 5851aef..9fe395a 100644 --- a/modules/jaison/typed.jai +++ b/modules/jaison/typed.jai @@ -40,7 +40,7 @@ json_parse_file :: (filename: string, $T: Type, ignore_unknown := true, rename : json_write_native :: (builder: *String_Builder, data: *void, info: *Type_Info, indent_char := "\t", ignore := ignore_by_note, rename := rename_by_note, level := 0) { if info.type == { case .BOOL; - append(builder, ifx <<(cast(*bool) data) "true" else "false"); + if cast(bool, data) { append(builder, "true"); } else { append(builder, "false"); } case .INTEGER; #through; case .FLOAT; any_val: Any; @@ -56,7 +56,7 @@ json_write_native :: (builder: *String_Builder, data: *void, info: *Type_Info, i print_item_to_builder(builder, any_val); append(builder, #char "\""); case .STRING; - json_append_escaped(builder, <<(cast(*string) data)); + json_append_escaped(builder, cast(string, data.*)); case .ARRAY; info_array := cast(*Type_Info_Array) info; element_size := info_array.element_type.runtime_size; @@ -66,10 +66,10 @@ json_write_native :: (builder: *String_Builder, data: *void, info: *Type_Info, i array_data := data; array_count := info_array.array_count; if info_array.array_count == -1 { - array_count = << cast(*s64) data; + array_count = cast(s64, data.*); array_dest: **void = data + 8; - array_data = << array_dest; + array_data = array_dest.*; } append(builder, "["); @@ -93,7 +93,7 @@ json_write_native :: (builder: *String_Builder, data: *void, info: *Type_Info, i struct_info := cast(*Type_Info_Struct) info; if is_generic_json_value(info) { value := cast(*JSON_Value) data; - json_write_json_value(builder, <