diff --git a/.gitignore b/.gitignore
index 1a33935..c35714c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ examples/viewer/viewer
test/tinyobj_tests
*.dSYM
*.o
+/nd_config/Working Data
diff --git a/doc/classes/C/COMPATtinyobj_attrib_t-Summary.js b/doc/classes/C/COMPATtinyobj_attrib_t-Summary.js
new file mode 100644
index 0000000..7d8a2d4
--- /dev/null
+++ b/doc/classes/C/COMPATtinyobj_attrib_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:COMPATtinyobj_attrib_t","COMPATtinyobj_attrib_t");NDSummary.OnSummaryLoaded("CClass:COMPATtinyobj_attrib_t",[["C/C++","C"]],[["Constants","Constant"],["Functions","Function"],["Groups","Group"],["Structs","Struct"]],[[40,0,3,"COMPATtinyobj_attrib_t"],[41,0,2,"String handling","String_handling"],[42,0,1,"String handling macros","String_handling_macros"],[43,0,1,,"IS_SPACE"],[44,0,1,,"IS_DIGIT"],[45,0,1,,"IS_NEW_LINE"],[120,0,1,"is_line_ending","is_line_ending"],[47,0,1,"skip_space","skip_space"],[48,0,1,"skip_space_and_cr","skip_space_and_cr"],[49,0,1,"length_until_space","length_until_space"],[50,0,1,"until_space","until_space"],[51,0,1,"until_space_cr_slash","until_space_cr_slash"],[52,0,1,"length_until_newline_comment_space","length_until_newline_comment_space"],[53,0,1,"length_until_line_feed","length_until_line_feed"],[123,0,1,"strdup_ml","strdup_ml"],[55,0,1,"strndup","strndup"],[124,0,1,"dynamic_fgets","dynamic_fgets"],[57,0,2,"Triplet handling","Triplet_handling"],[34,0,1,"fixIndex","fixIndex"],[59,0,1,"parseRawTriple","parseRawTriple"],[60,0,2,"Integer/Double/Float handling","Integer/Double/Float_handling"],[61,0,1,"parseInt","parseInt"],[62,0,1,"tryParseDouble_assemble","tryParseDouble_assemble"],[63,0,1,"tryParseDouble_integer","tryParseDouble_integer"],[125,0,1,"tryParseDouble","tryParseDouble"],[65,0,1,"try_parse_float","try_parse_float"],[66,0,1,"parseFloat","parseFloat"],[67,0,1,"parseFloat2","parseFloat2"],[68,0,1,"parseFloat3","parseFloat3"],[78,0,2,"TINYOBJ hashtable implementation","TINYOBJ_hashtable_implementation"],[70,0,0,"Hash table return codes","Hash_table_return_codes"],[71,0,0,,"TINYOBJ_HASH_TABLE_SUCCESS"],[72,0,0,,"TINYOBJ_HASH_TABLE_ERROR"],[73,0,0,"TINYOBJ_HASH_TABLE_DEFAULT_SIZE","TINYOBJ_HASH_TABLE_DEFAULT_SIZE"]]);
\ No newline at end of file
diff --git a/doc/classes/C/COMPATtinyobj_attrib_t-SummaryToolTips.js b/doc/classes/C/COMPATtinyobj_attrib_t-SummaryToolTips.js
new file mode 100644
index 0000000..e702c5f
--- /dev/null
+++ b/doc/classes/C/COMPATtinyobj_attrib_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:COMPATtinyobj_attrib_t",{40:"
",120:"",47:"",48:"",49:"",50:"",51:"",52:"",53:"",123:"",55:"",124:"",34:"",59:"",61:"",62:"",63:"",125:"",65:"",66:"",67:"",68:"",78:"",73:""});
\ No newline at end of file
diff --git a/doc/classes/C/COMPATtinyobj_attrib_t-ToolTips.js b/doc/classes/C/COMPATtinyobj_attrib_t-ToolTips.js
new file mode 100644
index 0000000..7e7c562
--- /dev/null
+++ b/doc/classes/C/COMPATtinyobj_attrib_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({31:"",59:"",66:"",77:"",125:""});
\ No newline at end of file
diff --git a/doc/classes/C/COMPATtinyobj_attrib_t.html b/doc/classes/C/COMPATtinyobj_attrib_t.html
new file mode 100644
index 0000000..dfdc04c
--- /dev/null
+++ b/doc/classes/C/COMPATtinyobj_attrib_t.html
@@ -0,0 +1,174 @@
+
+
+COMPATtinyobj_attrib_t
+
+
+
+
+
+
+
+
+
COMPATtinyobj_attrib_t
+
Object attributes (deprecated)
Fields
vertices Array of geometric vertices (x0, y0, z0, x1, y1, z1, ...)
num_vertices Number of vertices in 'vertices' (the actual array length is num_vertices*3)
normals Array of vertex normals (i0, j0, k0, i1, j1, k1, ...)
num_normals Number of vertices in 'normals' (the actual array length is num_normals*3)
texcoords Array of texture vertices (u0, w0, u1, w1, ...)
num_texcoords Number of vertices in 'texcoords' (the actual array length is num_normals*2)
faces Array of faces (containing tinyobj_vertex_index_t information)
num_faces Length of 'faces'
face_num_verts Array of number of vertices in each face (3 for triangulated faces)
num_face_num_verts Total number of triangles in this object (length of face_num_verts)
material_ids Array with the id of the material for each face (length is num_faces)
+
+
+
+
+
+
String handling macros
+
IS_SPACEIs this character a spacing character ' ' or '\t'
IS_DIGITIs this character a digit
IS_NEW_LINEIs this character a new line character '\r','\n','\0'
+
+
+
+
is_line_ending
+
+
Is given line ending in this character?
Returns
True if line is ending
+
+
+
+
skip_space
+
+
Skips the next spacing characters in given token
+
+
+
+
skip_space_and_cr
+
+
Skips the next spacing characters and also carriage return in given token
+
+
+
+
length_until_space
+
+
Calculates and returns the length until next space character in this token
Parameters
tokenconst char *
Token to be used
nsize_t
Maximum number of characters to count
Returns
Length until next space character
+
+
+
+
until_space
+
+
Skips the next spacing characters
'\0',' ', '\t', '\r'
+
+
+
+
until_space_cr_slash
+
+
Skips the next characters in given token
'\0','/', ' ', '\t', '\r'
+
+
+
+
length_until_newline_comment_space
+
+
Returns the length until the next characters:
\n \r\n \0 ' ' \t # Parameters
tokenconst char *
Buffer
nsize_t
Maximum number of characters to count
Returns
Length until next character
+
+
+
+
length_until_line_feed
+
+
Returns the length until the next line feed
Assumes token[n-1] = '\0'
Parameters
tokenconst char *
Buffer
nsize_t
Maximum number of characters to count
+
+
+
+
strdup_ml
+
+
Duplicates 'max_length' characters of given string (strdup with max length specified)
Parameters
sconst char *
String to be duplicated
max_lengthsize_t
Maximum length to be duplicated
Returns
Duplicated string (should be freed by the caller )
+
+
+
+
strndup
+
+
strndup implementation for non-GNU compliant compilers
+
+
+
+
dynamic_fgets
+
+
fgets with a dynamic buffer
+
+
+
+
+
+
fixIndex
+
+
Converts given absolute index, from triplet to zero-base, supports relative indices
See
Parameters
idxint
Index to be converted
nsize_t
Position of this couple/tuple regarding to the vertex type of this index
Returns
+
+
+
+
parseRawTriple
+
+
Parses raw triplets of given tokens
v, v/vt/vn, v//vn, v/vt Valid triplets: <x> 1//1 <x> 1//1 2//2 3//3 4//4 <x> 1/1/1 2/2/2 3/3/3 4/4/4 The following are examples of illegal statements <x> 1/1/1 2/2/2 3//3 4//4 <x> 1/ 1/1 2/2/2 3/3/3 4/4/4 Parameters
tokenconst char **
Token to parse
Returns
Indices parsed from given token, if one of the indices is not present it's equal to TINYOBJ_INVALID_INDEX
+
+
+
+
Integer/Double/Float handling
+
+
+
+
parseInt
+
+
Returns equivalent integer of next non-space character, the token is advanced until next space
+
+
+
+
tryParseDouble_assemble
+
+
Last step of tryParseDouble
Assembles given information into double
Parameters
signchar
Sign of this double
mantissadouble
Mantissa
exp_signchar
Exponent sign
exponentint
Exponent
Returns
Assembled double
+
+
+
+
tryParseDouble_integer
+
+
Part of tryParseDouble
Parses integer part of a double (advances curr)
Parameters
currconst char **
Current position in a buffer
s_endconst char *
End of the buffer
signchar *
[out] Sign to be filled
integerint *
[out] Integer to be filled
Returns
True integer was successfuly parsed, false if nothing was read
+
+
+
+
tryParseDouble
+
+
Tries to parse a floating point number located at s.
Parses the following EBNF grammar: sign = "+" | "-" ; END = ? anything not in digit ? digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; integer = [sign] , digit , {digit} ; decimal = integer , ["." , integer] ; float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; Valid strings are for example: -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 The function is greedy and will parse until any of the following happens:
The following situations triggers a failure:
s >= s_end.
parse failure.
Parameters
sconst char *
String to be parsed
s_endconst char *
Location in the string where reading should halt, e.g. the end of
the string result [out] Parsed double
Returns If the parsing is a success, result is set to the parsed value and true is returned.
+
+
+
+
try_parse_float
+
+
Tries to parse next float, if there's none fails
Parameters
tokenconst char **
Buffer to be read
resultfloat *
[out] Result, not set if return is false
Returns
If the parsing is a success, result is set to the parsed value and true is returned.
+
+
+
+
parseFloat
+
+
Returns equivalent float of next non-space character, the token is advanced until next space
+
+
+
+
parseFloat2
+
+
Parses the two next floats in given token
See
parseFloat
+
+
+
+
parseFloat3
+
+
Parses the three next floats in given token
See
parseFloat
+
+
+
+
TINYOBJ hashtable implementation
+
+
+
+
+
Hash table return codes
+
TINYOBJ_HASH_TABLE_SUCCESSSuccess
TINYOBJ_HASH_TABLE_ERRORFailure
+
+
+
+
TINYOBJ_HASH_TABLE_DEFAULT_SIZE
+
Default starting size for tinyobj own hash table implementation tables
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/Command-Summary.js b/doc/classes/C/Command-Summary.js
new file mode 100644
index 0000000..4fca7df
--- /dev/null
+++ b/doc/classes/C/Command-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:Command","Command");NDSummary.OnSummaryLoaded("CClass:Command",[["C/C++","C"]],[["Structs","Struct"]],[[118,0,0,"Command"]]);
\ No newline at end of file
diff --git a/doc/classes/C/Command-SummaryToolTips.js b/doc/classes/C/Command-SummaryToolTips.js
new file mode 100644
index 0000000..1af055f
--- /dev/null
+++ b/doc/classes/C/Command-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:Command",{118:""});
\ No newline at end of file
diff --git a/doc/classes/C/Command-ToolTips.js b/doc/classes/C/Command-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/Command-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/Command.html b/doc/classes/C/Command.html
new file mode 100644
index 0000000..07e66c1
--- /dev/null
+++ b/doc/classes/C/Command.html
@@ -0,0 +1,16 @@
+
+
+Command
+
+
+
+
+
+
+
+
+
Command
+
Command information used to parse obj files <tinyobj_parse_obj_line>
Fields
type Command
info Specific command information
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/Command/s_group_information-Summary.js b/doc/classes/C/Command/s_group_information-Summary.js
new file mode 100644
index 0000000..f145162
--- /dev/null
+++ b/doc/classes/C/Command/s_group_information-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:Command.s_group_information","s_group_information");NDSummary.OnSummaryLoaded("CClass:Command.s_group_information",[["C/C++","C"]],[["Structs","Struct"]],[[122,0,0,"Command. s_group_information"]]);
\ No newline at end of file
diff --git a/doc/classes/C/Command/s_group_information-SummaryToolTips.js b/doc/classes/C/Command/s_group_information-SummaryToolTips.js
new file mode 100644
index 0000000..a82f1c7
--- /dev/null
+++ b/doc/classes/C/Command/s_group_information-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:Command.s_group_information",{122:""});
\ No newline at end of file
diff --git a/doc/classes/C/Command/s_group_information-ToolTips.js b/doc/classes/C/Command/s_group_information-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/Command/s_group_information-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/Command/s_group_information.html b/doc/classes/C/Command/s_group_information.html
new file mode 100644
index 0000000..a54a38d
--- /dev/null
+++ b/doc/classes/C/Command/s_group_information.html
@@ -0,0 +1,16 @@
+
+
+s_group_information
+
+
+
+
+
+
+
+
+
Command. s_group_information
+
Grouping and Display/Render attributes
Fields
Name Name of group
len Group name length
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/Command/u_information-Summary.js b/doc/classes/C/Command/u_information-Summary.js
new file mode 100644
index 0000000..0814b16
--- /dev/null
+++ b/doc/classes/C/Command/u_information-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:Command.u_information","u_information");NDSummary.OnSummaryLoaded("CClass:Command.u_information",[["C/C++","C"]],[["Structs","Struct"]],[[85,0,0,"Command. u_information"]]);
\ No newline at end of file
diff --git a/doc/classes/C/Command/u_information-SummaryToolTips.js b/doc/classes/C/Command/u_information-SummaryToolTips.js
new file mode 100644
index 0000000..d262228
--- /dev/null
+++ b/doc/classes/C/Command/u_information-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:Command.u_information",{85:""});
\ No newline at end of file
diff --git a/doc/classes/C/Command/u_information-ToolTips.js b/doc/classes/C/Command/u_information-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/Command/u_information-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/Command/u_information.html b/doc/classes/C/Command/u_information.html
new file mode 100644
index 0000000..13c17f4
--- /dev/null
+++ b/doc/classes/C/Command/u_information.html
@@ -0,0 +1,16 @@
+
+
+u_information
+
+
+
+
+
+
+
+
+
Command. u_information
+
Specific command information
Fields
v Geometric vertex <COMMAND_V>
vn Vertex normal <COMMAND_VN>
vt Texture vertex <COMMAND_VT>
vp Parameter space vertex <COMMAND_VP>
f Face <COMMAND_F>
l Line <COMMAND_L>
p Point <COMMAND_P>
g Group <COMMAND_G>
o Object <COMMAND_O>
usemtl Use material <COMMAND_USEMTL>
mtllib Material lib (file) <COMMAND_MTLLIB>
smoothing_id Smoothing group <COMMAND_S>
See
<CommandType>
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation-Summary.js b/doc/classes/C/CommandInformation-Summary.js
new file mode 100644
index 0000000..057f0fd
--- /dev/null
+++ b/doc/classes/C/CommandInformation-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:CommandInformation","CommandInformation");NDSummary.OnSummaryLoaded("CClass:CommandInformation",[["C/C++","C"]],[["Structs","Struct"]],[[119,0,0,"CommandInformation"]]);
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation-SummaryToolTips.js b/doc/classes/C/CommandInformation-SummaryToolTips.js
new file mode 100644
index 0000000..532f678
--- /dev/null
+++ b/doc/classes/C/CommandInformation-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:CommandInformation",{119:""});
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation-ToolTips.js b/doc/classes/C/CommandInformation-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/CommandInformation-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation.html b/doc/classes/C/CommandInformation.html
new file mode 100644
index 0000000..848b211
--- /dev/null
+++ b/doc/classes/C/CommandInformation.html
@@ -0,0 +1,16 @@
+
+
+CommandInformation
+
+
+
+
+
+
+
+
+
CommandInformation
+
General information regarding the commands of a given file
Fields
command_list Array of commands
mtllib_line_index Index of MLLIB in command list (-1 no command)
count command_list count
counter Counter of command types
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation/s_counter-Summary.js b/doc/classes/C/CommandInformation/s_counter-Summary.js
new file mode 100644
index 0000000..5ad1b70
--- /dev/null
+++ b/doc/classes/C/CommandInformation/s_counter-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:CommandInformation.s_counter","s_counter");NDSummary.OnSummaryLoaded("CClass:CommandInformation.s_counter",[["C/C++","C"]],[["Functions","Function"],["Groups","Group"],["Structs","Struct"]],[[84,0,2,"CommandInformation. s_counter"],[69,0,1,"Object handling (.obj)","Object_handling"],[146,0,0,"tinyobj_obj_free_point","tinyobj_obj_free_point"],[147,0,0,"tinyobj_obj_parse_point","tinyobj_obj_parse_point"],[148,0,0,"tinyobj_triplet_list_grow","tinyobj_triplet_list_grow"],[149,0,0,"tinyobj_triplet_list_new","tinyobj_triplet_list_new"],[150,0,0,"tinyobj_obj_free_line","tinyobj_obj_free_line"],[151,0,0,"tinyuobj_obj_parse_line","tinyuobj_obj_parse_line"],[152,0,0,"tinyobj_obj_free_face","tinyobj_obj_free_face"],[83,0,0,"tinyobj_obj_parse_face","tinyobj_obj_parse_face"],[154,0,0,"tinyobj_obj_parse_vertex","tinyobj_obj_parse_vertex"],[138,0,0,"tinyobj_parse_obj_line","tinyobj_parse_obj_line"],[139,0,0,"tinyobj_parse_obj_line_traverse","tinyobj_parse_obj_line_traverse"],[157,0,0,"tinyobj_command_info_free","tinyobj_command_info_free"],[158,0,0,"tinyobj_shape_construct","tinyobj_shape_construct"],[159,0,0,"tinyobj_shape_free","tinyobj_shape_free"],[160,0,0,"tinyobj_attrib_construct","tinyobj_attrib_construct"],[54,0,1,"Aplication Programming Interface (.obj)","Aplication_Programming_Interface"],[161,0,0,"tinyobj_attrib_free","tinyobj_attrib_free"],[162,0,0,"tinyobj_attrib_init","tinyobj_attrib_init"],[140,0,0,"tinyobj_parse_obj","tinyobj_parse_obj"],[56,0,1,"Compatibility with older versions (deprecated)","Compatibility_with_older_versions"],[164,0,0,"tinyobj_attrib_free_compat","tinyobj_attrib_free_compat"],[39,0,0,"tinyobj_new2old","tinyobj_new2old"]]);
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation/s_counter-SummaryToolTips.js b/doc/classes/C/CommandInformation/s_counter-SummaryToolTips.js
new file mode 100644
index 0000000..5f6c67b
--- /dev/null
+++ b/doc/classes/C/CommandInformation/s_counter-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:CommandInformation.s_counter",{84:"",146:"",147:"",148:"",149:"",150:"",151:"",152:"",83:"",154:"",138:"",139:"",157:"",158:"",159:"",160:"",161:"",162:"",140:"",164:"",39:""});
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation/s_counter-ToolTips.js b/doc/classes/C/CommandInformation/s_counter-ToolTips.js
new file mode 100644
index 0000000..670af77
--- /dev/null
+++ b/doc/classes/C/CommandInformation/s_counter-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({4:"",5:"",8:"",10:"",11:"",13:"",14:"",15:"",31:"",32:"",33:"",35:"",37:"",38:"",40:"",75:"",118:"",119:""});
\ No newline at end of file
diff --git a/doc/classes/C/CommandInformation/s_counter.html b/doc/classes/C/CommandInformation/s_counter.html
new file mode 100644
index 0000000..41e65ee
--- /dev/null
+++ b/doc/classes/C/CommandInformation/s_counter.html
@@ -0,0 +1,147 @@
+
+
+s_counter
+
+
+
+
+
+
+
+
+
CommandInformation. s_counter
+
Counter of command types
Fields
v Count of geometric vertices
vn Count of vertex normals
vt Count of texture vertices
vp Count of parameter space vertices
f Count of faces
l Count of lines
p Count of points
shapes Shapes
+
+
+
+
Object handling (.obj)
+
+
+
+
tinyobj_obj_free_point
+
+
Frees allocated data of a given point
+
+
+
+
tinyobj_obj_parse_point
+
+
Parses a point from token ('p')
Parameters
command Command information to be filled
tokenconst char **
Token to be parsed
Returns
+
+
+
+
tinyobj_triplet_list_grow
+
+
Grows the given triplet list to a new size If the growth fails the list is unchanged
Parameters
list List to grow
new_lensize_t
New length
Returns
True if successful, false otherwise
+
+
+
+
tinyobj_triplet_list_new
+
+
Allocates memory for a new triplet list
Parameters
lensize_t
Initial length of list
Returns
Pointer to new list when successful, NULL if failure
+
+
+
+
tinyobj_obj_free_line
+
+
Frees allocated data of a given line
+
+
+
+
tinyuobj_obj_parse_line
+
Parses a line from token ('l')
Parameters
command Command information to be filled
token Token to be parsed
Returns
+
+
+
+
tinyobj_obj_free_face
+
+
Frees allocated data of a given face
+
+
+
+
tinyobj_obj_parse_face
+
+
Parses a face from token ('f') and fills given command with face information
Parameters
command Command information to be filled
tokenconst char **
Token to be parsed
triangulateint
(bool) Should this face be triangulated? TINYOBJ_FLAG_TRIANGULATE
Returns
+
+
+
+
tinyobj_obj_parse_vertex
+
+
Parses a vertex from token ('v')
v, vn, vt, vp Parameters
command Command information to be filled
tokenconst char **
Token to be parsed
Returns
+
+
+
+
tinyobj_parse_obj_line
+
+
Parses line of an .obj file
Parameters
command_info Command information list
posint
Current position in command information
pconst char *
String containing line
p_lensize_t
Line length
flagsint
Reading flags
Returns
+
+
+
+
tinyobj_parse_obj_line_traverse
+
+
Traverses and parses all lines of given buffer
Parameters
bufconst char *
Buffer to be parsed
lensize_t
Buffer length
command_info Command information to be filled
flagsunsigned int
Parsing flags
Returns
+
+
+
+
tinyobj_command_info_free
+
+
Frees given command information
+
+
+
+
tinyobj_shape_construct
+
+
Constructs shape information
Parameters
shapes [in/out] Pointer to be filled
num_shapesunsigned int *
[out] Length of shapes
command_info Information used to construct the shapes
+
+
+
+
tinyobj_shape_free
+
+
Frees shape data
Parameters
shapes Shape information to be freed
num_shapesunsigned int
Length of shapes
+
+
+
+
tinyobj_attrib_construct
+
+
Constructs attribute data with given information
Parameters
attrib [in/out] Attribute to be filled
command_info Information used to fill attrib
material_table Material table used (can be NULL)
+
+
+
+
Aplication Programming Interface (.obj)
+
+
+
+
tinyobj_attrib_free
+
+
+
+
+
+
tinyobj_attrib_init
+
+
Initializes given attribute information to default values
+
+
+
+
tinyobj_parse_obj
+
+
Parses .obj file
Parameters
attrib [in/out] Attributes to be filled
shapes [in/out] Shapes to be filled
num_shapesunsigned int *
[in/out] Length of shapes
materials_out [in/out] Materials to be filled
num_materials_outunsigned int *
[in/out] Length of materials
bufconst char *
Buffer to be read
lensize_t
Buffer length
flagsunsigned int
Parsing flags
Returns
+
+
+
+
Compatibility with older versions (deprecated)
+
+
+
+
tinyobj_attrib_free_compat
+
+
+
+
+
+
tinyobj_new2old
+
+
Converts new attribute format to the old one This expects that the faces were triangulated
Parameters
attrib [in] Attribute to be converted
out_attrib [out] Attribute to be filled
Returns
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/hash_table_entry_t-Summary.js b/doc/classes/C/hash_table_entry_t-Summary.js
new file mode 100644
index 0000000..ea76b12
--- /dev/null
+++ b/doc/classes/C/hash_table_entry_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:hash_table_entry_t","hash_table_entry_t");NDSummary.OnSummaryLoaded("CClass:hash_table_entry_t",[["C/C++","C"]],[["Structs","Struct"]],[[74,0,0,"hash_table_entry_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/hash_table_entry_t-SummaryToolTips.js b/doc/classes/C/hash_table_entry_t-SummaryToolTips.js
new file mode 100644
index 0000000..cbe3c27
--- /dev/null
+++ b/doc/classes/C/hash_table_entry_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:hash_table_entry_t",{74:""});
\ No newline at end of file
diff --git a/doc/classes/C/hash_table_entry_t-ToolTips.js b/doc/classes/C/hash_table_entry_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/hash_table_entry_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/hash_table_entry_t.html b/doc/classes/C/hash_table_entry_t.html
new file mode 100644
index 0000000..440d0f7
--- /dev/null
+++ b/doc/classes/C/hash_table_entry_t.html
@@ -0,0 +1,16 @@
+
+
+hash_table_entry_t
+
+
+
+
+
+
+
+
+
hash_table_entry_t
+
Entries used in tinyobj's own hash table implementation
Fields
hash Hash value
filled Is this entry filled
value Value
next Next entry
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_attrib_t-Summary.js b/doc/classes/C/tinyobj_attrib_t-Summary.js
new file mode 100644
index 0000000..9b9933b
--- /dev/null
+++ b/doc/classes/C/tinyobj_attrib_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_attrib_t","tinyobj_attrib_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_attrib_t",[["C/C++","C"]],[["Structs","Struct"]],[[37,0,0,"tinyobj_attrib_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_attrib_t-SummaryToolTips.js b/doc/classes/C/tinyobj_attrib_t-SummaryToolTips.js
new file mode 100644
index 0000000..c3c6ec6
--- /dev/null
+++ b/doc/classes/C/tinyobj_attrib_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_attrib_t",{37:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_attrib_t-ToolTips.js b/doc/classes/C/tinyobj_attrib_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_attrib_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_attrib_t.html b/doc/classes/C/tinyobj_attrib_t.html
new file mode 100644
index 0000000..d80c541
--- /dev/null
+++ b/doc/classes/C/tinyobj_attrib_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_attrib_t
+
+
+
+
+
+
+
+
+
tinyobj_attrib_t
+
Object attributes .obj format
Fields
v Geometric vertices
v_count Geometric vertex count
vn Vertex normals
vn_count Vertex normal count
vt Texture vertices
vt_count Texture vertex count
vp Parameter space vertices
vp_count Parameter space vertex count
f Object faces
f_count Face count
triangle_count_total Total count of triangles
l Lines
l_count Line count
p Points
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_coefficient_t-Summary.js b/doc/classes/C/tinyobj_coefficient_t-Summary.js
new file mode 100644
index 0000000..0b5d64c
--- /dev/null
+++ b/doc/classes/C/tinyobj_coefficient_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_coefficient_t","tinyobj_coefficient_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_coefficient_t",[["C/C++","C"]],[["Structs","Struct"]],[[36,0,0,"tinyobj_coefficient_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_coefficient_t-SummaryToolTips.js b/doc/classes/C/tinyobj_coefficient_t-SummaryToolTips.js
new file mode 100644
index 0000000..f79002f
--- /dev/null
+++ b/doc/classes/C/tinyobj_coefficient_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_coefficient_t",{36:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_coefficient_t-ToolTips.js b/doc/classes/C/tinyobj_coefficient_t-ToolTips.js
new file mode 100644
index 0000000..de02670
--- /dev/null
+++ b/doc/classes/C/tinyobj_coefficient_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({5:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_coefficient_t.html b/doc/classes/C/tinyobj_coefficient_t.html
new file mode 100644
index 0000000..4593432
--- /dev/null
+++ b/doc/classes/C/tinyobj_coefficient_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_coefficient_t
+
+
+
+
+
+
+
+
+
tinyobj_coefficient_t
+
RGB Color coefficient information used in materials tinyobj_material_t
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_face_t-Summary.js b/doc/classes/C/tinyobj_face_t-Summary.js
new file mode 100644
index 0000000..1395a54
--- /dev/null
+++ b/doc/classes/C/tinyobj_face_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_face_t","tinyobj_face_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_face_t",[["C/C++","C"]],[["Structs","Struct"]],[[35,0,0,"tinyobj_face_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_face_t-SummaryToolTips.js b/doc/classes/C/tinyobj_face_t-SummaryToolTips.js
new file mode 100644
index 0000000..e0e2580
--- /dev/null
+++ b/doc/classes/C/tinyobj_face_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_face_t",{35:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_face_t-ToolTips.js b/doc/classes/C/tinyobj_face_t-ToolTips.js
new file mode 100644
index 0000000..17b2d7a
--- /dev/null
+++ b/doc/classes/C/tinyobj_face_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({77:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_face_t.html b/doc/classes/C/tinyobj_face_t.html
new file mode 100644
index 0000000..cb7d0cc
--- /dev/null
+++ b/doc/classes/C/tinyobj_face_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_face_t
+
+
+
+
+
+
+
+
+
tinyobj_face_t
+
Face (f) Specifies a face element and its vertex reference number
f v1/vt1/vn1 v2/vt2/vn2 fo (deprecated) Fields
triplet list A list of triplets contained in this face
v Geometric vertex (minimum of 3)
vt Texture vertex (optional)
vn Vertex normal (optional)
When an index is empty it is equal to TINYOBJ_INVALID_INDEX
count Count of triplets
length Length of triplet list
triangle_count Number of triangles in this face
material_id Material to be applied to this face (defaults to -1)
smoothing_id Smoothing group to be applied to this face (defaults to 0)
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t-Summary.js b/doc/classes/C/tinyobj_line_t-Summary.js
new file mode 100644
index 0000000..0bd177a
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_line_t","tinyobj_line_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_line_t",[["C/C++","C"]],[["Structs","Struct"]],[[33,0,0,"tinyobj_line_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t-SummaryToolTips.js b/doc/classes/C/tinyobj_line_t-SummaryToolTips.js
new file mode 100644
index 0000000..a366101
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_line_t",{33:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t-ToolTips.js b/doc/classes/C/tinyobj_line_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t.html b/doc/classes/C/tinyobj_line_t.html
new file mode 100644
index 0000000..8596857
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_line_t
+
+
+
+
+
+
+
+
+
tinyobj_line_t
+
Line (l) Specifies a line and its vertex reference numbers
l v1/vt1 v2/vt2 v3/vt3 Fields
couple_list List of couples contained in this line
count Count of couples
length Total length of couple list
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t/s_line_vertex_index-Summary.js b/doc/classes/C/tinyobj_line_t/s_line_vertex_index-Summary.js
new file mode 100644
index 0000000..261ed49
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t/s_line_vertex_index-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_line_t.s_line_vertex_index","s_line_vertex_index");NDSummary.OnSummaryLoaded("CClass:tinyobj_line_t.s_line_vertex_index",[["C/C++","C"]],[["Structs","Struct"]],[[26,0,0,"tinyobj_line_t. s_line_vertex_index"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t/s_line_vertex_index-SummaryToolTips.js b/doc/classes/C/tinyobj_line_t/s_line_vertex_index-SummaryToolTips.js
new file mode 100644
index 0000000..38ff58f
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t/s_line_vertex_index-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_line_t.s_line_vertex_index",{26:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t/s_line_vertex_index-ToolTips.js b/doc/classes/C/tinyobj_line_t/s_line_vertex_index-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t/s_line_vertex_index-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_line_t/s_line_vertex_index.html b/doc/classes/C/tinyobj_line_t/s_line_vertex_index.html
new file mode 100644
index 0000000..4b46a71
--- /dev/null
+++ b/doc/classes/C/tinyobj_line_t/s_line_vertex_index.html
@@ -0,0 +1,16 @@
+
+
+s_line_vertex_index
+
+
+
+
+
+
+
+
+
tinyobj_line_t. s_line_vertex_index
+
List of couples
Fields
v_idx Geometric vertex
vt_idx Texture vertex (optional)
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_t-Summary.js b/doc/classes/C/tinyobj_material_t-Summary.js
new file mode 100644
index 0000000..30dc6e5
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_material_t","tinyobj_material_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_material_t",[["C/C++","C"]],[["Structs","Struct"]],[[5,0,0,"tinyobj_material_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_t-SummaryToolTips.js b/doc/classes/C/tinyobj_material_t-SummaryToolTips.js
new file mode 100644
index 0000000..3027c41
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_material_t",{5:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_t-ToolTips.js b/doc/classes/C/tinyobj_material_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_t.html b/doc/classes/C/tinyobj_material_t.html
new file mode 100644
index 0000000..7643946
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_material_t
+
+
+
+
+
+
+
+
+
tinyobj_material_t
+
Material attribute .mtl format
Fields
name Material name
ambient Ambient color coefficient (reflectivity)
diffuse Diffuse color coefficient (reflectivity)
specular Specular color coefficient (reflectivity)
transmittance Transmittance color coefficient (reflectivity)
emission Emission color coefficient (reflectivity)
shininess Specular exponent
ior Index Of Refraction (ior) 'optical density'
dissolve Non-transparency to be alpha 'dissolve' (0.0f transparent-1.0f opaque)
illum Illumination model (0-10)
ambient_texname map_Ka
diffuse_texname map_Kd
specular_texname map_Ks
specular_highlight_texname map_Ns
bump_texname map_bump, bump
displacement_texname disp
alpha_texname map_d
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_table_t-Summary.js b/doc/classes/C/tinyobj_material_table_t-Summary.js
new file mode 100644
index 0000000..8f98910
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_table_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_material_table_t","tinyobj_material_table_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_material_table_t",[["C/C++","C"]],[["Constants","Constant"],["Enums","Enumeration"],["Functions","Function"],["Groups","Group"],["Structs","Struct"]],[[75,0,4,"tinyobj_material_table_t"],[79,0,3,"TINYOBJ hashtable implementation","TINYOBJ_hashtable_implementation"],[126,0,2,"hash_djb2","hash_djb2"],[80,0,2,"create_hash_table","create_hash_table"],[128,0,2,"destroy_hash_table","destroy_hash_table"],[129,0,2,"hash_table_insert_value","hash_table_insert_value"],[130,0,2,"hash_table_insert","hash_table_insert"],[131,0,2,"hash_table_find","hash_table_find"],[132,0,2,"hash_table_maybe_grow","hash_table_maybe_grow"],[58,0,2,"hash_table_exists","hash_table_exists"],[81,0,2,"hash_table_set","hash_table_set"],[135,0,2,"hash_table_get","hash_table_get"],[86,0,3,"UTHash interface to tinyobj","UTHash_interface_to_tinyobj"],[87,0,4,"tinyobj_material_table_t"],[143,0,3,"UTHash interface to tinyobj","UTHash_interface_to_tinyobj(2)"],[88,0,0,"tinyobj_reserved","tinyobj_reserved"],[89,0,2,"tinyobj_hash_find","tinyobj_hash_find"],[90,0,2,"tinyobj_hash_add","tinyobj_hash_add"],[91,0,2,"tinyobj_hash_free","tinyobj_hash_free"],[92,0,2,"tinyobj_hash_init","tinyobj_hash_init"],[82,0,3,"Material handling (.mtl)","Material_handling"],[94,0,2,"initMaterial","initMaterial"],[95,0,2,"tinyobj_mtl_parse_map","tinyobj_mtl_parse_map"],[96,0,2,"tinyobj_mtl_parse_optical","tinyobj_mtl_parse_optical"],[97,0,2,"tinyobj_mtl_parse_color","tinyobj_mtl_parse_color"],[136,0,2,"tinyobj_parse_and_index_mtl_file","tinyobj_parse_and_index_mtl_file"],[46,0,3,"Aplication Programming Interface (.mtl)","Aplication_Programming_Interface"],[99,0,2,"tinyobj_parse_mtl_file","tinyobj_parse_mtl_file"],[100,0,2,"tinyobj_material_free","tinyobj_material_free"],[3,0,3,"Object handling (.obj)","Object_handling"],[102,0,1,"CommandType","CommandType"],[103,0,0,,"COMMAND_EMPTY"],[104,0,0,,"COMMAND_V"],[105,0,0,,"COMMAND_VN"],[106,0,0,,"COMMAND_VT"],[107,0,0,,"COMMAND_VP"],[108,0,0,,"COMMAND_F"],[109,0,0,,"COMMAND_P"],[110,0,0,,"COMMAND_L"],[111,0,0,,"COMMAND_G"],[112,0,0,,"COMMAND_O"],[113,0,0,,"COMMAND_S"],[114,0,0,,"COMMAND_USEMTL"],[115,0,0,,"COMMAND_MTLLIB"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_table_t-SummaryToolTips.js b/doc/classes/C/tinyobj_material_table_t-SummaryToolTips.js
new file mode 100644
index 0000000..16cb10e
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_table_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_material_table_t",{75:"",79:"",126:"",80:"",128:"",129:"",130:"",131:"",132:"",58:"",81:"",135:"",87:"",88:"",89:"",90:"",91:"",92:"",94:"",95:"",96:"",97:"",136:"",99:"",100:"",102:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_table_t-ToolTips.js b/doc/classes/C/tinyobj_material_table_t-ToolTips.js
new file mode 100644
index 0000000..ae10525
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_table_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({5:"",8:"",10:"",12:"",14:"",74:"",75:"",126:"",129:"",130:"",131:"",136:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_material_table_t.html b/doc/classes/C/tinyobj_material_table_t.html
new file mode 100644
index 0000000..b956034
--- /dev/null
+++ b/doc/classes/C/tinyobj_material_table_t.html
@@ -0,0 +1,182 @@
+
+
+tinyobj_material_table_t
+
+
+
+
+
+
+
+
+
tinyobj_material_table_t
+
Hash table used in tinyobj's own hash table implementation
Fields
hashes Hashes used
entries Linked list of entries in this table
capacity Total capacity of this table
n Count of elements
+
+
+
+
TINYOBJ hashtable implementation
+
+
+
+
+
hash_djb2
+
+
Converts given string to hash
+
+
+
+
create_hash_table
+
+
Creates a new hash table in 'hash_table'
Parameters
start_capacitysize_t
Starting capacity, if equal to zero is <TINYOBJ_HASH_TABLE_DEFAULT_SIZE>
hash_table [in/out] Table to be initialized
+
+
+
+
destroy_hash_table
+
+
Finalizes given hash_table
+
+
+
+
hash_table_insert_value
+
+
Subroutine of hash_table_insert Inserts given value in hash, uses quadratic probing
Parameters
hashunsigned long
Hash to be used
valuelong
Value of this entity
hash_table Table to be used
Returns
<TINYOBJ_HASH_TABLE_ERROR> upon failure or <TINYOBJ_HASH_TABLE_SUCCESS> after succeding
+
+
+
+
hash_table_insert
+
+
Inserts given value in hash, calls hash_table_insert_value
Parameters
hashunsigned long
Hash to be used
valuelong
Value of this entity
hash_table Table to be used
Returns
<TINYOBJ_HASH_TABLE_ERROR> upon failure or <TINYOBJ_HASH_TABLE_SUCCESS> after succeding
+
+
+
+
hash_table_find
+
+
Tries to find given hash in the table
Parameters
hashunsigned long
Hash to be found
hash_table Table to be used
Returns
Entry upon succeding or NULL if failure to find
+
+
+
+
hash_table_maybe_grow
+
+
Grows given hash_table to new_n if new_n is less or equal to capacity
+
+
+
+
hash_table_exists
+
+
Tries to find 'name' in hash_table, returns true if successful
See
+
+
+
+
hash_table_set
+
+
Inserts value into hash_table, in name; creates new entry if it doesn't exist
See
+
+
+
+
hash_table_get
+
+
Returns value of entity with name, -1 when failing
+
+
+
+
UTHash interface to tinyobj
+
+
+
+
tinyobj_material_table_t
+
+
+
+
+
UTHash interface to tinyobj
+
+
+
+
tinyobj_reserved
+
Reserved key used by the hashtable implementation so the table isn't deleted
+
+
+
+
tinyobj_hash_find
+
+
Tries to find object with 'name' as key
Parameters
table Table to be used
nameconst char *
Key
entryOUTtinyobj_material_table_tEntry**
[out] Result
Returns
Sets entry to a valid result if entry was found, otherwise sets it to NULL
+
+
+
+
tinyobj_hash_add
+
+
Adds a material to the given material table If the material was already added its value is replaced
Parameters
table Table to be used
nameconst char *
Key
lengthsize_t
Name length
valuelong
Value of the new entity
Returns
Returns true if successful, false otherwise
+
+
+
+
tinyobj_hash_free
+
+
Frees all entries of a given tinyobj_material_table_t
Parameters
table Table to be freed
bfree_nameunsigned char
Should the key (name) be freed
Usually the name pointer is owned by the materials list by this point
See tinyobj_parse_and_index_mtl_file , freeing could lead to a segfault
+
+
+
+
tinyobj_hash_init
+
+
Initialises given table, and adds a reserved key
+
+
+
+
Material handling (.mtl)
+
+
+
+
+
+
tinyobj_mtl_parse_map
+
+
Parses a texture name from token ('map_')
Supported map types: map_Ka - ambient texture map_Kd - diffuse texture map_Ks - specular texture map_Ns - specular highlight texture map_bump - bump texture map_d - alpha texture Parameters
material Material to be filled
tokenconst char **
Buffer
line_endconst char *
Buffer end (halting point)
Returns
+
+
+
+
tinyobj_mtl_parse_optical
+
+
Parses an optical coefficient from token ('N')
Supported coefficient types: Ni - Index Of Refraction (ior) 'optical density' Ns - Specular exponent 'shininess' Parameters
material Material to be filled
tokenconst char **
Buffer
Returns
+
+
+
+
tinyobj_mtl_parse_color
+
+
Parses a color coefficient from token ('K')
Supported coefficient types: Ka - Ambient Kd - Diffuse Ks - Specular Kt - Transmittance Ke - Emissive Parameters
material Material to be filled
tokenconst char **
Buffer
Returns
+
+
+
+
tinyobj_parse_and_index_mtl_file
+
+
Parses and indexes a given material file
Parameters
materials_out [out] Completed list of materials
num_materials_outunsigned int *
[out] Length of materials_out
filenameconst char *
File to be parsed
material_table Table to be filled (can be NULL)
Returns
+
+
+
+
Aplication Programming Interface (.mtl)
+
+
+
+
tinyobj_parse_mtl_file
+
+
+
+
+
+
tinyobj_material_free
+
+
Frees material data
Parameters
materials Material list to be freed
num_materialsunsigned int
Length of materials
+
+
+
+
Object handling (.obj)
+
+
+
+
CommandType
+
Command identifiers used when parsing
<tinyobj_parse_obj_line>
COMMAND_EMPTYNo command
COMMAND_V'v' Vertex <tinyobj_obj_parse_vertex>
COMMAND_VN'vn' Vertex normal <tinyobj_obj_parse_vertex>
COMMAND_VT'vt' Texture coordinate <tinyobj_obj_parse_vertex>
COMMAND_VP'vp' Parameter space vertex <tinyobj_obj_parse_vertex>
COMMAND_F'f' Face <tinyobj_obj_parse_face>
COMMAND_P'p' Point <tinyobj_obj_parse_point>
COMMAND_L'l' Line <tinyobj_obj_parse_line>
COMMAND_G'g' Group name
COMMAND_O'o' Object name
COMMAND_S's' Smoothing group
COMMAND_USEMTL'usemtl' Use material
COMMAND_MTLLIB'mtllib' Load material
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_point_t-Summary.js b/doc/classes/C/tinyobj_point_t-Summary.js
new file mode 100644
index 0000000..bb797fd
--- /dev/null
+++ b/doc/classes/C/tinyobj_point_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_point_t","tinyobj_point_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_point_t",[["C/C++","C"]],[["Structs","Struct"]],[[32,0,0,"tinyobj_point_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_point_t-SummaryToolTips.js b/doc/classes/C/tinyobj_point_t-SummaryToolTips.js
new file mode 100644
index 0000000..0467f6e
--- /dev/null
+++ b/doc/classes/C/tinyobj_point_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_point_t",{32:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_point_t-ToolTips.js b/doc/classes/C/tinyobj_point_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_point_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_point_t.html b/doc/classes/C/tinyobj_point_t.html
new file mode 100644
index 0000000..cfe6967
--- /dev/null
+++ b/doc/classes/C/tinyobj_point_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_point_t
+
+
+
+
+
+
+
+
+
tinyobj_point_t
+
Point (p) Specifies a point element and its vertex
p v1 v2 v3 Fields
v_idx List of vertices
count Point count
length List length
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_shape_t-Summary.js b/doc/classes/C/tinyobj_shape_t-Summary.js
new file mode 100644
index 0000000..41a8971
--- /dev/null
+++ b/doc/classes/C/tinyobj_shape_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_shape_t","tinyobj_shape_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_shape_t",[["C/C++","C"]],[["Structs","Struct"]],[[38,0,0,"tinyobj_shape_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_shape_t-SummaryToolTips.js b/doc/classes/C/tinyobj_shape_t-SummaryToolTips.js
new file mode 100644
index 0000000..76c63ef
--- /dev/null
+++ b/doc/classes/C/tinyobj_shape_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_shape_t",{38:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_shape_t-ToolTips.js b/doc/classes/C/tinyobj_shape_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_shape_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_shape_t.html b/doc/classes/C/tinyobj_shape_t.html
new file mode 100644
index 0000000..ff59a98
--- /dev/null
+++ b/doc/classes/C/tinyobj_shape_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_shape_t
+
+
+
+
+
+
+
+
+
tinyobj_shape_t
+
Shapes contained in a given attribute
Fields
name Group name or object name
face_offset Face offset of this shape (starting point)
length Length of this shape (ending point)
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_index_t-Summary.js b/doc/classes/C/tinyobj_vertex_index_t-Summary.js
new file mode 100644
index 0000000..3c80766
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_index_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_vertex_index_t","tinyobj_vertex_index_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_vertex_index_t",[["C/C++","C"]],[["Structs","Struct"]],[[31,0,0,"tinyobj_vertex_index_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_index_t-SummaryToolTips.js b/doc/classes/C/tinyobj_vertex_index_t-SummaryToolTips.js
new file mode 100644
index 0000000..1400ec5
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_index_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_vertex_index_t",{31:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_index_t-ToolTips.js b/doc/classes/C/tinyobj_vertex_index_t-ToolTips.js
new file mode 100644
index 0000000..232e96d
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_index_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({35:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_index_t.html b/doc/classes/C/tinyobj_vertex_index_t.html
new file mode 100644
index 0000000..c718645
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_index_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_vertex_index_t
+
+
+
+
+
+
+
+
+
tinyobj_vertex_index_t
+
Indices for each vertex type, used for triplets in faces See: tinyobj_face_t
Fields
v_idx Geometric vertex index
vt_idx Texture vertex index
vn_idx Normal vertex index
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_normal_t-Summary.js b/doc/classes/C/tinyobj_vertex_normal_t-Summary.js
new file mode 100644
index 0000000..00c8704
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_normal_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_vertex_normal_t","tinyobj_vertex_normal_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_vertex_normal_t",[["C/C++","C"]],[["Structs","Struct"]],[[28,0,0,"tinyobj_vertex_normal_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_normal_t-SummaryToolTips.js b/doc/classes/C/tinyobj_vertex_normal_t-SummaryToolTips.js
new file mode 100644
index 0000000..963971a
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_normal_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_vertex_normal_t",{28:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_normal_t-ToolTips.js b/doc/classes/C/tinyobj_vertex_normal_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_normal_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_normal_t.html b/doc/classes/C/tinyobj_vertex_normal_t.html
new file mode 100644
index 0000000..01477f4
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_normal_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_vertex_normal_t
+
+
+
+
+
+
+
+
+
tinyobj_vertex_normal_t
+
Vertex normal (vn) Specifies a normal vector with components i, j, and k
vn i j k Fields
i i coordinate
j j coordinate
k k coordinate
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_param_t-Summary.js b/doc/classes/C/tinyobj_vertex_param_t-Summary.js
new file mode 100644
index 0000000..112e3c7
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_param_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_vertex_param_t","tinyobj_vertex_param_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_vertex_param_t",[["C/C++","C"]],[["Groups","Group"],["Structs","Struct"]],[[30,0,1,"tinyobj_vertex_param_t"],[145,0,0,"Element data","Element_data"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_param_t-SummaryToolTips.js b/doc/classes/C/tinyobj_vertex_param_t-SummaryToolTips.js
new file mode 100644
index 0000000..91a1df3
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_param_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_vertex_param_t",{30:"",145:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_param_t-ToolTips.js b/doc/classes/C/tinyobj_vertex_param_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_param_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_param_t.html b/doc/classes/C/tinyobj_vertex_param_t.html
new file mode 100644
index 0000000..d40ddae
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_param_t.html
@@ -0,0 +1,21 @@
+
+
+tinyobj_vertex_param_t
+
+
+
+
+
+
+
+
+
tinyobj_vertex_param_t
+
Parameter space vertex (vp) Specifies a point in the parameter space of a curve or surface.
vp u v w Fields
u (1D) Space control point
v (2D) Space control point
weight (Rational trimming curves) Weight (default: 0)
+
+
+
+
Element data
+
Polygonal and free-form geometry
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_t-Summary.js b/doc/classes/C/tinyobj_vertex_t-Summary.js
new file mode 100644
index 0000000..d5fb06c
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_vertex_t","tinyobj_vertex_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_vertex_t",[["C/C++","C"]],[["Structs","Struct"]],[[27,0,0,"tinyobj_vertex_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_t-SummaryToolTips.js b/doc/classes/C/tinyobj_vertex_t-SummaryToolTips.js
new file mode 100644
index 0000000..51442a4
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_vertex_t",{27:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_t-ToolTips.js b/doc/classes/C/tinyobj_vertex_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_t.html b/doc/classes/C/tinyobj_vertex_t.html
new file mode 100644
index 0000000..42c4bd2
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_vertex_t
+
+
+
+
+
+
+
+
+
tinyobj_vertex_t
+
Geometric vertex (v) Specifies a geometric vertex and its x y z coordinates. Rational curves and surfaces require a fourth homogeneous coordinate, also called the weight.
v x y z w Fields
x x coordinate
y y coordinate
z z coordinate
weight (Rational curves/surfaces) Weight (default: 1.0f)
+
+
+
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_texture_t-Summary.js b/doc/classes/C/tinyobj_vertex_texture_t-Summary.js
new file mode 100644
index 0000000..14527ab
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_texture_t-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("CClass:tinyobj_vertex_texture_t","tinyobj_vertex_texture_t");NDSummary.OnSummaryLoaded("CClass:tinyobj_vertex_texture_t",[["C/C++","C"]],[["Structs","Struct"]],[[29,0,0,"tinyobj_vertex_texture_t"]]);
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_texture_t-SummaryToolTips.js b/doc/classes/C/tinyobj_vertex_texture_t-SummaryToolTips.js
new file mode 100644
index 0000000..aab8acc
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_texture_t-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("CClass:tinyobj_vertex_texture_t",{29:""});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_texture_t-ToolTips.js b/doc/classes/C/tinyobj_vertex_texture_t-ToolTips.js
new file mode 100644
index 0000000..59992a1
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_texture_t-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({});
\ No newline at end of file
diff --git a/doc/classes/C/tinyobj_vertex_texture_t.html b/doc/classes/C/tinyobj_vertex_texture_t.html
new file mode 100644
index 0000000..18eda7d
--- /dev/null
+++ b/doc/classes/C/tinyobj_vertex_texture_t.html
@@ -0,0 +1,16 @@
+
+
+tinyobj_vertex_texture_t
+
+
+
+
+
+
+
+
+
tinyobj_vertex_texture_t
+
Texture vertex (vt) Specifies a texture vertex and its coordinates
vt u v w Fields
u Horizontal direction
v (2D and 3D) Vertical direction (Default: 0)
w (3D) Depth (Default: 0)
+
+
+
\ No newline at end of file
diff --git a/doc/files/tinyobj_loader_c-h-Summary.js b/doc/files/tinyobj_loader_c-h-Summary.js
new file mode 100644
index 0000000..b515583
--- /dev/null
+++ b/doc/files/tinyobj_loader_c-h-Summary.js
@@ -0,0 +1 @@
+NDFramePage.OnPageTitleLoaded("File:tinyobj_loader_c.h","tinyobj_loader_c.h");NDSummary.OnSummaryLoaded("File:tinyobj_loader_c.h",[["C/C++","C"]],[["Constants","Constant"],["Enums","Enumeration"],["Functions","Function"],["Groups","Group"],["Information","Information"],["Structs","Struct"]],[[1,0,4,"License","License"],[116,0,0,"TINYOBJ_LOADER_C_IMPLEMENTATION","TINYOBJ_LOADER_C_IMPLEMENTATION"],[2,0,0,"TINYOBJ_USE_UTHASH","TINYOBJ_USE_UTHASH"],[64,0,0,"TINYOBJ_ENABLE_OLDER_ATTRIBUTE","TINYOBJ_ENABLE_OLDER_ATTRIBUTE"],[4,0,0,"TINYOBJ_FLAG_TRIANGULATE","TINYOBJ_FLAG_TRIANGULATE"],[77,0,0,"TINYOBJ_INVALID_INDEX","TINYOBJ_INVALID_INDEX"],[6,0,0,"Return codes","Return_codes"],[7,0,0,,"TINYOBJ_NO_COMMAND"],[8,0,0,,"TINYOBJ_SUCCESS"],[9,0,0,,"TINYOBJ_ERROR_NOT_SET"],[10,0,0,,"TINYOBJ_ERROR_MEMORY"],[11,0,0,,"TINYOBJ_ERROR_EMPTY"],[12,0,0,,"TINYOBJ_ERROR_FILE_OPERATION"],[13,0,0,,"TINYOBJ_ERROR_INVALID_PARAMETER"],[14,0,0,,"TINYOBJ_ERROR_UNKNOWN_PARAMETER"],[15,0,0,,"TINYOBJ_ERROR_MALFORMED_PARAMETER"],[16,0,0,"Dynamic array basic parameters","Dynamic_array_basic_parameters"],[17,0,0,,"TINYOBJ_POINT_INITIAL_COUNT"],[18,0,0,,"TINYOBJ_POINT_GROWTH_FACTOR"],[19,0,0,,"TINYOBJ_COUPLE_INITIAL_COUNT"],[20,0,0,,"TINYOBJ_COUPLE_GROWTH_FACTOR"],[21,0,0,,"TINYOBJ_TRIPLET_INITIAL_COUNT"],[22,0,0,,"TINYOBJ_TRIPLET_GROWTH_FACTOR"],[23,0,0,,"TINYOBJ_MATERIAL_INITIAL_COUNT"],[24,0,0,,"TINYOBJ_MATERIAL_GROWTH_FACTOR"],[25,0,3,"Vertex data","Vertex_data"],[27,0,5,"tinyobj_vertex_t","tinyobj_vertex_t"],[28,0,5,"tinyobj_vertex_normal_t","tinyobj_vertex_normal_t"],[29,0,5,"tinyobj_vertex_texture_t","tinyobj_vertex_texture_t"],[30,0,5,"tinyobj_vertex_param_t","tinyobj_vertex_param_t"],[145,0,3,"Element data","tinyobj_vertex_param_t.Element_data"],[31,0,5,"tinyobj_vertex_index_t","tinyobj_vertex_index_t"],[32,0,5,"tinyobj_point_t","tinyobj_point_t"],[33,0,5,"tinyobj_line_t","tinyobj_line_t"],[26,0,5,"tinyobj_line_t. s_line_vertex_index","tinyobj_line_t.s_line_vertex_index"],[35,0,5,"tinyobj_face_t","tinyobj_face_t"],[36,0,5,"tinyobj_coefficient_t","tinyobj_coefficient_t"],[5,0,5,"tinyobj_material_t","tinyobj_material_t"],[38,0,5,"tinyobj_shape_t","tinyobj_shape_t"],[37,0,5,"tinyobj_attrib_t","tinyobj_attrib_t"],[40,0,5,"COMPATtinyobj_attrib_t","COMPATtinyobj_attrib_t"],[41,0,3,"String handling","COMPATtinyobj_attrib_t.String_handling"],[42,0,2,"String handling macros","COMPATtinyobj_attrib_t.String_handling_macros"],[43,0,2,,"COMPATtinyobj_attrib_t.IS_SPACE"],[44,0,2,,"COMPATtinyobj_attrib_t.IS_DIGIT"],[45,0,2,,"COMPATtinyobj_attrib_t.IS_NEW_LINE"],[120,0,2,"is_line_ending","COMPATtinyobj_attrib_t.is_line_ending"],[47,0,2,"skip_space","COMPATtinyobj_attrib_t.skip_space"],[48,0,2,"skip_space_and_cr","COMPATtinyobj_attrib_t.skip_space_and_cr"],[49,0,2,"length_until_space","COMPATtinyobj_attrib_t.length_until_space"],[50,0,2,"until_space","COMPATtinyobj_attrib_t.until_space"],[51,0,2,"until_space_cr_slash","COMPATtinyobj_attrib_t.until_space_cr_slash"],[52,0,2,"length_until_newline_comment_space","COMPATtinyobj_attrib_t.length_until_newline_comment_space"],[53,0,2,"length_until_line_feed","COMPATtinyobj_attrib_t.length_until_line_feed"],[123,0,2,"strdup_ml","COMPATtinyobj_attrib_t.strdup_ml"],[55,0,2,"strndup","COMPATtinyobj_attrib_t.strndup"],[124,0,2,"dynamic_fgets","COMPATtinyobj_attrib_t.dynamic_fgets"],[57,0,3,"Triplet handling","COMPATtinyobj_attrib_t.Triplet_handling"],[34,0,2,"fixIndex","COMPATtinyobj_attrib_t.fixIndex"],[59,0,2,"parseRawTriple","COMPATtinyobj_attrib_t.parseRawTriple"],[60,0,3,"Integer/Double/Float handling","COMPATtinyobj_attrib_t.Integer/Double/Float_handling"],[61,0,2,"parseInt","COMPATtinyobj_attrib_t.parseInt"],[62,0,2,"tryParseDouble_assemble","COMPATtinyobj_attrib_t.tryParseDouble_assemble"],[63,0,2,"tryParseDouble_integer","COMPATtinyobj_attrib_t.tryParseDouble_integer"],[125,0,2,"tryParseDouble","COMPATtinyobj_attrib_t.tryParseDouble"],[65,0,2,"try_parse_float","COMPATtinyobj_attrib_t.try_parse_float"],[66,0,2,"parseFloat","COMPATtinyobj_attrib_t.parseFloat"],[67,0,2,"parseFloat2","COMPATtinyobj_attrib_t.parseFloat2"],[68,0,2,"parseFloat3","COMPATtinyobj_attrib_t.parseFloat3"],[78,0,3,"TINYOBJ hashtable implementation","COMPATtinyobj_attrib_t.TINYOBJ_hashtable_implementation"],[70,0,0,"Hash table return codes","COMPATtinyobj_attrib_t.Hash_table_return_codes"],[71,0,0,,"COMPATtinyobj_attrib_t.TINYOBJ_HASH_TABLE_SUCCESS"],[72,0,0,,"COMPATtinyobj_attrib_t.TINYOBJ_HASH_TABLE_ERROR"],[73,0,0,"TINYOBJ_HASH_TABLE_DEFAULT_SIZE","COMPATtinyobj_attrib_t.TINYOBJ_HASH_TABLE_DEFAULT_SIZE"],[74,0,5,"hash_table_entry_t","hash_table_entry_t"],[75,0,5,"tinyobj_material_table_t","tinyobj_material_table_t"],[79,0,3,"TINYOBJ hashtable implementation","tinyobj_material_table_t.TINYOBJ_hashtable_implementation"],[126,0,2,"hash_djb2","tinyobj_material_table_t.hash_djb2"],[80,0,2,"create_hash_table","tinyobj_material_table_t.create_hash_table"],[128,0,2,"destroy_hash_table","tinyobj_material_table_t.destroy_hash_table"],[129,0,2,"hash_table_insert_value","tinyobj_material_table_t.hash_table_insert_value"],[130,0,2,"hash_table_insert","tinyobj_material_table_t.hash_table_insert"],[131,0,2,"hash_table_find","tinyobj_material_table_t.hash_table_find"],[132,0,2,"hash_table_maybe_grow","tinyobj_material_table_t.hash_table_maybe_grow"],[58,0,2,"hash_table_exists","tinyobj_material_table_t.hash_table_exists"],[81,0,2,"hash_table_set","tinyobj_material_table_t.hash_table_set"],[135,0,2,"hash_table_get","tinyobj_material_table_t.hash_table_get"],[86,0,3,"UTHash interface to tinyobj","tinyobj_material_table_t.UTHash_interface_to_tinyobj"],[87,0,5,"tinyobj_material_table_t","tinyobj_material_table_t(2)"],[143,0,3,"UTHash interface to tinyobj","tinyobj_material_table_t.UTHash_interface_to_tinyobj(2)"],[88,0,0,"tinyobj_reserved","tinyobj_material_table_t.tinyobj_reserved"],[89,0,2,"tinyobj_hash_find","tinyobj_material_table_t.tinyobj_hash_find"],[90,0,2,"tinyobj_hash_add","tinyobj_material_table_t.tinyobj_hash_add"],[91,0,2,"tinyobj_hash_free","tinyobj_material_table_t.tinyobj_hash_free"],[92,0,2,"tinyobj_hash_init","tinyobj_material_table_t.tinyobj_hash_init"],[82,0,3,"Material handling (.mtl)","tinyobj_material_table_t.Material_handling"],[94,0,2,"initMaterial","tinyobj_material_table_t.initMaterial"],[95,0,2,"tinyobj_mtl_parse_map","tinyobj_material_table_t.tinyobj_mtl_parse_map"],[96,0,2,"tinyobj_mtl_parse_optical","tinyobj_material_table_t.tinyobj_mtl_parse_optical"],[97,0,2,"tinyobj_mtl_parse_color","tinyobj_material_table_t.tinyobj_mtl_parse_color"],[136,0,2,"tinyobj_parse_and_index_mtl_file","tinyobj_material_table_t.tinyobj_parse_and_index_mtl_file"],[46,0,3,"Aplication Programming Interface (.mtl)","tinyobj_material_table_t.Aplication_Programming_Interface"],[99,0,2,"tinyobj_parse_mtl_file","tinyobj_material_table_t.tinyobj_parse_mtl_file"],[100,0,2,"tinyobj_material_free","tinyobj_material_table_t.tinyobj_material_free"],[3,0,3,"Object handling (.obj)","tinyobj_material_table_t.Object_handling"],[102,0,1,"CommandType","tinyobj_material_table_t.CommandType"],[103,0,0,,"tinyobj_material_table_t.COMMAND_EMPTY"],[104,0,0,,"tinyobj_material_table_t.COMMAND_V"],[105,0,0,,"tinyobj_material_table_t.COMMAND_VN"],[106,0,0,,"tinyobj_material_table_t.COMMAND_VT"],[107,0,0,,"tinyobj_material_table_t.COMMAND_VP"],[108,0,0,,"tinyobj_material_table_t.COMMAND_F"],[109,0,0,,"tinyobj_material_table_t.COMMAND_P"],[110,0,0,,"tinyobj_material_table_t.COMMAND_L"],[111,0,0,,"tinyobj_material_table_t.COMMAND_G"],[112,0,0,,"tinyobj_material_table_t.COMMAND_O"],[113,0,0,,"tinyobj_material_table_t.COMMAND_S"],[114,0,0,,"tinyobj_material_table_t.COMMAND_USEMTL"],[115,0,0,,"tinyobj_material_table_t.COMMAND_MTLLIB"],[118,0,5,"Command","Command"],[85,0,5,"Command. u_information","Command.u_information"],[122,0,5,"Command. s_group_information","Command.s_group_information"],[119,0,5,"CommandInformation","CommandInformation"],[84,0,5,"CommandInformation. s_counter","CommandInformation.s_counter"],[69,0,3,"Object handling (.obj)","CommandInformation.s_counter.Object_handling"],[146,0,2,"tinyobj_obj_free_point","CommandInformation.s_counter.tinyobj_obj_free_point"],[147,0,2,"tinyobj_obj_parse_point","CommandInformation.s_counter.tinyobj_obj_parse_point"],[148,0,2,"tinyobj_triplet_list_grow","CommandInformation.s_counter.tinyobj_triplet_list_grow"],[149,0,2,"tinyobj_triplet_list_new","CommandInformation.s_counter.tinyobj_triplet_list_new"],[150,0,2,"tinyobj_obj_free_line","CommandInformation.s_counter.tinyobj_obj_free_line"],[151,0,2,"tinyuobj_obj_parse_line","CommandInformation.s_counter.tinyuobj_obj_parse_line"],[152,0,2,"tinyobj_obj_free_face","CommandInformation.s_counter.tinyobj_obj_free_face"],[83,0,2,"tinyobj_obj_parse_face","CommandInformation.s_counter.tinyobj_obj_parse_face"],[154,0,2,"tinyobj_obj_parse_vertex","CommandInformation.s_counter.tinyobj_obj_parse_vertex"],[138,0,2,"tinyobj_parse_obj_line","CommandInformation.s_counter.tinyobj_parse_obj_line"],[139,0,2,"tinyobj_parse_obj_line_traverse","CommandInformation.s_counter.tinyobj_parse_obj_line_traverse"],[157,0,2,"tinyobj_command_info_free","CommandInformation.s_counter.tinyobj_command_info_free"],[158,0,2,"tinyobj_shape_construct","CommandInformation.s_counter.tinyobj_shape_construct"],[159,0,2,"tinyobj_shape_free","CommandInformation.s_counter.tinyobj_shape_free"],[160,0,2,"tinyobj_attrib_construct","CommandInformation.s_counter.tinyobj_attrib_construct"],[54,0,3,"Aplication Programming Interface (.obj)","CommandInformation.s_counter.Aplication_Programming_Interface"],[161,0,2,"tinyobj_attrib_free","CommandInformation.s_counter.tinyobj_attrib_free"],[162,0,2,"tinyobj_attrib_init","CommandInformation.s_counter.tinyobj_attrib_init"],[140,0,2,"tinyobj_parse_obj","CommandInformation.s_counter.tinyobj_parse_obj"],[56,0,3,"Compatibility with older versions (deprecated)","CommandInformation.s_counter.Compatibility_with_older_versions"],[164,0,2,"tinyobj_attrib_free_compat","CommandInformation.s_counter.tinyobj_attrib_free_compat"],[39,0,2,"tinyobj_new2old","CommandInformation.s_counter.tinyobj_new2old"]]);
\ No newline at end of file
diff --git a/doc/files/tinyobj_loader_c-h-SummaryToolTips.js b/doc/files/tinyobj_loader_c-h-SummaryToolTips.js
new file mode 100644
index 0000000..18414bf
--- /dev/null
+++ b/doc/files/tinyobj_loader_c-h-SummaryToolTips.js
@@ -0,0 +1 @@
+NDSummary.OnToolTipsLoaded("File:tinyobj_loader_c.h",{1:"",116:"",2:"",64:"",4:"",77:"",16:"",27:"",28:"",29:"",30:"",145:"",31:"",32:"",33:"",26:"",35:"",36:"",5:"",38:"",37:"",40:"",120:"",47:"",48:"",49:"",50:"",51:"",52:"",53:"",123:"",55:"",124:"",34:"",59:"",61:"",62:"",63:"",125:"",65:"",66:"",67:"",68:"",78:"",73:"",74:"",75:"",79:"",126:"",80:"",128:"",129:"",130:"",131:"",132:"",58:"",81:"",135:"",87:"",88:"",89:"",90:"",91:"",92:"",94:"",95:"",96:"",97:"",136:"",99:"",100:"",102:"",118:"",85:"",122:"",119:"",84:"",146:"",147:"",148:"",149:"",150:"",151:"",152:"",83:"",154:"",138:"",139:"",157:"",158:"",159:"",160:"",161:"",162:"",140:"",164:"",39:""});
\ No newline at end of file
diff --git a/doc/files/tinyobj_loader_c-h-ToolTips.js b/doc/files/tinyobj_loader_c-h-ToolTips.js
new file mode 100644
index 0000000..ef438d9
--- /dev/null
+++ b/doc/files/tinyobj_loader_c-h-ToolTips.js
@@ -0,0 +1 @@
+NDContentPage.OnToolTipsLoaded({4:"",5:"",8:"",10:"",11:"",12:"",13:"",14:"",15:"",31:"",32:"",33:"",35:"",37:"",38:"",40:"",59:"",66:"",74:"",75:"",77:"",118:"",119:"",125:"",126:"",129:"",130:"",131:"",136:""});
\ No newline at end of file
diff --git a/doc/files/tinyobj_loader_c-h.html b/doc/files/tinyobj_loader_c-h.html
new file mode 100644
index 0000000..090d70c
--- /dev/null
+++ b/doc/files/tinyobj_loader_c-h.html
@@ -0,0 +1,620 @@
+
+
+tinyobj_loader_c.h
+
+
+
+
+
+
+
+
+
+
+
TINYOBJ_LOADER_C_IMPLEMENTATION
+
Constant used to mark the .c file that holds the implementation of tinyobj
Only one file should have this definition!
+
+
+
+
TINYOBJ_USE_UTHASH
+
Use UThash hashtable implementation instead of tinyobj's implementation
+
+
+
+
TINYOBJ_ENABLE_OLDER_ATTRIBUTE
+
Enable use of older attribute type for objects (deprecated)
See
+
+
+
+
TINYOBJ_FLAG_TRIANGULATE
+
Should the faces be triangulated upon loading When triangulating we parse the vertices as if they were part of a triangle fan, so every three indices the next is always the first triplet of this face
See
<tinyobj_obj_parse_face>
+
+
+
+
TINYOBJ_INVALID_INDEX
+
Invalid vertex index used when referencing vertices in couples/tuples
See
+
+
+
+
Return codes
+
TINYOBJ_NO_COMMANDNo command
TINYOBJ_SUCCESSSuccess
TINYOBJ_ERROR_NOT_SETDefault error
TINYOBJ_ERROR_MEMORYMemory failure
TINYOBJ_ERROR_EMPTYEmpty file
TINYOBJ_ERROR_FILE_OPERATIONFailed file operation
TINYOBJ_ERROR_INVALID_PARAMETERInvalid parameter (function)
TINYOBJ_ERROR_UNKNOWN_PARAMETERUnknown parameter for object/material
TINYOBJ_ERROR_MALFORMED_PARAMETERMalformed parameter for object/material
+
+
+
+
Dynamic array basic parameters
+
Initial array lengths and growth factors
TINYOBJ_POINT_INITIAL_COUNTPoint array initial length
TINYOBJ_POINT_GROWTH_FACTORPoint array growth factor
TINYOBJ_COUPLE_INITIAL_COUNTCouple (line) array initial length
TINYOBJ_COUPLE_GROWTH_FACTORCouple (line) array growth factor
TINYOBJ_TRIPLET_INITIAL_COUNTTriplet (face) array initial length
TINYOBJ_TRIPLET_GROWTH_FACTORTriplet (face) array growth factor
TINYOBJ_MATERIAL_INITIAL_COUNTMaterial array initial length
TINYOBJ_MATERIAL_GROWTH_FACTORMaterial array growth factor
+
+
+
+
+
+
tinyobj_vertex_t
+
Geometric vertex (v) Specifies a geometric vertex and its x y z coordinates. Rational curves and surfaces require a fourth homogeneous coordinate, also called the weight.
v x y z w Fields
x x coordinate
y y coordinate
z z coordinate
weight (Rational curves/surfaces) Weight (default: 1.0f)
+
+
+
+
tinyobj_vertex_normal_t
+
Vertex normal (vn) Specifies a normal vector with components i, j, and k
vn i j k Fields
i i coordinate
j j coordinate
k k coordinate
+
+
+
+
tinyobj_vertex_texture_t
+
Texture vertex (vt) Specifies a texture vertex and its coordinates
vt u v w Fields
u Horizontal direction
v (2D and 3D) Vertical direction (Default: 0)
w (3D) Depth (Default: 0)
+
+
+
+
tinyobj_vertex_param_t
+
Parameter space vertex (vp) Specifies a point in the parameter space of a curve or surface.
vp u v w Fields
u (1D) Space control point
v (2D) Space control point
weight (Rational trimming curves) Weight (default: 0)
+
+
+
+
Element data
+
Polygonal and free-form geometry
+
+
+
+
tinyobj_vertex_index_t
+
Indices for each vertex type, used for triplets in faces See: tinyobj_face_t
Fields
v_idx Geometric vertex index
vt_idx Texture vertex index
vn_idx Normal vertex index
+
+
+
+
tinyobj_point_t
+
Point (p) Specifies a point element and its vertex
p v1 v2 v3 Fields
v_idx List of vertices
count Point count
length List length
+
+
+
+
tinyobj_line_t
+
Line (l) Specifies a line and its vertex reference numbers
l v1/vt1 v2/vt2 v3/vt3 Fields
couple_list List of couples contained in this line
count Count of couples
length Total length of couple list
+
+
+
+
tinyobj_line_t. s_line_vertex_index
+
List of couples
Fields
v_idx Geometric vertex
vt_idx Texture vertex (optional)
+
+
+
+
tinyobj_face_t
+
Face (f) Specifies a face element and its vertex reference number
f v1/vt1/vn1 v2/vt2/vn2 fo (deprecated) Fields
triplet list A list of triplets contained in this face
v Geometric vertex (minimum of 3)
vt Texture vertex (optional)
vn Vertex normal (optional)
When an index is empty it is equal to TINYOBJ_INVALID_INDEX
count Count of triplets
length Length of triplet list
triangle_count Number of triangles in this face
material_id Material to be applied to this face (defaults to -1)
smoothing_id Smoothing group to be applied to this face (defaults to 0)
+
+
+
+
tinyobj_coefficient_t
+
RGB Color coefficient information used in materials tinyobj_material_t
+
+
+
+
tinyobj_material_t
+
Material attribute .mtl format
Fields
name Material name
ambient Ambient color coefficient (reflectivity)
diffuse Diffuse color coefficient (reflectivity)
specular Specular color coefficient (reflectivity)
transmittance Transmittance color coefficient (reflectivity)
emission Emission color coefficient (reflectivity)
shininess Specular exponent
ior Index Of Refraction (ior) 'optical density'
dissolve Non-transparency to be alpha 'dissolve' (0.0f transparent-1.0f opaque)
illum Illumination model (0-10)
ambient_texname map_Ka
diffuse_texname map_Kd
specular_texname map_Ks
specular_highlight_texname map_Ns
bump_texname map_bump, bump
displacement_texname disp
alpha_texname map_d
+
+
+
+
tinyobj_shape_t
+
Shapes contained in a given attribute
Fields
name Group name or object name
face_offset Face offset of this shape (starting point)
length Length of this shape (ending point)
+
+
+
+
tinyobj_attrib_t
+
Object attributes .obj format
Fields
v Geometric vertices
v_count Geometric vertex count
vn Vertex normals
vn_count Vertex normal count
vt Texture vertices
vt_count Texture vertex count
vp Parameter space vertices
vp_count Parameter space vertex count
f Object faces
f_count Face count
triangle_count_total Total count of triangles
l Lines
l_count Line count
p Points
+
+
+
+
COMPATtinyobj_attrib_t
+
Object attributes (deprecated)
Fields
vertices Array of geometric vertices (x0, y0, z0, x1, y1, z1, ...)
num_vertices Number of vertices in 'vertices' (the actual array length is num_vertices*3)
normals Array of vertex normals (i0, j0, k0, i1, j1, k1, ...)
num_normals Number of vertices in 'normals' (the actual array length is num_normals*3)
texcoords Array of texture vertices (u0, w0, u1, w1, ...)
num_texcoords Number of vertices in 'texcoords' (the actual array length is num_normals*2)
faces Array of faces (containing tinyobj_vertex_index_t information)
num_faces Length of 'faces'
face_num_verts Array of number of vertices in each face (3 for triangulated faces)
num_face_num_verts Total number of triangles in this object (length of face_num_verts)
material_ids Array with the id of the material for each face (length is num_faces)
+
+
+
+
+
+
String handling macros
+
IS_SPACEIs this character a spacing character ' ' or '\t'
IS_DIGITIs this character a digit
IS_NEW_LINEIs this character a new line character '\r','\n','\0'
+
+
+
+
is_line_ending
+
+
Is given line ending in this character?
Returns
True if line is ending
+
+
+
+
skip_space
+
+
Skips the next spacing characters in given token
+
+
+
+
skip_space_and_cr
+
+
Skips the next spacing characters and also carriage return in given token
+
+
+
+
length_until_space
+
+
Calculates and returns the length until next space character in this token
Parameters
tokenconst char *
Token to be used
nsize_t
Maximum number of characters to count
Returns
Length until next space character
+
+
+
+
until_space
+
+
Skips the next spacing characters
'\0',' ', '\t', '\r'
+
+
+
+
until_space_cr_slash
+
+
Skips the next characters in given token
'\0','/', ' ', '\t', '\r'
+
+
+
+
length_until_newline_comment_space
+
+
Returns the length until the next characters:
\n \r\n \0 ' ' \t # Parameters
tokenconst char *
Buffer
nsize_t
Maximum number of characters to count
Returns
Length until next character
+
+
+
+
length_until_line_feed
+
+
Returns the length until the next line feed
Assumes token[n-1] = '\0'
Parameters
tokenconst char *
Buffer
nsize_t
Maximum number of characters to count
+
+
+
+
strdup_ml
+
+
Duplicates 'max_length' characters of given string (strdup with max length specified)
Parameters
sconst char *
String to be duplicated
max_lengthsize_t
Maximum length to be duplicated
Returns
Duplicated string (should be freed by the caller )
+
+
+
+
strndup
+
+
strndup implementation for non-GNU compliant compilers
+
+
+
+
dynamic_fgets
+
+
fgets with a dynamic buffer
+
+
+
+
+
+
fixIndex
+
+
Converts given absolute index, from triplet to zero-base, supports relative indices
See
Parameters
idxint
Index to be converted
nsize_t
Position of this couple/tuple regarding to the vertex type of this index
Returns
+
+
+
+
parseRawTriple
+
+
Parses raw triplets of given tokens
v, v/vt/vn, v//vn, v/vt Valid triplets: <x> 1//1 <x> 1//1 2//2 3//3 4//4 <x> 1/1/1 2/2/2 3/3/3 4/4/4 The following are examples of illegal statements <x> 1/1/1 2/2/2 3//3 4//4 <x> 1/ 1/1 2/2/2 3/3/3 4/4/4 Parameters
tokenconst char **
Token to parse
Returns
Indices parsed from given token, if one of the indices is not present it's equal to TINYOBJ_INVALID_INDEX
+
+
+
+
Integer/Double/Float handling
+
+
+
+
parseInt
+
+
Returns equivalent integer of next non-space character, the token is advanced until next space
+
+
+
+
tryParseDouble_assemble
+
+
Last step of tryParseDouble
Assembles given information into double
Parameters
signchar
Sign of this double
mantissadouble
Mantissa
exp_signchar
Exponent sign
exponentint
Exponent
Returns
Assembled double
+
+
+
+
tryParseDouble_integer
+
+
Part of tryParseDouble
Parses integer part of a double (advances curr)
Parameters
currconst char **
Current position in a buffer
s_endconst char *
End of the buffer
signchar *
[out] Sign to be filled
integerint *
[out] Integer to be filled
Returns
True integer was successfuly parsed, false if nothing was read
+
+
+
+
tryParseDouble
+
+
Tries to parse a floating point number located at s.
Parses the following EBNF grammar: sign = "+" | "-" ; END = ? anything not in digit ? digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; integer = [sign] , digit , {digit} ; decimal = integer , ["." , integer] ; float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; Valid strings are for example: -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 The function is greedy and will parse until any of the following happens:
The following situations triggers a failure:
s >= s_end.
parse failure.
Parameters
sconst char *
String to be parsed
s_endconst char *
Location in the string where reading should halt, e.g. the end of
the string result [out] Parsed double
Returns If the parsing is a success, result is set to the parsed value and true is returned.
+
+
+
+
try_parse_float
+
+
Tries to parse next float, if there's none fails
Parameters
tokenconst char **
Buffer to be read
resultfloat *
[out] Result, not set if return is false
Returns
If the parsing is a success, result is set to the parsed value and true is returned.
+
+
+
+
parseFloat
+
+
Returns equivalent float of next non-space character, the token is advanced until next space
+
+
+
+
parseFloat2
+
+
Parses the two next floats in given token
See
parseFloat
+
+
+
+
parseFloat3
+
+
Parses the three next floats in given token
See
parseFloat
+
+
+
+
TINYOBJ hashtable implementation
+
+
+
+
+
Hash table return codes
+
TINYOBJ_HASH_TABLE_SUCCESSSuccess
TINYOBJ_HASH_TABLE_ERRORFailure
+
+
+
+
TINYOBJ_HASH_TABLE_DEFAULT_SIZE
+
Default starting size for tinyobj own hash table implementation tables
+
+
+
+
hash_table_entry_t
+
Entries used in tinyobj's own hash table implementation
Fields
hash Hash value
filled Is this entry filled
value Value
next Next entry
+
+
+
+
tinyobj_material_table_t
+
Hash table used in tinyobj's own hash table implementation
Fields
hashes Hashes used
entries Linked list of entries in this table
capacity Total capacity of this table
n Count of elements
+
+
+
+
TINYOBJ hashtable implementation
+
+
+
+
+
hash_djb2
+
+
Converts given string to hash
+
+
+
+
create_hash_table
+
+
Creates a new hash table in 'hash_table'
Parameters
start_capacitysize_t
Starting capacity, if equal to zero is <TINYOBJ_HASH_TABLE_DEFAULT_SIZE>
hash_table [in/out] Table to be initialized
+
+
+
+
destroy_hash_table
+
+
Finalizes given hash_table
+
+
+
+
hash_table_insert_value
+
+
Subroutine of hash_table_insert Inserts given value in hash, uses quadratic probing
Parameters
hashunsigned long
Hash to be used
valuelong
Value of this entity
hash_table Table to be used
Returns
<TINYOBJ_HASH_TABLE_ERROR> upon failure or <TINYOBJ_HASH_TABLE_SUCCESS> after succeding
+
+
+
+
hash_table_insert
+
+
Inserts given value in hash, calls hash_table_insert_value
Parameters
hashunsigned long
Hash to be used
valuelong
Value of this entity
hash_table Table to be used
Returns
<TINYOBJ_HASH_TABLE_ERROR> upon failure or <TINYOBJ_HASH_TABLE_SUCCESS> after succeding
+
+
+
+
hash_table_find
+
+
Tries to find given hash in the table
Parameters
hashunsigned long
Hash to be found
hash_table Table to be used
Returns
Entry upon succeding or NULL if failure to find
+
+
+
+
hash_table_maybe_grow
+
+
Grows given hash_table to new_n if new_n is less or equal to capacity
+
+
+
+
hash_table_exists
+
+
Tries to find 'name' in hash_table, returns true if successful
See
+
+
+
+
hash_table_set
+
+
Inserts value into hash_table, in name; creates new entry if it doesn't exist
See
+
+
+
+
hash_table_get
+
+
Returns value of entity with name, -1 when failing
+
+
+
+
UTHash interface to tinyobj
+
+
+
+
tinyobj_material_table_t
+
+
+
+
+
UTHash interface to tinyobj
+
+
+
+
tinyobj_reserved
+
Reserved key used by the hashtable implementation so the table isn't deleted
+
+
+
+
tinyobj_hash_find
+
+
Tries to find object with 'name' as key
Parameters
table Table to be used
nameconst char *
Key
entryOUTtinyobj_material_table_tEntry**
[out] Result
Returns
Sets entry to a valid result if entry was found, otherwise sets it to NULL
+
+
+
+
tinyobj_hash_add
+
+
Adds a material to the given material table If the material was already added its value is replaced
Parameters
table Table to be used
nameconst char *
Key
lengthsize_t
Name length
valuelong
Value of the new entity
Returns
Returns true if successful, false otherwise
+
+
+
+
tinyobj_hash_free
+
+
Frees all entries of a given tinyobj_material_table_t
Parameters
table Table to be freed
bfree_nameunsigned char
Should the key (name) be freed
Usually the name pointer is owned by the materials list by this point
See tinyobj_parse_and_index_mtl_file , freeing could lead to a segfault
+
+
+
+
tinyobj_hash_init
+
+
Initialises given table, and adds a reserved key
+
+
+
+
Material handling (.mtl)
+
+
+
+
+
+
tinyobj_mtl_parse_map
+
+
Parses a texture name from token ('map_')
Supported map types: map_Ka - ambient texture map_Kd - diffuse texture map_Ks - specular texture map_Ns - specular highlight texture map_bump - bump texture map_d - alpha texture Parameters
material Material to be filled
tokenconst char **
Buffer
line_endconst char *
Buffer end (halting point)
Returns
+
+
+
+
tinyobj_mtl_parse_optical
+
+
Parses an optical coefficient from token ('N')
Supported coefficient types: Ni - Index Of Refraction (ior) 'optical density' Ns - Specular exponent 'shininess' Parameters
material Material to be filled
tokenconst char **
Buffer
Returns
+
+
+
+
tinyobj_mtl_parse_color
+
+
Parses a color coefficient from token ('K')
Supported coefficient types: Ka - Ambient Kd - Diffuse Ks - Specular Kt - Transmittance Ke - Emissive Parameters
material Material to be filled
tokenconst char **
Buffer
Returns
+
+
+
+
tinyobj_parse_and_index_mtl_file
+
+
Parses and indexes a given material file
Parameters
materials_out [out] Completed list of materials
num_materials_outunsigned int *
[out] Length of materials_out
filenameconst char *
File to be parsed
material_table Table to be filled (can be NULL)
Returns
+
+
+
+
Aplication Programming Interface (.mtl)
+
+
+
+
tinyobj_parse_mtl_file
+
+
+
+
+
+
tinyobj_material_free
+
+
Frees material data
Parameters
materials Material list to be freed
num_materialsunsigned int
Length of materials
+
+
+
+
Object handling (.obj)
+
+
+
+
CommandType
+
Command identifiers used when parsing
<tinyobj_parse_obj_line>
COMMAND_EMPTYNo command
COMMAND_V'v' Vertex <tinyobj_obj_parse_vertex>
COMMAND_VN'vn' Vertex normal <tinyobj_obj_parse_vertex>
COMMAND_VT'vt' Texture coordinate <tinyobj_obj_parse_vertex>
COMMAND_VP'vp' Parameter space vertex <tinyobj_obj_parse_vertex>
COMMAND_F'f' Face <tinyobj_obj_parse_face>
COMMAND_P'p' Point <tinyobj_obj_parse_point>
COMMAND_L'l' Line <tinyobj_obj_parse_line>
COMMAND_G'g' Group name
COMMAND_O'o' Object name
COMMAND_S's' Smoothing group
COMMAND_USEMTL'usemtl' Use material
COMMAND_MTLLIB'mtllib' Load material
+
+
+
+
Command
+
Command information used to parse obj files <tinyobj_parse_obj_line>
Fields
type Command
info Specific command information
+
+
+
+
Command. u_information
+
Specific command information
Fields
v Geometric vertex <COMMAND_V>
vn Vertex normal <COMMAND_VN>
vt Texture vertex <COMMAND_VT>
vp Parameter space vertex <COMMAND_VP>
f Face <COMMAND_F>
l Line <COMMAND_L>
p Point <COMMAND_P>
g Group <COMMAND_G>
o Object <COMMAND_O>
usemtl Use material <COMMAND_USEMTL>
mtllib Material lib (file) <COMMAND_MTLLIB>
smoothing_id Smoothing group <COMMAND_S>
See
<CommandType>
+
+
+
+
Command. s_group_information
+
Grouping and Display/Render attributes
Fields
Name Name of group
len Group name length
+
+
+
+
CommandInformation
+
General information regarding the commands of a given file
Fields
command_list Array of commands
mtllib_line_index Index of MLLIB in command list (-1 no command)
count command_list count
counter Counter of command types
+
+
+
+
CommandInformation. s_counter
+
Counter of command types
Fields
v Count of geometric vertices
vn Count of vertex normals
vt Count of texture vertices
vp Count of parameter space vertices
f Count of faces
l Count of lines
p Count of points
shapes Shapes
+
+
+
+
Object handling (.obj)
+
+
+
+
tinyobj_obj_free_point
+
+
Frees allocated data of a given point
+
+
+
+
tinyobj_obj_parse_point
+
+
Parses a point from token ('p')
Parameters
command Command information to be filled
tokenconst char **
Token to be parsed
Returns
+
+
+
+
tinyobj_triplet_list_grow
+
+
Grows the given triplet list to a new size If the growth fails the list is unchanged
Parameters
list List to grow
new_lensize_t
New length
Returns
True if successful, false otherwise
+
+
+
+
tinyobj_triplet_list_new
+
+
Allocates memory for a new triplet list
Parameters
lensize_t
Initial length of list
Returns
Pointer to new list when successful, NULL if failure
+
+
+
+
tinyobj_obj_free_line
+
+
Frees allocated data of a given line
+
+
+
+
tinyuobj_obj_parse_line
+
Parses a line from token ('l')
Parameters
command Command information to be filled
token Token to be parsed
Returns
+
+
+
+
tinyobj_obj_free_face
+
+
Frees allocated data of a given face
+
+
+
+
tinyobj_obj_parse_face
+
+
Parses a face from token ('f') and fills given command with face information
Parameters
command Command information to be filled
tokenconst char **
Token to be parsed
triangulateint
(bool) Should this face be triangulated? TINYOBJ_FLAG_TRIANGULATE
Returns
+
+
+
+
tinyobj_obj_parse_vertex
+
+
Parses a vertex from token ('v')
v, vn, vt, vp Parameters
command Command information to be filled
tokenconst char **
Token to be parsed
Returns
+
+
+
+
tinyobj_parse_obj_line
+
+
Parses line of an .obj file
Parameters
command_info Command information list
posint
Current position in command information
pconst char *
String containing line
p_lensize_t
Line length
flagsint
Reading flags
Returns
+
+
+
+
tinyobj_parse_obj_line_traverse
+
+
Traverses and parses all lines of given buffer
Parameters
bufconst char *
Buffer to be parsed
lensize_t
Buffer length
command_info Command information to be filled
flagsunsigned int
Parsing flags
Returns
+
+
+
+
tinyobj_command_info_free
+
+
Frees given command information
+
+
+
+
tinyobj_shape_construct
+
+
Constructs shape information
Parameters
shapes [in/out] Pointer to be filled
num_shapesunsigned int *
[out] Length of shapes
command_info Information used to construct the shapes
+
+
+
+
tinyobj_shape_free
+
+
Frees shape data
Parameters
shapes Shape information to be freed
num_shapesunsigned int
Length of shapes
+
+
+
+
tinyobj_attrib_construct
+
+
Constructs attribute data with given information
Parameters
attrib [in/out] Attribute to be filled
command_info Information used to fill attrib
material_table Material table used (can be NULL)
+
+
+
+
Aplication Programming Interface (.obj)
+
+
+
+
tinyobj_attrib_free
+
+
+
+
+
+
tinyobj_attrib_init
+
+
Initializes given attribute information to default values
+
+
+
+
tinyobj_parse_obj
+
+
Parses .obj file
Parameters
attrib [in/out] Attributes to be filled
shapes [in/out] Shapes to be filled
num_shapesunsigned int *
[in/out] Length of shapes
materials_out [in/out] Materials to be filled
num_materials_outunsigned int *
[in/out] Length of materials
bufconst char *
Buffer to be read
lensize_t
Buffer length
flagsunsigned int
Parsing flags
Returns
+
+
+
+
Compatibility with older versions (deprecated)
+
+
+
+
tinyobj_attrib_free_compat
+
+
+
+
+
+
tinyobj_new2old
+
+
Converts new attribute format to the old one This expects that the faces were triangulated
Parameters
attrib [in] Attribute to be converted
out_attrib [out] Attribute to be filled
Returns
+
+
+
\ No newline at end of file
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..82619a8
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,9 @@
+
+
+TinyObj Documentation
+
+
+
+
+
+Please enable JavaScript to view this documentation.
\ No newline at end of file
diff --git a/doc/menu/classes.js b/doc/menu/classes.js
new file mode 100644
index 0000000..5df92c7
--- /dev/null
+++ b/doc/menu/classes.js
@@ -0,0 +1 @@
+NDMenu.OnSectionLoaded("classes.js",[[1,"Command"],[2,"Command","CClass:Command.",[[1,"s_group_information"],[1,"u_information"]]],[1,"CommandInformation"],[2,"CommandInformation","CClass:CommandInformation.",[[1,"s_counter"]]],[1,"COMPATtinyobj_attrib_t"],[1,"hash_table_entry_t"],[1,"tinyobj_attrib_t"],[1,"tinyobj_coefficient_t"],[1,"tinyobj_face_t"],[1,"tinyobj_line_t"],[2,"tinyobj_line_t","CClass:tinyobj_line_t.",[[1,"s_line_vertex_index"]]],[1,"tinyobj_material_t"],[1,"tinyobj_material_table_t"],[1,"tinyobj_point_t"],[1,"tinyobj_shape_t"],[1,"tinyobj_vertex_index_t"],[1,"tinyobj_vertex_normal_t"],[1,"tinyobj_vertex_param_t"],[1,"tinyobj_vertex_t"],[1,"tinyobj_vertex_texture_t"]]);
\ No newline at end of file
diff --git a/doc/menu/files.js b/doc/menu/files.js
new file mode 100644
index 0000000..fedbe23
--- /dev/null
+++ b/doc/menu/files.js
@@ -0,0 +1 @@
+NDMenu.OnSectionLoaded("files.js",[[1,"tinyobj_loader_c.h"]]);
\ No newline at end of file
diff --git a/doc/menu/tabs.js b/doc/menu/tabs.js
new file mode 100644
index 0000000..4cdef48
--- /dev/null
+++ b/doc/menu/tabs.js
@@ -0,0 +1 @@
+NDMenu.OnTabsLoaded([["File","Files","File:","files.js"],["Class","Classes","CClass:","classes.js"]]);
\ No newline at end of file
diff --git a/doc/other/home.html b/doc/other/home.html
new file mode 100644
index 0000000..ed862c0
--- /dev/null
+++ b/doc/other/home.html
@@ -0,0 +1,13 @@
+
+
+TinyObj Documentation
+
+
+
+
+
+
+
+TinyObj
Tiny but powerful single file wavefront obj loader
+
+
\ No newline at end of file
diff --git a/doc/search/index.js b/doc/search/index.js
new file mode 100644
index 0000000..a8ad783
--- /dev/null
+++ b/doc/search/index.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixIndexLoaded(["arr","bas","cod","com","cre","des","dyn","fix","han","has","ini","is_","len","lic","mac","par","ret","s_c","s_g","s_l","ski","str","tab","tin","try","u_i","unt"]);
\ No newline at end of file
diff --git a/doc/search/keywords/006100720072.js b/doc/search/keywords/006100720072.js
new file mode 100644
index 0000000..d842931
--- /dev/null
+++ b/doc/search/keywords/006100720072.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("arr",["Constant"],[["array",,[[,"Dynamic array basic parameters",,,0,"File:tinyobj_loader_c.h:Dynamic_array_basic_parameters"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006200610073.js b/doc/search/keywords/006200610073.js
new file mode 100644
index 0000000..11b661e
--- /dev/null
+++ b/doc/search/keywords/006200610073.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("bas",["Constant"],[["basic",,[[,"Dynamic array basic parameters",,,0,"File:tinyobj_loader_c.h:Dynamic_array_basic_parameters"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0063006f0064.js b/doc/search/keywords/0063006f0064.js
new file mode 100644
index 0000000..2bd704d
--- /dev/null
+++ b/doc/search/keywords/0063006f0064.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("cod",["Constant"],[["codes",,[["COMPATtinyobj_attrib_t","Hash table return codes",,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.Hash_table_return_codes","CClass:COMPATtinyobj_attrib_t:Hash_table_return_codes"],[,"Return codes",,,0,"File:tinyobj_loader_c.h:Return_codes"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0063006f006d.js b/doc/search/keywords/0063006f006d.js
new file mode 100644
index 0000000..355d97d
--- /dev/null
+++ b/doc/search/keywords/0063006f006d.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("com",["Struct","Constant","Enumeration"],[["Command",,[[,,,,0,"File:tinyobj_loader_c.h:Command","CClass:Command"]]],["COMMAND_EMPTY",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_EMPTY","CClass:tinyobj_material_table_t:COMMAND_EMPTY"]]],["COMMAND_F",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_F","CClass:tinyobj_material_table_t:COMMAND_F"]]],["COMMAND_G",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_G","CClass:tinyobj_material_table_t:COMMAND_G"]]],["COMMAND_L",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_L","CClass:tinyobj_material_table_t:COMMAND_L"]]],["COMMAND_MTLLIB",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_MTLLIB","CClass:tinyobj_material_table_t:COMMAND_MTLLIB"]]],["COMMAND_O",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_O","CClass:tinyobj_material_table_t:COMMAND_O"]]],["COMMAND_P",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_P","CClass:tinyobj_material_table_t:COMMAND_P"]]],["COMMAND_S",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_S","CClass:tinyobj_material_table_t:COMMAND_S"]]],["COMMAND_USEMTL",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_USEMTL","CClass:tinyobj_material_table_t:COMMAND_USEMTL"]]],["COMMAND_V",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_V","CClass:tinyobj_material_table_t:COMMAND_V"]]],["COMMAND_VN",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_VN","CClass:tinyobj_material_table_t:COMMAND_VN"]]],["COMMAND_VP",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_VP","CClass:tinyobj_material_table_t:COMMAND_VP"]]],["COMMAND_VT",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.COMMAND_VT","CClass:tinyobj_material_table_t:COMMAND_VT"]]],["CommandInformation",,[[,,,,0,"File:tinyobj_loader_c.h:CommandInformation","CClass:CommandInformation"]]],["CommandType",,[["tinyobj_material_table_t",,,,2,"File:tinyobj_loader_c.h:tinyobj_material_table_t.CommandType","CClass:tinyobj_material_table_t:CommandType"]]],["COMPATtinyobj_attrib_t",,[[,,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t","CClass:COMPATtinyobj_attrib_t"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006300720065.js b/doc/search/keywords/006300720065.js
new file mode 100644
index 0000000..911b38d
--- /dev/null
+++ b/doc/search/keywords/006300720065.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("cre",["Function"],[["create_hash_table",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.create_hash_table","CClass:tinyobj_material_table_t:create_hash_table"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006400650073.js b/doc/search/keywords/006400650073.js
new file mode 100644
index 0000000..043bc5f
--- /dev/null
+++ b/doc/search/keywords/006400650073.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("des",["Function"],[["destroy_hash_table",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.destroy_hash_table","CClass:tinyobj_material_table_t:destroy_hash_table"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/00640079006e.js b/doc/search/keywords/00640079006e.js
new file mode 100644
index 0000000..89db2c6
--- /dev/null
+++ b/doc/search/keywords/00640079006e.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("dyn",["Constant","Function"],[["Dynamic",,[[,"Dynamic array basic parameters",,,0,"File:tinyobj_loader_c.h:Dynamic_array_basic_parameters"]]],["dynamic_fgets",,[["COMPATtinyobj_attrib_t",,,,1,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.dynamic_fgets","CClass:COMPATtinyobj_attrib_t:dynamic_fgets"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006600690078.js b/doc/search/keywords/006600690078.js
new file mode 100644
index 0000000..044b29c
--- /dev/null
+++ b/doc/search/keywords/006600690078.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("fix",["Function"],[["fixIndex",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.fixIndex","CClass:COMPATtinyobj_attrib_t:fixIndex"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/00680061006e.js b/doc/search/keywords/00680061006e.js
new file mode 100644
index 0000000..4c51f16
--- /dev/null
+++ b/doc/search/keywords/00680061006e.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("han",["Function"],[["handling",,[["COMPATtinyobj_attrib_t","String handling macros",,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.String_handling_macros","CClass:COMPATtinyobj_attrib_t:String_handling_macros"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006800610073.js b/doc/search/keywords/006800610073.js
new file mode 100644
index 0000000..aa866e5
--- /dev/null
+++ b/doc/search/keywords/006800610073.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("has",["Constant","Function","Struct"],[["Hash",,[["COMPATtinyobj_attrib_t","Hash table return codes",,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.Hash_table_return_codes","CClass:COMPATtinyobj_attrib_t:Hash_table_return_codes"]]],["hash_djb2",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_djb2","CClass:tinyobj_material_table_t:hash_djb2"]]],["hash_table_entry_t",,[[,,,,2,"File:tinyobj_loader_c.h:hash_table_entry_t","CClass:hash_table_entry_t"]]],["hash_table_exists",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_table_exists","CClass:tinyobj_material_table_t:hash_table_exists"]]],["hash_table_find",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_table_find","CClass:tinyobj_material_table_t:hash_table_find"]]],["hash_table_get",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_table_get","CClass:tinyobj_material_table_t:hash_table_get"]]],["hash_table_insert",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_table_insert","CClass:tinyobj_material_table_t:hash_table_insert"]]],["hash_table_insert_value",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_table_insert_value","CClass:tinyobj_material_table_t:hash_table_insert_value"]]],["hash_table_maybe_grow",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_table_maybe_grow","CClass:tinyobj_material_table_t:hash_table_maybe_grow"]]],["hash_table_set",,[["tinyobj_material_table_t",,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t.hash_table_set","CClass:tinyobj_material_table_t:hash_table_set"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0069006e0069.js b/doc/search/keywords/0069006e0069.js
new file mode 100644
index 0000000..2f0c61c
--- /dev/null
+++ b/doc/search/keywords/0069006e0069.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("ini",["Function"],[["initMaterial",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.initMaterial","CClass:tinyobj_material_table_t:initMaterial"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/00690073005f.js b/doc/search/keywords/00690073005f.js
new file mode 100644
index 0000000..19e3f2b
--- /dev/null
+++ b/doc/search/keywords/00690073005f.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("is_",["Function"],[["IS_DIGIT",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.IS_DIGIT","CClass:COMPATtinyobj_attrib_t:IS_DIGIT"]]],["is_line_ending",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.is_line_ending","CClass:COMPATtinyobj_attrib_t:is_line_ending"]]],["IS_NEW_LINE",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.IS_NEW_LINE","CClass:COMPATtinyobj_attrib_t:IS_NEW_LINE"]]],["IS_SPACE",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.IS_SPACE","CClass:COMPATtinyobj_attrib_t:IS_SPACE"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006c0065006e.js b/doc/search/keywords/006c0065006e.js
new file mode 100644
index 0000000..0c8ffdb
--- /dev/null
+++ b/doc/search/keywords/006c0065006e.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("len",["Function"],[["length_until_line_feed",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.length_until_line_feed","CClass:COMPATtinyobj_attrib_t:length_until_line_feed"]]],["length_until_newline_comment_space",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.length_until_newline_comment_space","CClass:COMPATtinyobj_attrib_t:length_until_newline_comment_space"]]],["length_until_space",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.length_until_space","CClass:COMPATtinyobj_attrib_t:length_until_space"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006c00690063.js b/doc/search/keywords/006c00690063.js
new file mode 100644
index 0000000..551ee8c
--- /dev/null
+++ b/doc/search/keywords/006c00690063.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("lic",["Information"],[["License",,[[,,,,0,"File:tinyobj_loader_c.h:License"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/006d00610063.js b/doc/search/keywords/006d00610063.js
new file mode 100644
index 0000000..26f9bde
--- /dev/null
+++ b/doc/search/keywords/006d00610063.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("mac",["Function"],[["macros",,[["COMPATtinyobj_attrib_t","String handling macros",,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.String_handling_macros","CClass:COMPATtinyobj_attrib_t:String_handling_macros"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/007000610072.js b/doc/search/keywords/007000610072.js
new file mode 100644
index 0000000..3661354
--- /dev/null
+++ b/doc/search/keywords/007000610072.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("par",["Constant","Function"],[["parameters",,[[,"Dynamic array basic parameters",,,0,"File:tinyobj_loader_c.h:Dynamic_array_basic_parameters"]]],["parseFloat",,[["COMPATtinyobj_attrib_t",,,,1,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.parseFloat","CClass:COMPATtinyobj_attrib_t:parseFloat"]]],["parseFloat2",,[["COMPATtinyobj_attrib_t",,,,1,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.parseFloat2","CClass:COMPATtinyobj_attrib_t:parseFloat2"]]],["parseFloat3",,[["COMPATtinyobj_attrib_t",,,,1,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.parseFloat3","CClass:COMPATtinyobj_attrib_t:parseFloat3"]]],["parseInt",,[["COMPATtinyobj_attrib_t",,,,1,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.parseInt","CClass:COMPATtinyobj_attrib_t:parseInt"]]],["parseRawTriple",,[["COMPATtinyobj_attrib_t",,,,1,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.parseRawTriple","CClass:COMPATtinyobj_attrib_t:parseRawTriple"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/007200650074.js b/doc/search/keywords/007200650074.js
new file mode 100644
index 0000000..492ee90
--- /dev/null
+++ b/doc/search/keywords/007200650074.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("ret",["Constant"],[["Return",,[[,"Return codes",,,0,"File:tinyobj_loader_c.h:Return_codes"],["COMPATtinyobj_attrib_t","Hash table return codes",,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.Hash_table_return_codes","CClass:COMPATtinyobj_attrib_t:Hash_table_return_codes"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0073005f0063.js b/doc/search/keywords/0073005f0063.js
new file mode 100644
index 0000000..4c513ac
--- /dev/null
+++ b/doc/search/keywords/0073005f0063.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("s_c",["Struct"],[["s_counter",,[["CommandInformation",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter","CClass:CommandInformation.s_counter"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0073005f0067.js b/doc/search/keywords/0073005f0067.js
new file mode 100644
index 0000000..86f3cca
--- /dev/null
+++ b/doc/search/keywords/0073005f0067.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("s_g",["Struct"],[["s_group_information",,[["Command",,,,0,"File:tinyobj_loader_c.h:Command.s_group_information","CClass:Command.s_group_information"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0073005f006c.js b/doc/search/keywords/0073005f006c.js
new file mode 100644
index 0000000..db9a99d
--- /dev/null
+++ b/doc/search/keywords/0073005f006c.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("s_l",["Struct"],[["s_line_vertex_index",,[["tinyobj_line_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_line_t.s_line_vertex_index","CClass:tinyobj_line_t.s_line_vertex_index"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0073006b0069.js b/doc/search/keywords/0073006b0069.js
new file mode 100644
index 0000000..58dbbbc
--- /dev/null
+++ b/doc/search/keywords/0073006b0069.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("ski",["Function"],[["skip_space",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.skip_space","CClass:COMPATtinyobj_attrib_t:skip_space"]]],["skip_space_and_cr",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.skip_space_and_cr","CClass:COMPATtinyobj_attrib_t:skip_space_and_cr"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/007300740072.js b/doc/search/keywords/007300740072.js
new file mode 100644
index 0000000..d714a71
--- /dev/null
+++ b/doc/search/keywords/007300740072.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("str",["Function"],[["strdup_ml",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.strdup_ml","CClass:COMPATtinyobj_attrib_t:strdup_ml"]]],["String",,[["COMPATtinyobj_attrib_t","String handling macros",,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.String_handling_macros","CClass:COMPATtinyobj_attrib_t:String_handling_macros"]]],["strndup",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.strndup","CClass:COMPATtinyobj_attrib_t:strndup"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/007400610062.js b/doc/search/keywords/007400610062.js
new file mode 100644
index 0000000..27288d8
--- /dev/null
+++ b/doc/search/keywords/007400610062.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("tab",["Constant"],[["table",,[["COMPATtinyobj_attrib_t","Hash table return codes",,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.Hash_table_return_codes","CClass:COMPATtinyobj_attrib_t:Hash_table_return_codes"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/00740069006e.js b/doc/search/keywords/00740069006e.js
new file mode 100644
index 0000000..be2f54f
--- /dev/null
+++ b/doc/search/keywords/00740069006e.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("tin",["Function","Struct","Constant"],[["tinyobj_attrib_construct",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_attrib_construct","CClass:CommandInformation.s_counter:tinyobj_attrib_construct"]]],["tinyobj_attrib_free",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_attrib_free","CClass:CommandInformation.s_counter:tinyobj_attrib_free"]]],["tinyobj_attrib_free_compat",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_attrib_free_compat","CClass:CommandInformation.s_counter:tinyobj_attrib_free_compat"]]],["tinyobj_attrib_init",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_attrib_init","CClass:CommandInformation.s_counter:tinyobj_attrib_init"]]],["tinyobj_attrib_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_attrib_t","CClass:tinyobj_attrib_t"]]],["tinyobj_coefficient_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_coefficient_t","CClass:tinyobj_coefficient_t"]]],["tinyobj_command_info_free",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_command_info_free","CClass:CommandInformation.s_counter:tinyobj_command_info_free"]]],["TINYOBJ_COUPLE_GROWTH_FACTOR",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_COUPLE_GROWTH_FACTOR"]]],["TINYOBJ_COUPLE_INITIAL_COUNT",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_COUPLE_INITIAL_COUNT"]]],["TINYOBJ_ENABLE_OLDER_ATTRIBUTE",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ENABLE_OLDER_ATTRIBUTE"]]],["TINYOBJ_ERROR_EMPTY",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ERROR_EMPTY"]]],["TINYOBJ_ERROR_FILE_OPERATION",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ERROR_FILE_OPERATION"]]],["TINYOBJ_ERROR_INVALID_PARAMETER",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ERROR_INVALID_PARAMETER"]]],["TINYOBJ_ERROR_MALFORMED_PARAMETER",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ERROR_MALFORMED_PARAMETER"]]],["TINYOBJ_ERROR_MEMORY",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ERROR_MEMORY"]]],["TINYOBJ_ERROR_NOT_SET",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ERROR_NOT_SET"]]],["TINYOBJ_ERROR_UNKNOWN_PARAMETER",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_ERROR_UNKNOWN_PARAMETER"]]],["tinyobj_face_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_face_t","CClass:tinyobj_face_t"]]],["TINYOBJ_FLAG_TRIANGULATE",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_FLAG_TRIANGULATE"]]],["tinyobj_hash_add",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_hash_add","CClass:tinyobj_material_table_t:tinyobj_hash_add"]]],["tinyobj_hash_find",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_hash_find","CClass:tinyobj_material_table_t:tinyobj_hash_find"]]],["tinyobj_hash_free",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_hash_free","CClass:tinyobj_material_table_t:tinyobj_hash_free"]]],["tinyobj_hash_init",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_hash_init","CClass:tinyobj_material_table_t:tinyobj_hash_init"]]],["TINYOBJ_HASH_TABLE_DEFAULT_SIZE",,[["COMPATtinyobj_attrib_t",,,,2,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.TINYOBJ_HASH_TABLE_DEFAULT_SIZE","CClass:COMPATtinyobj_attrib_t:TINYOBJ_HASH_TABLE_DEFAULT_SIZE"]]],["TINYOBJ_HASH_TABLE_ERROR",,[["COMPATtinyobj_attrib_t",,,,2,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.TINYOBJ_HASH_TABLE_ERROR","CClass:COMPATtinyobj_attrib_t:TINYOBJ_HASH_TABLE_ERROR"]]],["TINYOBJ_HASH_TABLE_SUCCESS",,[["COMPATtinyobj_attrib_t",,,,2,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.TINYOBJ_HASH_TABLE_SUCCESS","CClass:COMPATtinyobj_attrib_t:TINYOBJ_HASH_TABLE_SUCCESS"]]],["TINYOBJ_INVALID_INDEX",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_INVALID_INDEX"]]],["tinyobj_line_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_line_t","CClass:tinyobj_line_t"]]],["TINYOBJ_LOADER_C_IMPLEMENTATION",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_LOADER_C_IMPLEMENTATION"]]],["tinyobj_material_free",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_material_free","CClass:tinyobj_material_table_t:tinyobj_material_free"]]],["TINYOBJ_MATERIAL_GROWTH_FACTOR",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_MATERIAL_GROWTH_FACTOR"]]],["TINYOBJ_MATERIAL_INITIAL_COUNT",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_MATERIAL_INITIAL_COUNT"]]],["tinyobj_material_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_material_t","CClass:tinyobj_material_t"]]],["tinyobj_material_table_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_material_table_t","CClass:tinyobj_material_table_t"]]],["tinyobj_mtl_parse_color",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_mtl_parse_color","CClass:tinyobj_material_table_t:tinyobj_mtl_parse_color"]]],["tinyobj_mtl_parse_map",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_mtl_parse_map","CClass:tinyobj_material_table_t:tinyobj_mtl_parse_map"]]],["tinyobj_mtl_parse_optical",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_mtl_parse_optical","CClass:tinyobj_material_table_t:tinyobj_mtl_parse_optical"]]],["tinyobj_new2old",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_new2old","CClass:CommandInformation.s_counter:tinyobj_new2old"]]],["TINYOBJ_NO_COMMAND",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_NO_COMMAND"]]],["tinyobj_obj_free_face",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_obj_free_face","CClass:CommandInformation.s_counter:tinyobj_obj_free_face"]]],["tinyobj_obj_free_line",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_obj_free_line","CClass:CommandInformation.s_counter:tinyobj_obj_free_line"]]],["tinyobj_obj_free_point",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_obj_free_point","CClass:CommandInformation.s_counter:tinyobj_obj_free_point"]]],["tinyobj_obj_parse_face",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_obj_parse_face","CClass:CommandInformation.s_counter:tinyobj_obj_parse_face"]]],["tinyobj_obj_parse_point",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_obj_parse_point","CClass:CommandInformation.s_counter:tinyobj_obj_parse_point"]]],["tinyobj_obj_parse_vertex",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_obj_parse_vertex","CClass:CommandInformation.s_counter:tinyobj_obj_parse_vertex"]]],["tinyobj_parse_and_index_mtl_file",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_parse_and_index_mtl_file","CClass:tinyobj_material_table_t:tinyobj_parse_and_index_mtl_file"]]],["tinyobj_parse_mtl_file",,[["tinyobj_material_table_t",,,,0,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_parse_mtl_file","CClass:tinyobj_material_table_t:tinyobj_parse_mtl_file"]]],["tinyobj_parse_obj",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_parse_obj","CClass:CommandInformation.s_counter:tinyobj_parse_obj"]]],["tinyobj_parse_obj_line",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_parse_obj_line","CClass:CommandInformation.s_counter:tinyobj_parse_obj_line"]]],["tinyobj_parse_obj_line_traverse",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_parse_obj_line_traverse","CClass:CommandInformation.s_counter:tinyobj_parse_obj_line_traverse"]]],["TINYOBJ_POINT_GROWTH_FACTOR",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_POINT_GROWTH_FACTOR"]]],["TINYOBJ_POINT_INITIAL_COUNT",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_POINT_INITIAL_COUNT"]]],["tinyobj_point_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_point_t","CClass:tinyobj_point_t"]]],["tinyobj_reserved",,[["tinyobj_material_table_t",,,,2,"File:tinyobj_loader_c.h:tinyobj_material_table_t.tinyobj_reserved","CClass:tinyobj_material_table_t:tinyobj_reserved"]]],["tinyobj_shape_construct",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_shape_construct","CClass:CommandInformation.s_counter:tinyobj_shape_construct"]]],["tinyobj_shape_free",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_shape_free","CClass:CommandInformation.s_counter:tinyobj_shape_free"]]],["tinyobj_shape_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_shape_t","CClass:tinyobj_shape_t"]]],["TINYOBJ_SUCCESS",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_SUCCESS"]]],["TINYOBJ_TRIPLET_GROWTH_FACTOR",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_TRIPLET_GROWTH_FACTOR"]]],["TINYOBJ_TRIPLET_INITIAL_COUNT",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_TRIPLET_INITIAL_COUNT"]]],["tinyobj_triplet_list_grow",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_triplet_list_grow","CClass:CommandInformation.s_counter:tinyobj_triplet_list_grow"]]],["tinyobj_triplet_list_new",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyobj_triplet_list_new","CClass:CommandInformation.s_counter:tinyobj_triplet_list_new"]]],["TINYOBJ_USE_UTHASH",,[[,,,,2,"File:tinyobj_loader_c.h:TINYOBJ_USE_UTHASH"]]],["tinyobj_vertex_index_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_vertex_index_t","CClass:tinyobj_vertex_index_t"]]],["tinyobj_vertex_normal_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_vertex_normal_t","CClass:tinyobj_vertex_normal_t"]]],["tinyobj_vertex_param_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_vertex_param_t","CClass:tinyobj_vertex_param_t"]]],["tinyobj_vertex_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_vertex_t","CClass:tinyobj_vertex_t"]]],["tinyobj_vertex_texture_t",,[[,,,,1,"File:tinyobj_loader_c.h:tinyobj_vertex_texture_t","CClass:tinyobj_vertex_texture_t"]]],["tinyuobj_obj_parse_line",,[["CommandInformation::s_counter",,,,0,"File:tinyobj_loader_c.h:CommandInformation.s_counter.tinyuobj_obj_parse_line","CClass:CommandInformation.s_counter:tinyuobj_obj_parse_line"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/007400720079.js b/doc/search/keywords/007400720079.js
new file mode 100644
index 0000000..8f7a61c
--- /dev/null
+++ b/doc/search/keywords/007400720079.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("try",["Function"],[["try_parse_float",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.try_parse_float","CClass:COMPATtinyobj_attrib_t:try_parse_float"]]],["tryParseDouble",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.tryParseDouble","CClass:COMPATtinyobj_attrib_t:tryParseDouble"]]],["tryParseDouble_assemble",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.tryParseDouble_assemble","CClass:COMPATtinyobj_attrib_t:tryParseDouble_assemble"]]],["tryParseDouble_integer",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.tryParseDouble_integer","CClass:COMPATtinyobj_attrib_t:tryParseDouble_integer"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0075005f0069.js b/doc/search/keywords/0075005f0069.js
new file mode 100644
index 0000000..b494621
--- /dev/null
+++ b/doc/search/keywords/0075005f0069.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("u_i",["Struct"],[["u_information",,[["Command",,,,0,"File:tinyobj_loader_c.h:Command.u_information","CClass:Command.u_information"]]]]);
\ No newline at end of file
diff --git a/doc/search/keywords/0075006e0074.js b/doc/search/keywords/0075006e0074.js
new file mode 100644
index 0000000..2e16873
--- /dev/null
+++ b/doc/search/keywords/0075006e0074.js
@@ -0,0 +1 @@
+NDSearch.OnPrefixDataLoaded("unt",["Function"],[["until_space",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.until_space","CClass:COMPATtinyobj_attrib_t:until_space"]]],["until_space_cr_slash",,[["COMPATtinyobj_attrib_t",,,,0,"File:tinyobj_loader_c.h:COMPATtinyobj_attrib_t.until_space_cr_slash","CClass:COMPATtinyobj_attrib_t:until_space_cr_slash"]]]]);
\ No newline at end of file
diff --git a/doc/styles/Default/Default.css b/doc/styles/Default/Default.css
new file mode 100644
index 0000000..6cc2700
--- /dev/null
+++ b/doc/styles/Default/Default.css
@@ -0,0 +1,12 @@
+/*
+This file is part of Natural Docs, which is Copyright © 2003-2018 Code Clear LLC.
+Natural Docs is licensed under version 3 of the GNU Affero General Public
+License (AGPL). Refer to License.txt or www.naturaldocs.org for the
+complete details.
+
+This file may be distributed with documentation files generated by Natural Docs.
+Such documentation is not covered by Natural Docs' copyright and licensing,
+and may have its own copyright and distribution terms as decided by its author.
+*/
+
+html{height:100%;font:10pt "Trebuchet MS",Tahoma,Geneva,sans-serif}body.NDFramePage{background-color:#E0E0E0;margin:0;padding:0}a:link,a:visited{text-decoration:none}a:hover,a:active{text-decoration:underline}table{margin:0;padding:0;border:0 none;border-spacing:0}td{margin:0;padding:0;border:0 none;border-spacing:0;vertical-align:top}#NDLoadingNotice{height:40px;background:URL("images/menu-loading.gif") no-repeat center center}#NDJavaScriptRequiredNotice{margin:20px;text-align:center;font:10pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif}#NDHeader{background-color:#7070C0;padding:.65em .65em .55em .65em;border-bottom:1px solid #7070C0;z-index:2}#HTitle{font:22pt Georgia,serif;text-shadow:0 .1ex .3ex #202020;line-height:90%}#HTitle,#HTitle a:link,#HTitle a:hover,#HTitle a:active,#HTitle a:visited{color:#FFFFFF;text-decoration:none}#HSubtitle{font:10pt Georgia,serif;margin-left:.65em;text-shadow:0 .1ex .3ex #202020}#HSubtitle,#HSubtitle a:link,#HSubtitle a:hover,#HSubtitle a:active,#HSubtitle a:visited{color:#C8C8C8;text-decoration:none}#NDSearchField{z-index:201;width:25ex;color:#000000;font:10pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif;background:#FFFFFF URL("images/search.png") no-repeat left center;padding:.5ex 1.5ex .5ex 3.5ex;border:0 none;-moz-border-radius:5ex;border-radius:5ex;display:none}.IE #NDSearchField{font:inherit}#NDSearchField.DefaultText{color:#808080;font-style:italic}#NDSearchField:focus{outline:none}#NDSearchResults{z-index:200;overflow:auto;font:10pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif;background-color:#FFFFFF;padding:.5ex 0;border-style:solid;border-width:1px;border-color:#BCBCBC #747474 #747474 #BCBCBC;-moz-box-shadow:.15ex .25ex .4ex #606060;-webkit-box-shadow:.15ex .25ex .4ex #606060;box-shadow:.15ex .25ex .4ex #606060}.SeEntry{padding:.2ex 1.25ex .2ex 3.25ex;color:#000000;border-width:1px 0;border-style:solid;border-color:#FFFFFF}.SeEntry:hover{background-color:#E8E8E8}#SeSelectedEntry{background-color:#E8E8E8;border-color:#D0D0D0}a.SeEntry{display:block}a.SeEntry:link,a.SeEntry:hover,a.SeEntry:active,a.SeEntry:visited{color:#000000;text-decoration:none}.SeQualifier,.SeChildCount{font-weight:normal;color:#7C7C7C}.SeParent.open .SeChildCount{display:none}.SeChildren.closed{display:none}.SeChildren .SeEntry{padding-left:5.5ex}.SeEntryIcon{position:absolute;left:1.7ex;width:.8ex;height:.9em;margin-top:2px}.SeChildren .SeEntryIcon{left:3.9ex}.SeParent .SeEntryIcon{margin-top:4px;margin-left:1px;background:url("images/search-parent.png") center center no-repeat}.TClass .SeEntryIcon,.TInterface .SeEntryIcon,.TStruct .SeEntryIcon{width:2ex;height:1em;left:.9ex;background:url("images/search-class.png") center center no-repeat}.SeChildren .TClass .SeEntryIcon,.SeChildren .TInterface .SeEntryIcon,.SeChildren .TStruct .SeEntryIcon{left:3.15ex}.SeEntry.TClass,.SeEntry.TInterface,.SeEntry.TStruct{font-weight:bold}.SeEntry.MoreResults{font-weight:bold;margin-top:.75em}.SeStatus{margin:.25ex 1.25ex;color:#A0A0A0;text-align:center}.SeEntry+.SeStatus,.SeChildren+.SeStatus{margin-top:.75ex;padding-top:.75ex;border-top:1px solid #C0C0C0}.SeStatus.Searching{font-style:italic}#NDMenu{background-color:#E0E0E0;z-index:5;overflow:auto;width:30ex;display:none}.IE7 #NDMenu{overflow-x:hidden}#NDMenu a:hover,#NDMenu a:active{text-decoration:none}.MLoadingNotice{height:20px;background:URL("images/menu-loading.gif") no-repeat center bottom}#MTabBar{background-color:#C0C0C0}.MTab{background-color:#E0E0E0;border-right:1px solid #C0C0C0;border-bottom:1px solid #C0C0C0;display:inline-block;padding:2px 5px;opacity:.55}.MTab:hover{background-color:#EEEEEE;opacity:1}.MTab.Selected{border-bottom:1px solid #E0E0E0;opacity:1}a.MTab:active,a.MTab:focus{outline:0}.IE6 .MTab,.IE7 .MTab,.IE8 .MTab{background-color:#D0D0D0}.IE6 .MTab.Selected,.IE7 .MTab.Selected,.IE8 .MTab.Selected{background-color:#E0E0E0}.IE6 .MTab:hover,.IE7 .MTab:hover,.IE8 .MTab:hover{background-color:#EEEEEE}.MTabIcon{display:inline-block;width:16px;height:16px;position:relative;top:3px}.IE7 .MTabIcon,.IE6 .MTabIcon{top:0}#MFileTab .MTabIcon{background:url("images/menu-tab-files.png") center center no-repeat}#MClassTab .MTabIcon{background:url("images/menu-tab-classes.png") center center no-repeat}#MDatabaseTab .MTabIcon{background:url("images/menu-tab-database.png") center center no-repeat}.MTabTitle{display:inline-block;color:#606060;font:italic 10pt "Trebuchet MS",Tahoma,Geneva,sans-serif;margin:0 4px}.MTab.Narrow .MTabTitle{width:1px;margin:0 -1px 0 0;visibility:hidden}.IE6 .MTab.Narrow .MTabTitle{display:none}.MEntry{display:block;font:10pt "Trebuchet MS",Tahoma,Geneva,sans-serif;padding:.4ex 1.5ex .4ex 3ex}.MEntry,a.MEntry{color:#606060}a.MEntry:hover,a.MEntry:active{background-color:#EEEEEE}.MTabAsFolder{padding-left:1ex}.MFolder{font-weight:bold}.MFolder.Parent{border-bottom:1px solid #C0C0C0}.MFolder.Parent,a.MFolder.Parent{color:#808080}.MFolder.Parent,.MFolder.Selected{padding-left:1.5ex}.MFolder.Parent.Empty{border-bottom-style:dashed;color:#B0B0B0}.MFolder.Child{padding-right:2.5ex}.MFolderIcon{position:absolute;right:0;background:url("images/menu-folder-arrow.png") center center no-repeat;width:6px;height:7px;padding:.8em 1.2em 0 0}.IE6 .MFolderIcon{padding-top:0}.MFolder.Selected{font-weight:bold;background-color:#FFFFFF;border-bottom:3px solid #888888}.MFile.Selected{background-color:#FFFFFF;font-weight:bold}#NDSummary{background-color:#F8F8F8;border-left:1px solid #D8D8D8;border-right:1px solid #E8E8E8;z-index:4;overflow:auto;width:30ex;display:none}#NDSummary a:hover,#NDSummary a:active{text-decoration:none}.SuLoadingNotice{height:20px;background:URL("images/summary-loading.gif") no-repeat center bottom}.SuEntry{display:block;font:10pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif;padding:1px 1.5ex 1px 3.5ex}.SuEntry.first{margin-top:1ex}.SuEntry.last{margin-bottom:2ex}.SuEntry{color:#707070}.SuEntryIcon{position:absolute;left:1.8ex;width:.8ex;height:.9em;margin-top:2px}.SuEntry.TClass,.SuEntry.TInterface,.SuEntry.TStruct,.SuEntry.TSection,.SuEntry.TDatabase,.SuEntry.TDatabaseTable,.SuEntry.TFile.first{font:bold 11pt "Trebuchet MS",Tahoma,Geneva,sans-serif;color:#6B6B6B;padding-left:1.25ex;padding-top:.75ex;padding-bottom:.75ex;margin:2.5ex 0 .5ex 0;line-height:120%;border-bottom:1px solid #D4D4D4}.SuEntry.TClass.first,.SuEntry.TInterface.first,.SuEntry.TStruct.first,.SuEntry.TSection.first,.SuEntry.TDatabase.first,.SuEntry.TDatabaseTable.first,.SuEntry.TFile.first{border-top:0 none;margin-top:0}.SuEntry .Qualifier{font-weight:normal}.SuEntry.TGroup{font:bold 10pt "Trebuchet MS",Tahoma,Geneva,sans-serif;color:#6B6B6B;padding-left:1.5ex;padding-top:.25ex;padding-bottom:.25ex;margin:1.25ex 0 0 0;line-height:120%}.SuEntry.TClass+.SuEntry.TGroup,.SuEntry.TInterface+.SuEntry.TGroup{margin-top:.75ex}a.SuEntry:hover,a.SuEntry:active{background-color:#E6E6E6}#NDMenuSizer,#NDSummarySizer{cursor:e-resize;width:.8ex;z-index:101}#NDMenuSizer:hover,#NDMenuSizer.Dragging{background-color:#F8F8F8;border-left:1px solid #D8D8D8;border-right:1px solid #D8D8D8}#NDSummarySizer:hover,#NDSummarySizer.Dragging{background-color:#FFFFFF;border-right:1px solid #E8E8E8}#NDContentCover{position:fixed;z-index:100;background:url("images/transparent.gif") no-repeat}#NDContent{z-index:3;overflow:hidden;display:none}#CFrame{width:100%;height:100%;border:0 none}body.NDContentPage{background-color:#FFFFFF;padding:.5em 1em;margin:0}.CTopic{margin-bottom:1em;font:10pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif}.CTitle{font:bold 14pt "Trebuchet MS",Tahoma,Geneva,sans-serif;margin:1.5em 0 .5em 0;border-bottom:1px solid #C0C0C0}.CTopic.first .CTitle{margin-top:0}.CTitle .Qualifier{color:#404040;font-weight:normal}.TClass .CTitle,.TInterface .CTitle,.TStruct .CTitle,.TSection .CTitle,.TDatabase .CTitle,.TDatabaseTable .CTitle,.TFile.first .CTitle{font-size:20pt;border-color:#A8A8A8;line-height:110%;padding-bottom:.05em}.TGroup .CTitle{text-transform:uppercase;font-size:16pt;border-bottom:2px solid #000000}.CHeading{font:bold 11pt "Trebuchet MS",Tahoma,Geneva,sans-serif;margin:1.2em 0 .5em 0}.CHeading:first-child{margin-top:0}.CTopic p{margin:0 0 .5em 0;text-indent:4ex}.CTopic a:link,.CTopic a:visited,.CTopic a:hover{color:#880000}.CTopic a:active{color:#F04040}.CTopic ul{margin:0 0 0 6ex;padding:0}.CTopic ul ul{margin:0 0 0 2.5ex}.CTopic li{list-style-type:disc}.CTopic li p{text-indent:0}table.CDefinitionList{margin:0 0 0 4ex}td.CDLEntry{font:bold 10pt "Trebuchet MS",Tahoma,Geneva,sans-serif;padding:0 2ex .5em 0;min-width:18%;line-height:123%}.CDLParameterType{opacity:0.5}td.CDLDefinition p{text-indent:0}.CTopic pre{font:9pt Consolas,"Courier New",monospace;overflow:auto;margin:1em 4.5ex;border-style:solid;border-color:#E0E0E0;border-width:1px 1px 1px 1.5ex;padding:.5ex 1ex}.CBodyNDMarkup{font:8pt Verdana,sans-serif;color:#787800;background-color:#FFFFF0;border:1px solid #A0A000;padding:.5em}.NDPrototype{font:10pt Consolas,"Courier New",monospace;background-color:#FFFFFF;border:1px solid #808080;margin:0 5ex 1em 5ex;padding:.75ex 1.25ex 1.25ex 1.25ex;overflow:auto}.NDPrototype td{white-space:nowrap}.NDPrototype.NarrowForm td.PBeforeParameters,.NDPrototype.NarrowForm td.PAfterParameters,.NDPrototype.NarrowForm td.PDefaultValue{white-space:normal}.NarrowForm table.PParameters td.last{width:100%}.NarrowForm table.PParameters td.first,.NarrowForm.CStyle td.PAfterParameters{padding-left:3.5ex}.WideForm td.PAfterParameters{vertical-align:bottom}.CStyle .PPostPrototypeLine{padding-left:3.5ex}.CDLParameterType{font:8pt Consolas,"Courier New",monospace;white-space:nowrap}.NDPrototype a:link,.NDPrototype a:visited,.NDPrototype a:hover,.NDPrototype a:active,.CDLParameterType a:link,.CDLParameterType a:visited,.CDLParameterType a:hover,.CDLParameterType a:active{color:#000000}.NDPrototype:hover a:link,.NDPrototype:hover a:visited,.NDPrototype:hover a:hover,.NDPrototype:hover a:active,.CDLEntry:hover .CDLParameterType a:link,.CDLEntry:hover .CDLParameterType a:visited,.CDLEntry:hover .CDLParameterType a:hover,.CDLEntry:hover .CDLParameterType a:active{text-decoration:underline}.PNameModifier,.CStyle .PModifierQualifier,.PDefaultValue,.PDefaultValueSeparator{opacity:0.45}.PNameModifier,.CStyle .PModifierQualifier,.CStyle .PNamePrefix{text-align:right}.NDClassPrototype{margin:0 4ex 1em 4ex}.CPEntry{border-width:1px 2px 2px 1px;border-style:solid;border-color:#000000;padding:.5ex 1ex;margin-bottom:4px;-moz-border-radius:1ex;border-radius:1ex;background-color:#FFFFFF;overflow:auto}a.CPEntry,a.CPAdditionalChildrenNotice{display:block}a.CPEntry:link,a.CPEntry:hover,a.CPEntry:visited,a.CPEntry:active{color:#000000;text-decoration:none}a.CPEntry:hover .CPName,a.CPEntry:active .CPName{text-decoration:underline}.NDClassPrototype .CPEntry.Parent{margin-right:3.5ex}.NDClassPrototype.HasChildren .CPEntry.Parent{margin-right:7ex}.NDClassPrototype.HasParents .CPEntry.Current{margin-left:3.5ex}.NDClassPrototype.HasChildren .CPEntry.Current{margin-right:3.5ex}.NDClassPrototype .CPEntry.Child,.NDClassPrototype .CPAdditionalChildrenNotice{margin-left:3.5ex}.NDClassPrototype.HasParents .CPEntry.Child,.NDClassPrototype.HasParents .CPAdditionalChildrenNotice{margin-left:7ex}.CPName{font:bold 11pt "Trebuchet MS",Tahoma,Geneva,sans-serif;line-height:110%}.CPName .Qualifier,.CPName .TemplateSignature{font-weight:normal;color:#282828}.CPEntry.Parent .CPName,.CPEntry.Child .CPName{font-size:10pt}.CPEntry.Parent,.CPEntry.Child{opacity:.5}.CPEntry .CPPrePrototypeLine,.CPEntry .CPModifiers,.CPEntry .CPPostPrototypeLine{font:8pt Consolas,"Courier New",monospace;opacity:.5}.CPEntry.Parent .CPModifiers,.CPEntry.Child .CPModifiers{opacity:.75}.CPEntry .CPPostPrototypeLine{margin-left:4ex}a.CPAdditionalChildrenNotice:link,a.CPAdditionalChildrenNotice:visited,a.CPAdditionalChildrenNotice:hover,a.CPAdditionalChildrenNotice:active{color:#949494;font:italic 10pt "Trebuchet MS",Tahoma,Geneva,sans-serif;padding:0 1.5ex}.CPAdditionalChildren{display:none}.TFunction .NDPrototype,.TOperator .NDPrototype{background-color:#F0F0F0;border-color:#C8C8C8}.TFunction .SuEntryIcon,.TOperator .SuEntryIcon{background-color:#EAEAEA;border:1px solid #C7C7C7}.TFunction .SeEntryIcon,.TOperator .SeEntryIcon{background-color:#ECECEC;border:1px solid #C2C2C2}.TProperty .NDPrototype{background-color:#ECECFF;border-color:#C0C0D6}.TProperty .SuEntryIcon{background-color:#DCDCFF;border:1px solid #BEBED4}.TProperty .SeEntryIcon{background-color:#D8DAFF;border:1px solid #BABAD0}.TVariable .NDPrototype,.TDatabaseField .NDPrototype{background-color:#FCFCE6;border-color:#D2D2B8}.TVariable .SuEntryIcon,.TDatabaseField .SuEntryIcon{background-color:#F9F9C5;border:1px solid #D0D09A}.TVariable .SeEntryIcon,.TDatabaseField .SeEntryIcon{background-color:#F9F9C4;border:1px solid #CFCEB4}.TConstant .NDPrototype{background-color:#CACACA;border-color:#979797}.TConstant .SuEntryIcon{background-color:#C6C6C6;border:1px solid #A6A6A6}.TConstant .SeEntryIcon{background-color:#C6C6C6;border:1px solid #969696}.TType .NDPrototype,.TEnumeration .NDPrototype,.TDelegate .NDPrototype{background-color:#FCEFDE;border-color:#D8CBBA}.TType .SuEntryIcon,.TEnumeration .SuEntryIcon,.TDelegate .SuEntryIcon{background-color:#F7E6D3;border:1px solid #D8C7B3}.TType .SeEntryIcon,.TEnumeration .SeEntryIcon,.TDelegate .SeEntryIcon{background-color:#F5E3D1;border:1px solid #D4C3B2}.TEvent .NDPrototype{background-color:#E6F9E6;border-color:#BCCFBC}.TEvent .SuEntryIcon{background-color:#D3F3D3;border:1px solid #B2D1B1}.TEvent .SeEntryIcon{background-color:#D3F3D3;border:1px solid #B2D1B1}.NDClassPrototype .CPEntry{border-color:#686868;background-color:#EAEAEA}.IE6 .NDClassPrototype .CPEntry.Parent,.IE6 .NDClassPrototype .CPEntry.Child,.IE7 .NDClassPrototype .CPEntry.Parent,.IE7 .NDClassPrototype .CPEntry.Child,.IE8 .NDClassPrototype .CPEntry.Parent,.IE8 .NDClassPrototype .CPEntry.Child{border-color:#B4B4B4;background-color:#F5F5F5}.NDClassPrototype .CPEntry.TInterface{border-color:#686879;background-color:#EAEAFF}.IE6 .NDClassPrototype .CPEntry.TInterface.Parent,.IE6 .NDClassPrototype .CPEntry.TInterface.Child,.IE7 .NDClassPrototype .CPEntry.TInterface.Parent,.IE7 .NDClassPrototype .CPEntry.TInterface.Child,.IE8 .NDClassPrototype .CPEntry.TInterface.Parent,.IE8 .NDClassPrototype .CPEntry.TInterface.Child{border-color:#B3B3BB;background-color:#F5F5FF}.SHComment{color:#808080}.SHKeyword{color:#880000}.SHNumber,.SHString{color:#000088}.SHPreprocessingDirective{color:#000000;font-weight:bold}.SHMetadata{color:#808000}.CPPrePrototypeLine .SHMetadata{color:#484800}.NDToolTip{background-color:#FFFFF0;border-style:solid;border-width:1px;border-color:#A0A193 #57574B #57574B #A0A193;padding:1ex;overflow:hidden;-moz-border-radius:1ex;border-radius:1ex;-moz-box-shadow:.15ex .25ex .4ex #606060;-webkit-box-shadow:.15ex .25ex .4ex #606060;box-shadow:.15ex .25ex .4ex #606060}.NDToolTip .NDPrototype,.NDToolTip .NDClassPrototype{margin:0;overflow:hidden}.NDToolTip .NDClassPrototype .CPEntry{margin-left:0 !important;margin-right:0 !important}.TTSummary{font:10pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif;max-width:75ex}.NDPrototype+.TTSummary{margin-top:.5em}#NDFooter{background-color:#E0E0E0;font:7pt "Trebuchet MS",Tahoma,Geneva,sans-serif;padding:.1em 1ex;text-align:right;z-index:1}#NDFooter,#NDFooter a{color:#808080}#FCopyright,#FTimestamp{display:inline}#FCopyright:after,#FTimestamp:after{white-space:pre;content:" · "}.IE6 #FCopyright,.IE7 #FCopyright,.IE6 #FTimestamp,.IE7 #FTimestamp{margin-right:3ex}#FGeneratedBy{display:inline}body.NDHomePage{padding:0;margin:0;background-color:#E0E0E0;background-image:-moz-linear-gradient(top,#7070C0,#E0E0E0);background-image:-webkit-gradient(linear,left top,left bottom,from(#7070C0),to(#E0E0E0));background-image:-o-linear-gradient(top,#7070C0,#E0E0E0);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#7070C0',endColorstr='#E0E0E0',GradientType=0 );background-image:-ms-linear-gradient(top,#7070C0 0%,#E0E0E0 100%);background-image:linear-gradient(top,#7070C0,#E0E0E0);background-repeat:no-repeat;background-attachment:fixed;height:100%}.HTitle,.HSubtitle,.HCopyright,.HTimestamp,.HGeneratedBy,.NDHomePage p{text-align:center}.HFrame{padding:4em 5em .5em 5em}.HContent{max-width:28em;margin:0 auto 3em auto;padding:1.5em 2em;overflow:hidden;background-color:#FFFFFF;border-width:.5ex;border-style:solid;border-color:#E0E0E0 #A0A0A0 #202020 #C0C0C0;-moz-box-shadow:0 .25ex .75ex #383838;-webkit-box-shadow:0 .25ex .75ex #383838;box-shadow:0 .25ex .75ex #383838}.HTitle{font:34pt Georgia,serif;line-height:95%}.HSubtitle{font:16pt Georgia,serif;padding-top:.075em}.HFooter{margin:0 auto;margin-top:2em;border-top:1px solid #A0A0A0;padding-top:2em;font:8pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif}.HFooter div{margin-bottom:.5em;color:#A0A0A0}.HFooter a:link,.HFooter a:visited,.HFooter a:hover,.HFooter a:active{color:#A0A0A0}#NDMessages{background-color:#FFFFF0;z-index:9999;overflow:auto;max-height:33%;display:none;padding:.8ex 1ex 1ex 1ex;border-bottom:3px solid #A0A000;border-left:1px solid #A0A000}#MsgCloseButton{float:right;font:bold 8pt Verdana,sans-serif;color:#000000}a#MsgCloseButton:hover,a#MsgCloseButton:active{text-decoration:none}.MsgMessage{color:#787800;font:italic 8pt Verdana,sans-serif}.MsgMessage ~ .MsgMessage{padding-top:.9ex;border-top:1px dashed #D0D0C8;margin-top:1.2ex}.IE6 .MTab{border-bottom:0px none}.IE6 #MContent .Selected{background-color:#FFFFFF}.IE6 .NDPrototype .PModifierQualifier,.IE6 .NDPrototype .PType{font:10pt Consolas,"Courier New",monospace;padding:0;border-bottom:0px none}.IE6 .CTopic p,.IE6 .CTopic .CTitle,.IE6 .NDClassPrototype .CPName{color:#000000}.IE6 .NDClassPrototype .CPEntry{margin-top:0}.IE6 .SeEntry{border-bottom:0px none;padding:.2ex 1.25ex .2ex 3.25ex;margin:0;font:10pt Tahoma,Geneva,"Trebuchet MS",Verdana,sans-serif}.IE6 .SeChildren .SeEntry{padding-left:5.5ex}.IE6 #SeSelectedEntry{border-style:solid;border-width:1px}
\ No newline at end of file
diff --git a/doc/styles/Default/images/menu-folder-arrow.png b/doc/styles/Default/images/menu-folder-arrow.png
new file mode 100644
index 0000000..7aec69c
Binary files /dev/null and b/doc/styles/Default/images/menu-folder-arrow.png differ
diff --git a/doc/styles/Default/images/menu-loading.gif b/doc/styles/Default/images/menu-loading.gif
new file mode 100644
index 0000000..d8d0608
Binary files /dev/null and b/doc/styles/Default/images/menu-loading.gif differ
diff --git a/doc/styles/Default/images/menu-tab-classes.png b/doc/styles/Default/images/menu-tab-classes.png
new file mode 100644
index 0000000..d126b4a
Binary files /dev/null and b/doc/styles/Default/images/menu-tab-classes.png differ
diff --git a/doc/styles/Default/images/menu-tab-database.png b/doc/styles/Default/images/menu-tab-database.png
new file mode 100644
index 0000000..01f6108
Binary files /dev/null and b/doc/styles/Default/images/menu-tab-database.png differ
diff --git a/doc/styles/Default/images/menu-tab-files.png b/doc/styles/Default/images/menu-tab-files.png
new file mode 100644
index 0000000..ebe763d
Binary files /dev/null and b/doc/styles/Default/images/menu-tab-files.png differ
diff --git a/doc/styles/Default/images/search-class.png b/doc/styles/Default/images/search-class.png
new file mode 100644
index 0000000..1494838
Binary files /dev/null and b/doc/styles/Default/images/search-class.png differ
diff --git a/doc/styles/Default/images/search-parent.png b/doc/styles/Default/images/search-parent.png
new file mode 100644
index 0000000..54d1a8d
Binary files /dev/null and b/doc/styles/Default/images/search-parent.png differ
diff --git a/doc/styles/Default/images/search.png b/doc/styles/Default/images/search.png
new file mode 100644
index 0000000..6edae9f
Binary files /dev/null and b/doc/styles/Default/images/search.png differ
diff --git a/doc/styles/Default/images/summary-loading.gif b/doc/styles/Default/images/summary-loading.gif
new file mode 100644
index 0000000..2f6f3fe
Binary files /dev/null and b/doc/styles/Default/images/summary-loading.gif differ
diff --git a/doc/styles/Default/images/transparent.gif b/doc/styles/Default/images/transparent.gif
new file mode 100644
index 0000000..f191b28
Binary files /dev/null and b/doc/styles/Default/images/transparent.gif differ
diff --git a/doc/styles/DefaultJS/NDContentPage.js b/doc/styles/DefaultJS/NDContentPage.js
new file mode 100644
index 0000000..14ecb1f
--- /dev/null
+++ b/doc/styles/DefaultJS/NDContentPage.js
@@ -0,0 +1,12 @@
+/*
+This file is part of Natural Docs, which is Copyright © 2003-2018 Code Clear LLC.
+Natural Docs is licensed under version 3 of the GNU Affero General Public
+License (AGPL). Refer to License.txt or www.naturaldocs.org for the
+complete details.
+
+This file may be distributed with documentation files generated by Natural Docs.
+Such documentation is not covered by Natural Docs' copyright and licensing,
+and may have its own copyright and distribution terms as decided by its author.
+*/
+
+"use strict";var NDContentPage=new function(){this.Start=function(){var ieVersion=NDCore.IEVersion();if(ieVersion==undefined||ieVersion>=8){this.CalculateWideFormPrototypeWidths();for(var key in this.wideFormPrototypeWidths){if(this.wideFormPrototypeWidths[key]==0){setTimeout("NDContentPage.Start();",200);return;}}this.ReformatPrototypes();window.onresize=function(){NDContentPage.OnResize();}}this.toolTipHolder=document.createElement("div");this.toolTipHolder.style.display="none";this.toolTipHolder.style.position="fixed";if(ieVersion==6){this.toolTipHolder.style.position="absolute";}this.toolTipHolder.style.zIndex=20;document.body.appendChild(this.toolTipHolder);var ttLocation=location.href;var hashIndex=ttLocation.indexOf('#');if(hashIndex!=-1){ttLocation=ttLocation.substr(0,hashIndex);}ttLocation=ttLocation.substr(0,ttLocation.length-5)+"-ToolTips.js";NDCore.LoadJavaScript(ttLocation);};this.OnResize=function(){if(this.reformatPrototypesTimeout==undefined){this.reformatPrototypesTimeout=setTimeout("NDContentPage.ReformatPrototypes()",200);}};this.GetPrototypeIDNumber=function(element){if(element.id.indexOf("NDPrototype")==0){var id=parseInt(element.id.substr(11),10);if(id!=NaN&&id>0){return id;}}return-1;};this.CalculateWideFormPrototypeWidths=function(){var prototypes=NDCore.GetElementsByClassName(document,"NDPrototype","div");for(var i=0;i=wideFormWidth&&NDCore.HasClass(prototypes[i],"NarrowForm")){NDCore.ChangePrototypeToWideForm(prototypes[i]);}else if(availableWidthdocument.body.offsetWidth){x=document.body.offsetWidth-this.toolTipHolder.offsetWidth-2;if(x<2){x=2;newWidth=document.body.offsetWidth-4;}}NDCore.SetToAbsolutePosition(this.toolTipHolder,x,y,newWidth,undefined);var prototypes=NDCore.GetElementsByClassName(this.toolTipHolder,"NDPrototype","div");if(prototypes.length>0&&NDCore.HasClass(prototypes[0],"WideForm")&&prototypes[0].scrollWidth>prototypes[0].offsetWidth){NDCore.ChangePrototypeToNarrowForm(prototypes[0]);}if(y+this.toolTipHolder.offsetHeight+2>document.body.parentNode.offsetHeight){var newY=linkOffsets.offsetTop-this.toolTipHolder.offsetHeight-scrollParent.scrollTop-5;if(newY>=0){NDCore.SetToAbsolutePosition(this.toolTipHolder,undefined,newY,undefined,undefined);}}this.toolTipHolder.style.visibility="visible";};this.ResetToolTip=function(){if(this.showingToolTip!=undefined){this.toolTipHolder.style.display="none";this.toolTipHolder.style.width=null;this.lastToolTip=this.showingToolTip;this.showingToolTip=undefined;}if(this.toolTipTimeout!=undefined){clearTimeout(this.toolTipTimeout);this.toolTipTimeout=undefined;}};this.wideFormPrototypeWidths={};};
\ No newline at end of file
diff --git a/doc/styles/DefaultJS/NDCore.js b/doc/styles/DefaultJS/NDCore.js
new file mode 100644
index 0000000..a928e52
--- /dev/null
+++ b/doc/styles/DefaultJS/NDCore.js
@@ -0,0 +1,12 @@
+/*
+This file is part of Natural Docs, which is Copyright © 2003-2018 Code Clear LLC.
+Natural Docs is licensed under version 3 of the GNU Affero General Public
+License (AGPL). Refer to License.txt or www.naturaldocs.org for the
+complete details.
+
+This file may be distributed with documentation files generated by Natural Docs.
+Such documentation is not covered by Natural Docs' copyright and licensing,
+and may have its own copyright and distribution terms as decided by its author.
+*/
+
+"use strict";var NDCore=new function(){this.GetElementsByClassName=function(baseElement,className,tagHint){if(baseElement.getElementsByClassName){return baseElement.getElementsByClassName(className);}if(!tagHint){tagHint="*";}var tagArray=baseElement.getElementsByTagName(tagHint);var matchArray=new Array();var tagIndex=0;var matchIndex=0;while(tagIndex0){newClassName+=element.className.substr(0,index);}if(index+targetClassName.length!=element.className.length){newClassName+=element.className.substr(index+targetClassName.length);}element.className=newClassName;return;}index=element.className.indexOf(targetClassName,index+1);}};this.LoadJavaScript=function(path,id){var script=document.createElement("script");script.src=path;script.type="text/javascript";if(id!=undefined){script.id=id;}document.getElementsByTagName("head")[0].appendChild(script);};this.RemoveScriptElement=function(id){var script=document.getElementById(id);if(this.IEVersion()==6){setTimeout(function(){script.parentNode.removeChild(script);},1);}else{script.parentNode.removeChild(script);}};this.WindowClientWidth=function(){var width=window.innerWidth;if(width===undefined){width=document.documentElement.clientWidth;}return width;};this.WindowClientHeight=function(){var height=window.innerHeight;if(height===undefined){height=document.documentElement.clientHeight;}return height;};this.SetToAbsolutePosition=function(element,x,y,width,height){if(x!=undefined&&element.offsetLeft!=x){element.style.left=x+"px";}if(y!=undefined&&element.offsetTop!=y){element.style.top=y+"px";}if(width!=undefined&&element.offsetWidth!=width){if(!this.pxRegex.test(element.style.width)){element.style.width=width+"px";if(element.offsetWidth!=width){var adjustment=width-element.offsetWidth;element.style.width=(width+adjustment)+"px";}}else{var styleWidth=RegExp.$1;var adjustment=styleWidth-element.offsetWidth;element.style.width=(width+adjustment)+"px";}}if(height!=undefined&&element.offsetHeight!=height){if(!this.pxRegex.test(element.style.height)){element.style.height=height+"px";if(element.offsetHeight!=height){var adjustment=height-element.offsetHeight;element.style.height=(height+adjustment)+"px";}}else{var styleHeight=RegExp.$1;var adjustment=styleHeight-element.offsetHeight;element.style.height=(height+adjustment)+"px";}}};this.GetFullOffsets=function(element){var result={offsetTop:element.offsetTop,offsetLeft:element.offsetLeft};element=element.offsetParent;while(element!=undefined&&element.nodeName!="BODY"){result.offsetTop+=element.offsetTop;result.offsetLeft+=element.offsetLeft;element=element.offsetParent;}return result;};this.NormalizeHash=function(hashString){if(hashString==undefined){return"";}if(hashString.charAt(0)=="#"){hashString=hashString.substr(1);}hashString=decodeURI(hashString);return hashString;};this.IsIE=function(){return(navigator.userAgent.indexOf("MSIE")!=-1||navigator.userAgent.indexOf("Trident")!=-1);};this.IEVersion=function(){var ieIndex=navigator.userAgent.indexOf("MSIE");if(ieIndex!=-1){ieIndex+=5;}else{ieIndex=navigator.userAgent.indexOf("Trident");if(ieIndex!=-1){ieIndex=navigator.userAgent.indexOf("rv:");if(ieIndex!=-1){ieIndex+=3;}}}if(ieIndex!=-1){return parseInt(navigator.userAgent.substr(ieIndex));}else{return undefined;}};this.AddIEClassesToBody=function(){var ieVersion=this.IEVersion();if(ieVersion!=undefined){this.AddClass(document.body,"IE");if(ieVersion>=6&&ieVersion<=8){this.AddClass(document.body,"IE"+ieVersion);}}};this.SupportsOnInput=function(){if(this.IEVersion()==9){return false;}else{return(window.oninput!==undefined);}};this.ChangePrototypeToNarrowForm=function(prototype){var newPrototype=document.createElement("div");newPrototype.id=prototype.id;newPrototype.className=prototype.className;this.RemoveClass(newPrototype,"WideForm");this.AddClass(newPrototype,"NarrowForm");var prePrototypeLines=NDCore.GetElementsByClassName(prototype,"PPrePrototypeLine","div");for(var i=0;i=other.length&&this.substr(0,other.length)==other);};String.prototype.EntityDecode=function(){var output=this;output=output.replace(/</g,"<");output=output.replace(/>/g,">");output=output.replace(/"/g,"\"");output=output.replace(/&/g,"&");return output;};function NDLocation(hashString){this.Constructor=function(hashString){this.hashString=NDCore.NormalizeHash(hashString);if(this.hashString.match(/^File[0-9]*:/)!=null){this.type="File";this.SplitPathAndMember();this.AddFileURLs();}else if(this.hashString.match(/^[A-Z]+Class:/i)!=null){this.type="Class";this.SplitPathAndMember();this.AddClassURLs();}else if(this.hashString.substr(0,9).toLowerCase()=="database:"){this.type="Database";this.SplitPathAndMember();this.AddDatabaseURLs();}else{this.type="Home";this.AddHomeURLs();}};this.SplitPathAndMember=function(){var pathSeparator=this.hashString.indexOf(':');var memberSeparator=this.hashString.indexOf(':',pathSeparator+1);if(memberSeparator==-1){this.path=this.hashString;}else{this.path=this.hashString.substr(0,memberSeparator);this.member=this.hashString.substr(memberSeparator+1);if(this.member==""){this.member=undefined;}}};this.AddHomeURLs=function(){this.contentPage="other/home.html";};this.AddFileURLs=function(){var pathPrefix=this.path.match(/^File([0-9]*):/);var basePath="files"+pathPrefix[1]+"/"+this.path.substr(pathPrefix[0].length);var lastSeparator=basePath.lastIndexOf('/');var filename=basePath.substr(lastSeparator+1);filename=filename.replace(/\./g,'-');basePath=basePath.substr(0,lastSeparator+1)+filename;this.contentPage=basePath+".html";this.summaryFile=basePath+"-Summary.js";this.summaryTTFile=basePath+"-SummaryToolTips.js";if(this.member!=undefined){this.contentPage+='#'+this.member;}};this.AddClassURLs=function(){var pathPrefix=this.path.match(/^([A-Z]+)Class:/i);var basePath="classes/"+pathPrefix[1]+"/"+this.path.substr(pathPrefix[0].length);basePath=basePath.replace(/\.|::/g,"/");this.contentPage=basePath+".html";this.summaryFile=basePath+"-Summary.js";this.summaryTTFile=basePath+"-SummaryToolTips.js";if(this.member!=undefined){this.contentPage+='#'+this.member;}};this.AddDatabaseURLs=function(){var basePath="database/"+this.path.substr(9);basePath=basePath.replace(/\./g,"/");this.contentPage=basePath+".html";this.summaryFile=basePath+"-Summary.js";this.summaryTTFile=basePath+"-SummaryToolTips.js";if(this.member!=undefined){this.contentPage+='#'+this.member;}};this.Constructor(hashString);};if(!Array.prototype.indexOf){Array.prototype.indexOf=function(searchElement){"use strict";if(this==null){throw new TypeError();}var n,k,t=Object(this),len=t.length>>>0;if(len===0){return-1;}n=0;if(arguments.length>1){n=Number(arguments[1]);if(n!=n){n=0;}else if(n!=0&&n!=Infinity&&n!=-Infinity){n=(n>0||-1)*Math.floor(Math.abs(n));}}if(n>=len){return-1;}for(k=n>=0?n:Math.max(len-Math.abs(n),0);k7));if(supportsOnHashChange){window.onhashchange=function(){NDFramePage.OnHashChange();};}if(!supportsOnHashChange||NDCore.IsIE()){this.hashChangePoller={timeoutLength:200,lastHash:location.hash};if(!NDCore.IsIE()||supportsOnHashChange){this.hashChangePoller.Start=function(){this.Poll();};this.hashChangePoller.Stop=function(){if(this.timeoutID!=undefined){clearTimeout(this.timeoutID);this.timeoutID=undefined;}};this.hashChangePoller.Poll=function(){if(location.hash!=this.lastHash){this.lastHash=location.hash;NDFramePage.OnHashChange();}this.timeoutID=setTimeout("NDFramePage.hashChangePoller.Poll()",this.timeoutLength);};}else{this.hashChangePoller.Start=function(){var iframeElement=document.createElement("iframe");iframeElement.title="empty";iframeElement.tabindex=-1;iframeElement.style.display="none";iframeElement.width=0;iframeElement.height=0;iframeElement.src="javascript:0";this.firstRun=true;iframeElement.attachEvent("onload",function(){if(NDFramePage.hashChangePoller.firstRun){NDFramePage.hashChangePoller.firstRun=false;NDFramePage.hashChangePoller.SetHistory(location.hash);NDFramePage.hashChangePoller.Poll();}});document.body.appendChild(iframeElement);this.iframe=iframeElement.contentWindow;document.onpropertychange=function(){if(event.propertyName=="title"){try{NDFramePage.hashChangePoller.iframe.document.title=document.title;}catch(e){}}};};this.hashChangePoller.Stop=function(){};this.hashChangePoller.Poll=function(){var hash=location.hash;var historyHash=this.GetHistory();if(hash!=this.lastHash){this.lastHash=location.hash;this.SetHistory(hash,historyHash);NDFramePage.OnHashChange();}else if(historyHash!=this.lastHash){location.href=location.href.replace(/#.*/,'')+historyHash;}this.timeoutID=setTimeout("NDFramePage.hashChangePoller.Poll()",this.timeoutLength);};this.hashChangePoller.GetHistory=function(){return this.iframe.location.hash;};this.hashChangePoller.SetHistory=function(hash,historyHash){if(hash!=historyHash){this.iframe.document.title=document.title;this.iframe.document.open();this.iframe.document.close();this.iframe.location.hash=hash;}};}this.hashChangePoller.Start();}};this.OnResize=function(){this.UpdateLayout();};this.UpdateLayout=function(){var ieVersion=NDCore.IEVersion();var useSizers=(ieVersion==undefined||ieVersion>=8);var fullWidth=NDCore.WindowClientWidth();var fullHeight=NDCore.WindowClientHeight();var header=document.getElementById("NDHeader");var searchField=document.getElementById("NDSearchField");var footer=document.getElementById("NDFooter");var menu=document.getElementById("NDMenu");var menuSizer=document.getElementById("NDMenuSizer");var summary=document.getElementById("NDSummary");var summarySizer=document.getElementById("NDSummarySizer");var content=document.getElementById("NDContent");var messages=document.getElementById("NDMessages");NDCore.SetToAbsolutePosition(header,0,0,fullWidth,undefined);NDCore.SetToAbsolutePosition(footer,0,undefined,fullWidth,undefined);var headerHeight=header.offsetHeight-1;var footerHeight=footer.offsetHeight;NDCore.SetToAbsolutePosition(footer,undefined,fullHeight-footerHeight,undefined,undefined);var searchMargin=(headerHeight-searchField.offsetHeight)/2;NDCore.SetToAbsolutePosition(searchField,fullWidth-searchField.offsetWidth-searchMargin,searchMargin,undefined,undefined);var remainingHeight=fullHeight-headerHeight-footerHeight;var remainingWidth=fullWidth;var currentX=0;if(this.MenuIsVisible()){menu.style.display="block";NDCore.SetToAbsolutePosition(menu,currentX,headerHeight,undefined,remainingHeight);currentX+=menu.offsetWidth;remainingWidth-=menu.offsetWidth;if(this.desiredMenuWidth==undefined){this.desiredMenuWidth=menu.offsetWidth;}if(useSizers){menuSizer.style.display="block";NDCore.SetToAbsolutePosition(menuSizer,currentX,headerHeight,undefined,remainingHeight);}NDMenu.OnUpdateLayout();}else{menu.style.display="none";menuSizer.style.display="none";}if(this.SummaryIsVisible()){summary.style.display="block";NDCore.SetToAbsolutePosition(summary,currentX,headerHeight,undefined,remainingHeight);currentX+=summary.offsetWidth;remainingWidth-=summary.offsetWidth;if(this.desiredSummaryWidth==undefined){this.desiredSummaryWidth=summary.offsetWidth;}if(useSizers){summarySizer.style.display="block";NDCore.SetToAbsolutePosition(summarySizer,currentX,headerHeight,undefined,remainingHeight);}}else{summary.style.display="none";summarySizer.style.display="none";}NDCore.SetToAbsolutePosition(content,currentX,headerHeight,remainingWidth,remainingHeight);NDCore.SetToAbsolutePosition(messages,currentX,0,remainingWidth,undefined);NDSearch.OnUpdateLayout();};this.MenuIsVisible=function(){return true;};this.SummaryIsVisible=function(){return(this.currentLocation!=undefined&&this.currentLocation.summaryFile!=undefined);};this.OnMouseDown=function(event){if(event==undefined){event=window.event;}var target=event.target||event.srcElement;if(NDSearch.SearchFieldIsActive()){var targetIsInResults=false;for(var element=target;element!=undefined;element=element.parentNode){if(element.id=="NDSearchResults"){targetIsInResults=true;break;}}if(!targetIsInResults){NDSearch.ClearResults();NDSearch.DeactivateSearchField();}}if(target.id=="NDMenuSizer"||target.id=="NDSummarySizer"){var element;if(target.id=="NDMenuSizer"){element=document.getElementById("NDMenu");}else{element=document.getElementById("NDSummary");}this.sizerDragging={"sizer":target,"element":element,"originalSizerX":target.offsetLeft,"originalElementWidth":element.offsetWidth,"originalClientX":event.clientX};NDCore.AddClass(target,"Dragging");document.onmousemove=function(e){return NDFramePage.OnSizerMouseMove(e);};document.onmouseup=function(e){return NDFramePage.OnSizerMouseUp(e);};document.onselectstart=function(){return false;};var contentCover=document.createElement("div");contentCover.id="NDContentCover";document.body.appendChild(contentCover);NDCore.SetToAbsolutePosition(contentCover,0,0,NDCore.WindowClientWidth(),NDCore.WindowClientHeight());return false;}else{return true;}};this.OnSizerMouseMove=function(event){if(event==undefined){event=window.event;}var offset=event.clientX-this.sizerDragging.originalClientX;var windowClientWidth=NDCore.WindowClientWidth();if(this.sizerDragging.sizer.id=="NDMenuSizer"){if(this.sizerDragging.originalSizerX+offset<0){offset=0-this.sizerDragging.originalSizerX;}else if(this.sizerDragging.originalSizerX+offset+this.sizerDragging.sizer.offsetWidth>windowClientWidth){offset=windowClientWidth-this.sizerDragging.sizer.offsetWidth-this.sizerDragging.originalSizerX;}}else{var menuSizer=document.getElementById("NDMenuSizer");var leftLimit=menuSizer.offsetLeft+menuSizer.offsetWidth;if(this.sizerDragging.originalSizerX+offsetwindowClientWidth){offset=windowClientWidth-this.sizerDragging.sizer.offsetWidth-this.sizerDragging.originalSizerX;}}NDCore.SetToAbsolutePosition(this.sizerDragging.sizer,this.sizerDragging.originalSizerX+offset,undefined,undefined,undefined);NDCore.SetToAbsolutePosition(this.sizerDragging.element,undefined,undefined,this.sizerDragging.originalElementWidth+offset,undefined);if(this.sizerDragging.sizer.id=="NDMenuSizer"){this.desiredMenuWidth=document.getElementById("NDMenu").offsetWidth;}else{this.desiredSummaryWidth=document.getElementById("NDSummary").offsetWidth;}this.UpdateLayout();};this.OnSizerMouseUp=function(event){document.onmousemove=null;document.onmouseup=null;document.onselectstart=null;document.body.removeChild(document.getElementById("NDContentCover"));NDCore.RemoveClass(this.sizerDragging.sizer,"Dragging");this.sizerDragging=undefined;};this.SizeSummaryToContent=function(){this.SizePanelToContent(document.getElementById("NDSummary"),this.desiredSummaryWidth);};this.SizePanelToContent=function(panel,desiredOffsetWidth){if(this.desiredSummaryWidth==undefined){return;}var resized=false;if(panel.clientWidth==panel.scrollWidth){if(panel.offsetWidth==desiredOffsetWidth){return;}else{NDCore.SetToAbsolutePosition(panel,undefined,undefined,desiredOffsetWidth,undefined);resized=true;}}var newOffsetWidth=panel.scrollWidth;if(panel.scrollHeight>panel.clientHeight){newOffsetWidth+=panel.offsetWidth-panel.clientWidth;}else{newOffsetWidth+=NDCore.GetComputedPixelWidth(panel,"borderLeftWidth")+NDCore.GetComputedPixelWidth(panel,"borderRightWidth");}if(newOffsetWidth!=desiredOffsetWidth){newOffsetWidth+=3;if(newOffsetWidth/desiredOffsetWidth>1.333){newOffsetWidth=Math.floor(desiredOffsetWidth*1.333);}if(panel.offsetWidth!=newOffsetWidth){NDCore.SetToAbsolutePosition(panel,undefined,undefined,newOffsetWidth,undefined);resized=true;}}if(resized){this.UpdateLayout();}};};
\ No newline at end of file
diff --git a/doc/styles/DefaultJS/NDMenu.js b/doc/styles/DefaultJS/NDMenu.js
new file mode 100644
index 0000000..383bfee
--- /dev/null
+++ b/doc/styles/DefaultJS/NDMenu.js
@@ -0,0 +1,12 @@
+/*
+This file is part of Natural Docs, which is Copyright © 2003-2018 Code Clear LLC.
+Natural Docs is licensed under version 3 of the GNU Affero General Public
+License (AGPL). Refer to License.txt or www.naturaldocs.org for the
+complete details.
+
+This file may be distributed with documentation files generated by Natural Docs.
+Such documentation is not covered by Natural Docs' copyright and licensing,
+and may have its own copyright and distribution terms as decided by its author.
+*/
+
+"use strict";var NDMenu=new function(){this.Start=function(){this.menuSections=[];this.firstUnusedMenuSection=0;var menuContainer=document.getElementById("NDMenu");var menuTabBar=document.createElement("div");menuTabBar.id="MTabBar";menuContainer.appendChild(menuTabBar);var menuContent=document.createElement("div");menuContent.id="MContent";menuContainer.appendChild(menuContent);NDCore.LoadJavaScript("menu/tabs.js","NDMenuTabsLoader");};this.OnLocationChange=function(oldLocation,newLocation){if(newLocation.type=="Home"&&this.tabs!=undefined&&this.tabs.length==1){this.GoToOffsets([0]);return;}if(oldLocation==undefined||oldLocation.type!=newLocation.type){this.UpdateTabs(newLocation.type);}if(oldLocation==undefined||oldLocation.type!=newLocation.type||oldLocation.path!=newLocation.path){this.Build(new NDMenuHashPath(newLocation.type,newLocation.path));}};this.GoToOffsets=function(offsets){if(this.tabs!=undefined){var newSelectedTab;if(offsets.length>=1){newSelectedTab=this.tabs[offsets[0]][0];if(newSelectedTab!=this.selectedTabType){this.UpdateTabs(newSelectedTab);}}}this.Build(new NDMenuOffsetPath(offsets));};this.Build=function(path){if(path!=undefined){this.pathBeingBuilt=path;}else if(this.pathBeingBuilt==undefined){return;}this.firstUnusedMenuSection=0;var newMenuContent=document.createElement("div");newMenuContent.id="MContent";var result;if(this.tabs!=undefined){result=this.BuildEntries(newMenuContent,this.pathBeingBuilt);}else{result={completed:false};}if(!result.completed){var htmlEntry=document.createElement("div");htmlEntry.className="MLoadingNotice";newMenuContent.appendChild(htmlEntry);}var oldMenuContent=document.getElementById("MContent");var menuContainer=oldMenuContent.parentNode;menuContainer.replaceChild(newMenuContent,oldMenuContent);if(NDCore.IsIE()&&NDCore.IEVersion()==10){setTimeout(function(){document.getElementById("NDMenu").style.zoom="100%";},0);}if(result.completed){if(result.selectedFile){if(result.selectedFile.offsetTop+result.selectedFile.offsetHeight>menuContainer.scrollTop+menuContainer.clientHeight){result.selectedFile.scrollIntoView(false);}else if(result.selectedFile.offsetTop"+" "+""+tabTitle+" ";htmlMenu.appendChild(htmlEntry);}}else{for(var i=0;i";if(typeof(member[1])=="object"){title+=member[1][0];}else{title+=member[1];}var targetPath=(pathSoFar.length==0?i:pathSoFar.join(",")+","+i);var htmlEntry=document.createElement("a");htmlEntry.className="MEntry MFolder Child";htmlEntry.setAttribute("href","javascript:NDMenu.GoToOffsets(["+targetPath+"])");htmlEntry.innerHTML=title;htmlMenu.appendChild(htmlEntry);}}}result.completed=true;if(selectedTab!=undefined){selectedTab[6]=pathSoFar;}return result;};this.MenuSection=function(file){for(var i=0;i=this.firstUnusedMenuSection){if(i>this.firstUnusedMenuSection){this.menuSections.splice(i,1);this.menuSections.splice(this.firstUnusedMenuSection,0,section);}this.firstUnusedMenuSection++;}if(section.ready==true){return section.contents;}else{return undefined;}}}return undefined;};this.LoadMenuSection=function(file){for(var i=0;i10){for(var i=this.menuSections.length-1;i>=this.firstUnusedMenuSection&&this.menuSections.length>10;i--){if(this.menuSections[i].ready==false){break;}this.menuSections.pop();}}};this.OnTabsLoaded=function(tabs){this.tabs=tabs;NDCore.RemoveScriptElement("NDMenuTabsLoader");var tabBar=document.getElementById("MTabBar");for(var i=0;i"+tabTitle+" ";tab.style.position="absolute";tab.style.visibility="hidden";tabBar.appendChild(tab);tabs[i][4]=tab.offsetWidth;tab.className="MTab Narrow";tabs[i][5]=tab.offsetWidth;if(tabs[i][0]==this.selectedTabType){tab.className+=" Selected";}}this.ResizeTabs();if(this.ShouldTabsShow()==false){tabBar.style.display="none";}for(var i=0;i1&&this.selectedTabType!=undefined&&this.selectedTabType!="Home");};this.OnUpdateLayout=function(){this.ResizeTabs();};this.GoToTab=function(newTabType){var tabIndex;for(var i=0;i=this.pathObject.path.length){return;}else if(this.currentContainer==undefined){this.pathIndex++;}else{var currentEntry=this.currentContainer[this.pathObject.path[this.pathIndex]];this.currentContainer=undefined;this.currentContainerHashPath=undefined;this.needToLoad=undefined;if(this.pathIndex==0){this.currentContainerHashPath=currentEntry[2];this.needToLoad=currentEntry[3];}else if(currentEntry[0]==2){this.currentContainerHashPath=currentEntry[2];if(typeof currentEntry[3]=="string"){this.needToLoad=currentEntry[3];}else{this.currentContainer=currentEntry[3];}}this.pathIndex++;if(this.needToLoad!=undefined){this.currentContainer=NDMenu.MenuSection(this.needToLoad);if(this.currentContainer!=undefined){this.needToLoad=undefined;}}}};this.NavigationType=function(){if(this.currentContainer==undefined){return 9;}if(this.pathIndex>=this.pathObject.path.length){return-1;}var currentEntry=this.currentContainer[this.pathObject.path[this.pathIndex]];if(this.InTabs()==false&¤tEntry[0]==1){return 3;}if(this.pathIndex==this.pathObject.path.length-1){return 2;}if(this.pathIndex+2<=this.pathObject.path.length-1){return 1;}var lookahead=this.Duplicate();lookahead.Next();if(lookahead.NavigationType()==9){this.needToLoad=lookahead.NeedToLoad();return 9;}else if(lookahead.CurrentEntry()[0]==2){return 1;}else{return 2;}};this.Duplicate=function(){var newObject=new NDMenuOffsetPathIterator(this.pathObject);newObject.pathIndex=this.pathIndex;newObject.currentContainer=this.currentContainer;newObject.needToLoad=this.needToLoad;return newObject;};this.CurrentEntry=function(){if(this.currentContainer!=undefined&&this.pathIndex0){this.domResults.scrollTop--;}};this.LoadMoreResults=function(){this.moreResultsThreshold=this.visibleEntryCount+25;this.domSearchField.focus();this.Update();};this.ActivateLinkFromKeyboard=function(domLink){var address=domLink.getAttribute("href");if(address.substr(0,11)=="javascript:"){address=address.substr(11);address=address.replace(/^(NDSearch.ToggleParent\([0-9]+,)false(.*)$/,"$1true$2");eval(address);}else{location.href=address;}};this.OnSearchFieldFocus=function(){if(!this.SearchFieldIsActive()){this.ActivateSearchField();if(this.allPrefixesStatus==1){this.allPrefixesStatus=2;NDCore.LoadJavaScript("search/index.js");}}};this.OnSearchFieldKey=function(event){if(event===undefined){event=window.event;}if(event.keyCode==27){this.ClearResults();this.DeactivateSearchField();document.getElementById("CFrame").contentWindow.focus();}else if(event.keyCode==38){if(this.keyboardSelectionIndex<=0){this.keyboardSelectionIndex=this.visibleEntryCount-1;}else{this.keyboardSelectionIndex--;}this.UpdateSelection();}else if(event.keyCode==40){if(this.visibleEntryCount==0){this.keyboardSelectionIndex=-1;}else if(this.keyboardSelectionIndex>=this.visibleEntryCount-1){this.keyboardSelectionIndex=0;}else{this.keyboardSelectionIndex++;}this.UpdateSelection();}else if(event.keyCode==37){if(this.keyboardSelectionIndex!=-1){var domSelectedEntry=document.getElementById("SeSelectedEntry");if(NDCore.HasClass(domSelectedEntry,"SeParent")&&NDCore.HasClass(domSelectedEntry,"open")){this.ActivateLinkFromKeyboard(domSelectedEntry);}}}else if(event.keyCode==39){if(this.keyboardSelectionIndex!=-1){var domSelectedEntry=document.getElementById("SeSelectedEntry");if(NDCore.HasClass(domSelectedEntry,"SeParent")&&NDCore.HasClass(domSelectedEntry,"closed")){this.ActivateLinkFromKeyboard(domSelectedEntry);}}}else if(event.keyCode==13){var domSelectedEntry=undefined;if(this.keyboardSelectionIndex!=-1){domSelectedEntry=document.getElementById("SeSelectedEntry");}else if(this.visibleEntryCount==1){domSelectedEntry=this.domResultsContent.firstChild;}else if(this.visibleEntryCount==2&&NDCore.HasClass(this.domResultsContent.firstChild,"SeParent")){domSelectedEntry=this.domResultsContent.childNodes[1].firstChild;}if(domSelectedEntry!=undefined){this.ActivateLinkFromKeyboard(domSelectedEntry);}else if(this.keyboardSelectionIndex==-1&&this.visibleEntryCount>0){this.keyboardSelectionIndex=0;this.UpdateSelection();}}else if(NDCore.SupportsOnInput()==false){if(event.keyCode!=37&&event.keyCode!=39){this.OnSearchFieldChange(event);}}};this.OnSearchFieldChange=function(event){if(event===undefined){event=window.event;}this.keyboardSelectionIndex=-1;if(this.initialTimeoutStatus==3){if(this.updateTimeout==undefined){this.updateTimeout=setTimeout(function(){clearTimeout(NDSearch.updateTimeout);NDSearch.updateTimeout=undefined;NDSearch.Update();},350);}}else{var searchInterpretations=this.GetSearchInterpretations();if(searchInterpretations.length!=0&&this.allPrefixesStatus==3&&this.GetMatchingPrefixes(searchInterpretations).length<=1){if(this.initialTimeoutStatus==2){clearTimeout(this.initialTimeout);this.initialTimeout=undefined;}this.initialTimeoutStatus=3;this.Update();}else if(this.initialTimeoutStatus==1){this.initialTimeoutStatus=2;this.initialTimeout=setTimeout(function(){if(NDSearch.initialTimeoutStatus==2){clearTimeout(NDSearch.initialTimeout);NDSearch.initialTimeout=undefined;NDSearch.initialTimeoutStatus=3;NDSearch.Update();}},1250);}}};this.OnResultsFocus=function(){this.domSearchField.focus();};this.OnUpdateLayout=function(){if(this.domResults!=undefined){this.PositionResults();if(this.keyboardSelectionIndex!=-1){var domSelectedEntry=document.getElementById("SeSelectedEntry");if(domSelectedEntry!=undefined){this.ScrollEntryIntoView(domSelectedEntry,false);}}}};this.GetSearchInterpretations=function(){var interpretations=[];var normalizedSearchText=this.domSearchField.value.toLowerCase();normalizedSearchText=normalizedSearchText.replace(/\s+/g," ");normalizedSearchText=normalizedSearchText.replace(/^ /,"");normalizedSearchText=normalizedSearchText.replace(/ $/,"");normalizedSearchText=normalizedSearchText.replace(/([^a-z0-9_]) /g,"$1");normalizedSearchText=normalizedSearchText.replace(/ (?=[^a-z0-9_])/g,"");normalizedSearchText=normalizedSearchText.replace(/::|->/g,".");normalizedSearchText=normalizedSearchText.replace(/\\/g,"/");normalizedSearchText=normalizedSearchText.replace(/^[./]+/,"");if(normalizedSearchText==""){return interpretations;}interpretations.push(normalizedSearchText);var lastChar=normalizedSearchText.charAt(normalizedSearchText.length-1);if(lastChar==":"||lastChar=="-"){interpretations.push(normalizedSearchText.substr(0,normalizedSearchText.length-1)+".");}return interpretations;};this.GetMatchingPrefixes=function(searchTextArray){var matchingPrefixes=[];if(this.allPrefixesStatus!=3){return matchingPrefixes;}for(var i=0;i=searchPrefix.length&&this.allPrefixes[prefixIndex].substr(0,searchPrefix.length)==searchPrefix){matchingPrefixes.push(this.allPrefixes[prefixIndex]);prefixIndex++;}else{break;}}}}if(searchTextArray.length<=1){return matchingPrefixes;}matchingPrefixes.sort();for(var i=1;i>1;if(prefix==this.allPrefixes[testIndex]){return testIndex;}else if(prefixmaximum){return true;}}}}}}return false;};this.BuildResults=function(searchInterpretations,searchInterpretationPrefixes,favorClasses,forceExpansion){var results={html:""};this.topLevelEntryCount=0;this.visibleEntryCount=0;var addSearchingStatus=false;for(var p=0;p"+"
"+lastMatchingMemberObject[1];if(lastMatchingMemberObject[0]!=undefined||lastMatchingMemberObject[2]!=undefined){html+="";if(lastMatchingMemberObject[0]!=undefined){html+=", "+lastMatchingMemberObject[0];}if(lastMatchingMemberObject[2]!=undefined){html+=", "+lastMatchingMemberObject[2];}html+=" ";}html+="";this.topLevelEntryCount++;this.visibleEntryCount++;return html;}else{var selected=(this.keyboardSelectionIndex==this.visibleEntryCount);var openClosed;if(forceExpansion||this.openParents.indexOf(this.topLevelEntryCount)!=-1){openClosed="open";}else{openClosed="closed";}var html=""+"
"+keywordObject[0]+" ("+memberMatches+") "+" ";this.topLevelEntryCount++;this.visibleEntryCount++;if(openClosed=="open"){html+="";for(var i=0;i
"+"
"+memberObject[1];if(memberObject[0]!=undefined||memberObject[2]!=undefined){html+="";if(memberObject[0]!=undefined){html+=", "+memberObject[0];}if(memberObject[2]!=undefined){html+=", "+memberObject[2];}html+=" ";}html+="";this.visibleEntryCount++;}}html+=" ";}return html;}};this.BuildSearchingStatus=function(){return""+"Searching..."+"
";};this.BuildNoMatchesStatus=function(){return""+"No Matches"+"
";};this.BuildMoreResultsEntry=function(){var selected=(this.keyboardSelectionIndex==this.visibleEntryCount);var html=""+"
"+"More Results..."+" ";this.visibleEntryCount++;this.topLevelEntryCount++;return html;};this.MakePrefix=function(searchText){var prefix="";for(var i=0;i<3;i++){if(i>=searchText.length){break;}var char=searchText.charAt(i);if(char==" "||char=="."||char=="/"){break;}prefix+=char;}if(prefix.length>0){return prefix;}else{return undefined;}};this.PrefixToHex=function(prefix){var hex="";for(var i=0;imaxHeight){NDCore.SetToAbsolutePosition(this.domResults,undefined,undefined,undefined,maxHeight);}if(this.domResults.offsetWidth>maxWidth){NDCore.SetToAbsolutePosition(this.domResults,undefined,undefined,maxWidth,undefined);}else{if(this.domResults.scrollWidth>this.domResults.clientWidth){var newWidth=this.domResults.offsetWidth+(this.domResults.scrollWidth-this.domResults.clientWidth)+5;if(newWidth>maxWidth){newWidth=maxWidth;}NDCore.SetToAbsolutePosition(this.domResults,undefined,undefined,newWidth,undefined);}if(this.domResults.offsetWidthitemTop){offset=itemTop-windowTop;}if(offset!=0){this.domResults.scrollTop+=offset;}};this.OnPrefixIndexLoaded=function(prefixes){this.allPrefixes=prefixes;this.allPrefixesStatus=3;if(this.initialTimeoutStatus==3){this.Update();}};this.LoadPrefixData=function(prefix){if(this.prefixObjects[prefix]==undefined){var prefixObject=[];prefixObject[0]=prefix;prefixObject[2]=false;prefixObject[3]="NDPrefixLoader_"+this.PrefixToHex(prefix);this.prefixObjects[prefix]=prefixObject;NDCore.LoadJavaScript(this.PrefixToDataFile(prefix),prefixObject[3]);}};this.OnPrefixDataLoaded=function(prefix,commentTypes,keywordObjects){var prefixObject=this.prefixObjects[prefix];if(prefixObject==undefined){return;}for(var k=0;k"+entry[3];entryHTML.onmouseover=mouseOverHandler;entryHTML.onmouseout=mouseOutHandler;var entryHTMLChild=entryHTML.firstChild;if(entryHTMLChild!=undefined&&NDCore.HasClass(entryHTMLChild,"Qualifier")){entryHTMLChild.onmouseover=mouseOverHandler;entryHTMLChild.onmouseout=mouseOutHandler;}newContent.appendChild(entryHTML);}}}var summaryContainer=document.getElementById("NDSummary");var oldContent=document.getElementById("SuContent");if(oldContent!=undefined){summaryContainer.replaceChild(newContent,oldContent);}else{summaryContainer.appendChild(newContent);}newContent.scrollIntoView(true);if(this.summaryEntries!=undefined){NDFramePage.SizeSummaryToContent();}if(NDCore.IsIE()&&NDCore.IEVersion()==10){setTimeout(function(){document.getElementById("NDSummary").style.zoom="100%";},0);}};this.FinishIENavigation=function(){if(NDCore.IsIE()&&this.summaryEntries!=undefined&&NDFramePage.currentLocation!=undefined&&NDFramePage.currentLocation.type=="File"&&NDFramePage.currentLocation.member!=undefined){var topicID=-1;for(var i=0;i0){return id;}else{return-1;}};this.ShowToolTip=function(){var entry=document.getElementById("SuEntry"+this.showingToolTip);this.toolTipHolder.innerHTML=this.summaryToolTips[this.showingToolTip];this.toolTipHolder.style.visibility="hidden";this.toolTipHolder.style.display="block";var summaryBlock=document.getElementById("NDSummary");var x=summaryBlock.offsetLeft+entry.offsetLeft+entry.offsetWidth;var y=summaryBlock.offsetTop+entry.offsetTop-summaryBlock.scrollTop;var newWidth=undefined;var maxWidth=NDCore.WindowClientWidth()-x;if(this.toolTipHolder.offsetWidth>maxWidth){newWidth=maxWidth;}NDCore.SetToAbsolutePosition(this.toolTipHolder,x,y,newWidth,undefined);var prototypes=NDCore.GetElementsByClassName(this.toolTipHolder,"NDPrototype","div");if(prototypes.length>0&&NDCore.HasClass(prototypes[0],"WideForm")&&prototypes[0].scrollWidth>prototypes[0].offsetWidth){NDCore.ChangePrototypeToNarrowForm(prototypes[0]);}var footer=document.getElementById("NDFooter");if(y+this.toolTipHolder.offsetHeight+(footer.offsetHeight*2)>NDCore.WindowClientHeight()){var newY=NDCore.WindowClientHeight()-this.toolTipHolder.offsetHeight-(footer.offsetHeight*2);if(newY<0){newY=0;}NDCore.SetToAbsolutePosition(this.toolTipHolder,undefined,newY,undefined,undefined);}this.toolTipHolder.style.visibility="visible";};this.ResetToolTip=function(){if(this.showingToolTip!=undefined){this.toolTipHolder.style.display="none";this.toolTipHolder.style.width=null;this.lastToolTip=this.showingToolTip;this.showingToolTip=undefined;}if(this.toolTipTimeout!=undefined){clearTimeout(this.toolTipTimeout);this.toolTipTimeout=undefined;}};};
\ No newline at end of file
diff --git a/doc/styles/main.css b/doc/styles/main.css
new file mode 100644
index 0000000..4333a29
--- /dev/null
+++ b/doc/styles/main.css
@@ -0,0 +1 @@
+@import URL("Default/Default.css");
\ No newline at end of file
diff --git a/doc/styles/main.js b/doc/styles/main.js
new file mode 100644
index 0000000..e47a88e
--- /dev/null
+++ b/doc/styles/main.js
@@ -0,0 +1 @@
+"use strict";var NDLoader=new function(){this.JSLinks_All=["DefaultJS/NDCore.js"];this.JSLinks_Frame=["DefaultJS/NDFramePage.js","DefaultJS/NDMenu.js","DefaultJS/NDSummary.js","DefaultJS/NDSearch.js"];this.JSLinks_Content=["DefaultJS/NDContentPage.js"];this.JSLinks_Home=[];this.LoadJS=function(pageType,relativePrefix){this.LoadJSArray(this.JSLinks_All,relativePrefix);this.LoadJSArray(this['JSLinks_'+pageType],relativePrefix);};this.LoadJSArray=function(links,relativePrefix){if(navigator.userAgent.indexOf('KHTML')!=-1){for(var i=0;i');}}else{var head=document.getElementsByTagName('head')[0];for(var i=0;i
@@ -7,16 +10,16 @@
#include
#include
-#ifdef _WIN64
+#if defined(_MSC_VER) || defined(_WIN64)
#define atoll(S) _atoi64(S)
#include
+#include
#else
#include
#include
#include
#include
#include
-#include
#endif
#ifdef __APPLE__
@@ -85,18 +88,24 @@ static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
}
static const char* mmap_file(size_t* len, const char* filename) {
-#ifdef _WIN64
+#if defined(_MSC_VER) || defined(_WIN64)
HANDLE file =
CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ HANDLE fileMapping;
+ LPVOID fileMapView;
assert(file != INVALID_HANDLE_VALUE);
- HANDLE fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
+ fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
assert(fileMapping != INVALID_HANDLE_VALUE);
- LPVOID fileMapView = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
- auto fileMapViewChar = (const char*)fileMapView;
- assert(fileMapView != NULL);
+ fileMapView = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
+ {
+ auto fileMapViewChar = (const char*)fileMapView;
+ assert(fileMapView != NULL);
+ (*len) = GetFileSize(file, NULL);
+ return fileMapViewChar;
+ }
#else
FILE* f;
@@ -166,7 +175,8 @@ static const char* get_file_data(size_t* len, const char* filename) {
static int LoadObjAndConvert(float bmin[3], float bmax[3],
const char* filename) {
- tinyobj_attrib_t attrib;
+ COMPATtinyobj_attrib_t attrib;
+ tinyobj_attrib_t new_attrib;
tinyobj_shape_t* shapes = NULL;
size_t num_shapes;
tinyobj_material_t* materials = NULL;
@@ -182,7 +192,7 @@ static int LoadObjAndConvert(float bmin[3], float bmax[3],
{
unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
- int ret = tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &materials,
+ int ret = tinyobj_parse_obj(&new_attrib, &shapes, &num_shapes, &materials,
&num_materials, data, data_len, flags);
if (ret != TINYOBJ_SUCCESS) {
return 0;
@@ -190,7 +200,8 @@ static int LoadObjAndConvert(float bmin[3], float bmax[3],
printf("# of shapes = %d\n", (int)num_shapes);
printf("# of materials = %d\n", (int)num_materials);
-
+ if(tinyobj_new2old(&new_attrib, &attrib) != TINYOBJ_SUCCESS)
+ exit(-1);
/*
{
int i;
@@ -334,9 +345,10 @@ static int LoadObjAndConvert(float bmin[3], float bmax[3],
printf("bmax = %f, %f, %f\n", (double)bmax[0], (double)bmax[1],
(double)bmax[2]);
- tinyobj_attrib_free(&attrib);
- tinyobj_shapes_free(shapes, num_shapes);
- tinyobj_materials_free(materials, num_materials);
+ tinyobj_attrib_free(&new_attrib);
+ tinyobj_attrib_free_compat(&attrib);
+ tinyobj_shape_free(shapes, num_shapes);
+ tinyobj_material_free(materials, num_materials);
return 1;
}
diff --git a/nd_config/Comments.txt b/nd_config/Comments.txt
new file mode 100644
index 0000000..5990768
--- /dev/null
+++ b/nd_config/Comments.txt
@@ -0,0 +1,85 @@
+Format: 2.0.2
+
+# This is the Natural Docs comments file for this project. If you change
+# anything here, it will apply to THIS PROJECT ONLY. You can edit the version
+# in Natural Docs' Config folder to make the changes apply to all projects,
+# but it's recommended that you edit this version instead.
+
+
+# Ignored Keywords
+# ------------------------------------------------------------------------
+
+# If you'd like to prevent keywords from being recognized by Natural Docs,
+# you can do it like this:
+#
+# Ignore Keywords:
+# [keyword]
+# [keyword]
+# ...
+
+
+# Comment Types
+# ------------------------------------------------------------------------
+
+Comment Type: Union
+
+ Plural Display Name: Unions
+
+ Scope: Normal
+
+ Keywords:
+ union, unions
+
+
+# Each Natural Docs comment has a corresponding type which determine its
+# behavior. You can define your own here or override the settings of the
+# existing ones.
+#
+# Comment Type: [name]
+# Alter Comment Type: [name]
+# Creates a new comment type or changes an existing one.
+#
+# Display Name: [name]
+# Plural Display Name: [name]
+# The singular and plural name of the comment type as it should appear in
+# the output.
+#
+# Simple Identifier: [name]
+# The name of the comment type using only the letters A to Z. No spaces,
+# numbers, symbols, or Unicode allowed. Defaults to the comment type name
+# minus any unacceptable characters. This is used to generate things like
+# CSS class names.
+#
+# Scope: [normal|start|end|always global]
+# How the comment affects scope. Defaults to normal.
+# normal - The comment stays within the current scope.
+# start - The comment starts a new scope for all the comments
+# beneath it, like class comments.
+# end - The comment resets the scope back to global for all the
+# comments beneath it, like section comments.
+# always global - The comment is defined as a global symbol, but does not
+# change the scope for any other comments.
+#
+# Flags: [flag], [flag], ...
+# A combination of settings that apply to the comment type.
+# Code, File, or Documentation
+# Whether it's used to describe a code element, a file, or is a
+# standalone documentation comment. Defaults to Code.
+# Variable Type
+# Whether it describes a code element that can be used as a variable's
+# type.
+# Class Hierarchy or Database Hierarchy
+# Whether it describes a code element that should be included in the
+# class or database hierarchy. Requires Scope: Start.
+# Enum
+# Whether it describes an enum.
+#
+# Keywords:
+# [keyword]
+# [keyword], [plural keyword]
+# ...
+# A list of the comment type's keywords. Each line after the heading is
+# the keyword and optionally its plural form for list comments. You can
+# reuse existing keywords to change their definition. When using
+# "Alter Comment Type", these keywords are added to the existing ones
+# rather than replacing them.
diff --git a/nd_config/Languages.txt b/nd_config/Languages.txt
new file mode 100644
index 0000000..232d50d
--- /dev/null
+++ b/nd_config/Languages.txt
@@ -0,0 +1,95 @@
+Format: 2.0.2
+
+# This is the Natural Docs languages file for this project. If you change
+# anything here, it will apply to THIS PROJECT ONLY. You can edit the version
+# in Natural Docs' Config folder to make the changes apply to all projects,
+# but it's recommended that you edit this version instead.
+
+
+# Ignored Extensions
+# ------------------------------------------------------------------------
+
+# If you'd like to prevent certain file extensions from being scanned by
+# Natural Docs, you can do it like this:
+#
+# Ignore Extensions: [extension] [extension] ...
+
+
+# Languages
+# ------------------------------------------------------------------------
+
+# These settings define the languages Natural Docs knows how to parse. You
+# can define your own here or override the settings of the existing ones.
+# Note that all lists are space separated so that commas can be used as
+# values.
+#
+# Language: [name]
+# Alter Language: [name]
+# Defines a new language or alters an existing one. Its name can use any
+# characters. If any of the properties below have an add/replace form, you
+# must use that when using Alter Language.
+#
+# The language Shebang Script is special. It's entry is only used for
+# extensions, and files with those extensions have their shebang (#!) lines
+# read to determine the real language of the file. Extensionless files are
+# always treated this way.
+#
+# The language Text File is also special. It's treated as one big comment
+# so you can put Natural Docs content in them without special symbols.
+#
+# Extensions: [extension] [extension] ...
+# [Add/Replace] Extensions: [extension] [extension] ...
+# Defines the file extensions of the language's source files.
+#
+# Shebang Strings: [string] [string] ...
+# [Add/Replace] Shebang Strings: [string] [string] ...
+# Defines a list of strings that can appear in the shebang (#!) line to
+# designate that it's part of the language.
+#
+# Simple Identifier: [name]
+# The name of the language using only the letters A to Z. No spaces,
+# numbers, symbols, or Unicode allowed. Defaults to the language name
+# minus any unacceptable characters. This is used to generate things like
+# CSS class names.
+#
+# Aliases: [alias] [alias] ...
+# [Add/Replace] Aliases: [alias] [alias] ...
+# Defines alternate names for the language that can be used to start a code
+# block.
+#
+#
+# Properties for Basic Language Support Only
+# ------------------------------------------------------------------------
+# If you're adding your own language to Natural Docs you must define these.
+#
+# Line Comments: [symbol] [symbol] ...
+# Defines a space-separated list of symbols that are used for line comments,
+# if any.
+#
+# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ...
+# Defines a space-separated list of symbol pairs that are used for block
+# comments, if any.
+#
+# Member Operator: [symbol]
+# Defines the default member operator symbol. The default is a dot.
+#
+# Line Extender: [symbol]
+# Defines the symbol that allows a prototype to span multiple lines if
+# normally a line break would end it.
+#
+# Enum Values: [global|under type|under parent]
+# Defines how enum values are referenced. The default is global.
+# global - Values are always global, referenced as 'value'.
+# under type - Values are under the enum type, referenced as
+# 'class.enum.value'.
+# under parent - Values are under the enum's parent, referenced as
+# 'class.value'.
+#
+# Case Sensitive: [yes|no]
+# Defines whether the language's identifiers are case sensitive. The
+# default is yes.
+#
+# [Comment Type] Prototype Enders: [symbol] [symbol] ...
+# When defined, Natural Docs will attempt to get a prototype from the code
+# immediately following the comment type. It stops when it reaches one of
+# these symbols. Use \n for line breaks.
diff --git a/nd_config/Project.txt b/nd_config/Project.txt
new file mode 100644
index 0000000..211c2be
--- /dev/null
+++ b/nd_config/Project.txt
@@ -0,0 +1,121 @@
+Format: 2.0.2
+
+# This is the file you use to provide information about your project. It can
+# also be used to specify input and output settings so you don't have to
+# include them on the command line.
+
+
+# Project Information
+# ------------------------------------------------------------------------
+
+Title: TinyObj
+Subtitle: Tiny but powerful single file wavefront obj loader
+
+Copyright: Copyright © 2016 - 2019 Syoyo Fujita and many contributors (the MIT license).
+
+Timestamp: Last updated yyyy/mon/dd
+# m - Single digit month, when possible. January is "1".
+# mm - Always double digit month. January is "01".
+# mon - Short month word. January is "Jan".
+# month - Long month word. January is "January".
+# d - Single digit day, when possible. 1 is "1".
+# dd - Always double digit day. 1 is "01".
+# day - Day with text extension. 1 is "1st".
+# yy - Double digit year. 2017 is "17".
+# yyyy - Four digit year. 2017 is "2017".
+# year - Four digit year. 2017 is "2017".
+
+
+# This is where you specify general information about your project.
+#
+# Style: [style]
+# The style to apply to the generated documentation. It can be the name of
+# a CSS file in the project configuration folder or a subfolder that
+# contains Style.txt. Do not include ".css" if using a CSS file.
+
+
+# Source Code
+# ------------------------------------------------------------------------
+
+Source Folder: ..
+
+
+
+# This is where you specify what files and folders Natural Docs should be
+# scanning. If you use any of these options on the command line, this entire
+# section is ignored except for names and numbers.
+#
+# All paths are relative to the project configuration folder, which lets this
+# file remain portable across computers and not cause problems in version
+# control systems. You can enter absolute paths and they will be converted
+# automatically.
+#
+# Source Folder: [path]
+# Name: [name]
+#
+# Specifies a folder which will be searched for source files. If you have
+# more than one, add the Name property to set how it will show up in the
+# menu.
+
+
+# Source Filtering
+# ------------------------------------------------------------------------
+
+# If there are any subfolders in the source code that you would like Natural
+# Docs to ignore, they can be specified here. If you use any of these options
+# on the command line, this entire section is ignored.
+#
+# Ignore Source Folder: [path]
+# Tells Natural Docs to skip this folder when scanning files.
+#
+# Ignore Source Folder Pattern: [pattern]
+# Tells Natural Docs to skip all folder names which match this pattern when
+# scanning files. ? matches a single character, * matches zero or more
+# characters. It applies to the entire folder name, so "cli" will not
+# match "client", although "cli*" will.
+#
+# The data folders of common version control systems (.git, .svn, .cvs, .hg)
+# are ignored automatically. You do not have to specify them here.
+
+
+# Generated Documentation
+# ------------------------------------------------------------------------
+
+HTML Output Folder: ..\doc
+
+
+# This is where you specify what kind of documentation you want Natural Docs
+# to build and where it should be put. If you use any of these options on the
+# command line, this entire section is ignored except for secondary settings.
+#
+# All paths are relative to the project configuration folder, which lets this
+# file remain portable across computers and not cause problems in version
+# control systems. You can enter absolute paths and they will be converted
+# automatically.
+#
+# You can override any of the project information settings under each entry,
+# so if you have multiple output folders you can give them each different
+# styles or subtitles.
+#
+# HTML Output Folder: [path]
+# [Project Information]
+#
+# Generates HTML documentation in the specified folder.
+
+
+# Global Settings
+# ------------------------------------------------------------------------
+
+Auto Group: No
+
+# Other settings that apply to your entire project. Settings specified on the
+# command line override the settings here.
+#
+# Tab Width: [width]
+# The number of spaces tabs should be expanded to.
+#
+# Documented Only: [yes|no]
+# Whether only documented code elements should appear in the output.
+# Defaults to no.
+
+
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..5f79dd8
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.14)
+
+project(tinyobjloader LANGUAGES C)
+
+file(GLOB SOURCE_FILES "*.c")
+file(GLOB HEADER_FILES "*.h")
+
+add_executable(tinyobjloader ${SOURCE_FILES} ${HEADER_FILES} "../tinyobj_loader_c.h")
+target_compile_definitions(tinyobjloader PUBLIC "_CRT_SECURE_NO_WARNINGS")
+target_include_directories(tinyobjloader PUBLIC "../")
diff --git a/test/acutest.h b/test/acutest.h
index fd8c185..bbd284a 100644
--- a/test/acutest.h
+++ b/test/acutest.h
@@ -2,7 +2,7 @@
* Acutest -- Another C/C++ Unit Test facility
*
*
- * Copyright (c) 2013-2017 Martin Mitas
+ * Copyright (c) 2013-2019 Martin Mitas
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -56,7 +56,7 @@
*
* void test_func(void);
*/
-#define TEST_LIST const struct test__ test_list__[]
+#define TEST_LIST const struct test__ test_list__[]
/* Macros for testing whether an unit test succeeds or fails. These macros
@@ -78,31 +78,130 @@
* TEST_CHECK(ptr->member2 > 200);
* }
*/
-#define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__)
-#define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond)
+#define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__)
+#define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond)
+
+#ifdef __cplusplus
+/* Macros to verify that the code (the 1st argument) throws exception of given
+ * type (the 2nd argument). (Note these macros are only available in C++.)
+ *
+ * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like
+ * message.
+ *
+ * For example:
+ *
+ * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType);
+ *
+ * If the function_that_throw() throws ExpectedExceptionType, the check passes.
+ * If the function throws anything incompatible with ExpectedExceptionType
+ * (or if it does not thrown an exception at all), the check fails.
+ */
+#define TEST_EXCEPTION(code, exctype) \
+ do { \
+ bool exc_ok__ = false; \
+ const char *msg__ = NULL; \
+ try { \
+ code; \
+ msg__ = "No exception thrown."; \
+ } catch(exctype const&) { \
+ exc_ok__= true; \
+ } catch(...) { \
+ msg__ = "Unexpected exception thrown."; \
+ } \
+ test_check__(exc_ok__, __FILE__, __LINE__, #code " throws " #exctype); \
+ if(msg__ != NULL) \
+ test_message__("%s", msg__); \
+ } while(0)
+#define TEST_EXCEPTION_(code, exctype, ...) \
+ do { \
+ bool exc_ok__ = false; \
+ const char *msg__ = NULL; \
+ try { \
+ code; \
+ msg__ = "No exception thrown."; \
+ } catch(exctype const&) { \
+ exc_ok__= true; \
+ } catch(...) { \
+ msg__ = "Unexpected exception thrown."; \
+ } \
+ test_check__(exc_ok__, __FILE__, __LINE__, __VA_ARGS__); \
+ if(msg__ != NULL) \
+ test_message__("%s", msg__); \
+ } while(0)
+#endif /* #ifdef __cplusplus */
+
+
+/* Sometimes it is useful to split execution of more complex unit tests to some
+ * smaller parts and associate those parts with some names.
+ *
+ * This is especially handy if the given unit test is implemented as a loop
+ * over some vector of multiple testing inputs. Using these macros allow to use
+ * sort of subtitle for each iteration of the loop (e.g. outputting the input
+ * itself or a name associated to it), so that if any TEST_CHECK condition
+ * fails in the loop, it can be easily seen which iteration triggers the
+ * failure, without the need to manually output the iteration-specific data in
+ * every single TEST_CHECK inside the loop body.
+ *
+ * TEST_CASE allows to specify only single string as the name of the case,
+ * TEST_CASE_ provides all the power of printf-like string formatting.
+ *
+ * Note that the test cases cannot be nested. Starting a new test case ends
+ * implicitly the previous one. To end the test case explicitly (e.g. to end
+ * the last test case after exiting the loop), you may use TEST_CASE(NULL).
+ */
+#define TEST_CASE_(...) test_case__(__VA_ARGS__)
+#define TEST_CASE(name) test_case__("%s", name);
/* printf-like macro for outputting an extra information about a failure.
*
- * Note it does not output anything if there was not (yet) failed condition
- * in the current test. Intended use is to output some computed output
- * versus the expected value, e.g. like this:
+ * Intended use is to output some computed output versus the expected value,
+ * e.g. like this:
*
* if(!TEST_CHECK(produced == expected)) {
* TEST_MSG("Expected: %d", expected);
* TEST_MSG("Produced: %d", produced);
* }
*
+ * Note the message is only written down if the most recent use of any checking
+ * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed.
+ * This means the above is equivalent to just this:
+ *
+ * TEST_CHECK(produced == expected);
+ * TEST_MSG("Expected: %d", expected);
+ * TEST_MSG("Produced: %d", produced);
+ *
* The macro can deal with multi-line output fairly well. It also automatically
* adds a final new-line if there is none present.
*/
-#define TEST_MSG(...) test_message__(__VA_ARGS__)
+#define TEST_MSG(...) test_message__(__VA_ARGS__)
+
/* Maximal output per TEST_MSG call. Longer messages are cut.
* You may define another limit prior including "acutest.h"
*/
#ifndef TEST_MSG_MAXSIZE
-#define TEST_MSG_MAXSIZE 1024
+ #define TEST_MSG_MAXSIZE 1024
+#endif
+
+
+/* Macro for dumping a block of memory.
+ *
+ * Its inteded use is very similar to what TEST_MSG is for, but instead of
+ * generating any printf-like message, this is for dumping raw block of a
+ * memory in a hexadecimal form:
+ *
+ * TEST_CHECK(size_produced == size_expected && memcmp(addr_produced, addr_expected, size_produced) == 0);
+ * TEST_DUMP("Expected:", addr_expected, size_expected);
+ * TEST_DUMP("Produced:", addr_produced, size_produced);
+ */
+#define TEST_DUMP(title, addr, size) test_dump__(title, addr, size)
+
+/* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut.
+ * You may define another limit prior including "acutest.h"
+ */
+#ifndef TEST_DUMP_MAXSIZE
+ #define TEST_DUMP_MAXSIZE 1024
#endif
@@ -112,34 +211,41 @@
/* The unit test files should not rely on anything below. */
+#include
#include
#include
#include
#include
#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
-#define ACUTEST_UNIX__ 1
+ #define ACUTEST_UNIX__ 1
#include
+ #include
#include
#include
#include
#include
+ #include
+
+ #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC
+ #define ACUTEST_HAS_POSIX_TIMER__ 1
+ #endif
#endif
#if defined(__gnu_linux__)
-#define ACUTEST_LINUX__ 1
+ #define ACUTEST_LINUX__ 1
#include
#include
#endif
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
-#define ACUTEST_WIN__ 1
+ #define ACUTEST_WIN__ 1
#include
#include
#endif
#ifdef __cplusplus
-#include
+ #include
#endif
@@ -148,7 +254,7 @@
#ifdef __cplusplus
-extern "C" {
+ extern "C" {
#endif
@@ -157,31 +263,151 @@ struct test__ {
void (*func)(void);
};
+struct test_detail__ {
+ unsigned char flags;
+ double duration;
+};
+
+enum {
+ TEST_FLAG_RUN__ = 1 << 0,
+ TEST_FLAG_SUCCESS__ = 1 << 1,
+ TEST_FLAG_FAILURE__ = 1 << 2,
+};
+
extern const struct test__ test_list__[];
int test_check__(int cond, const char* file, int line, const char* fmt, ...);
+void test_case__(const char* fmt, ...);
void test_message__(const char* fmt, ...);
+void test_dump__(const char* title, const void* addr, size_t size);
#ifndef TEST_NO_MAIN
static char* test_argv0__ = NULL;
static size_t test_list_size__ = 0;
-static const struct test__** tests__ = NULL;
-static char* test_flags__ = NULL;
+static struct test_detail__ *test_details__ = NULL;
static size_t test_count__ = 0;
static int test_no_exec__ = -1;
static int test_no_summary__ = 0;
+static int test_tap__ = 0;
static int test_skip_mode__ = 0;
+static int test_worker__ = 0;
+static int test_worker_index__ = 0;
+static int test_cond_failed__ = 0;
+static FILE *test_xml_output__ = NULL;
static int test_stat_failed_units__ = 0;
static int test_stat_run_units__ = 0;
static const struct test__* test_current_unit__ = NULL;
+static int test_current_index__ = 0;
+static char test_case_name__[64] = "";
static int test_current_already_logged__ = 0;
+static int test_case_current_already_logged__ = 0;
static int test_verbose_level__ = 2;
static int test_current_failures__ = 0;
static int test_colorize__ = 0;
+static int test_timer__ = 0;
+
+#if defined ACUTEST_WIN__
+ typedef LARGE_INTEGER test_timer_type__;
+ static LARGE_INTEGER test_timer_freq__;
+ static test_timer_type__ test_timer_start__;
+ static test_timer_type__ test_timer_end__;
+
+ static void
+ test_timer_init__(void)
+ {
+ QueryPerformanceFrequency(&test_timer_freq__);
+ }
+
+ static void
+ test_timer_get_time__(LARGE_INTEGER* ts)
+ {
+ QueryPerformanceCounter(ts);
+ }
+
+ static double
+ test_timer_diff__(LARGE_INTEGER start, LARGE_INTEGER end)
+ {
+ double duration = end.QuadPart - start.QuadPart;
+ duration /= test_timer_freq__.QuadPart;
+ return duration;
+ }
+
+ static void
+ test_timer_print_diff__(void)
+ {
+ printf("%.6lf secs", test_timer_diff__(test_timer_start__, test_timer_end__));
+ }
+#elif defined ACUTEST_HAS_POSIX_TIMER__
+ static clockid_t test_timer_id__;
+ typedef struct timespec test_timer_type__;
+ static test_timer_type__ test_timer_start__;
+ static test_timer_type__ test_timer_end__;
+
+ static void
+ test_timer_init__(void)
+ {
+ if(test_timer__ == 1)
+ #ifdef CLOCK_MONOTONIC_RAW
+ /* linux specific; not subject of NTP adjustements or adjtime() */
+ test_timer_id__ = CLOCK_MONOTONIC_RAW;
+ #else
+ test_timer_id__ = CLOCK_MONOTONIC;
+ #endif
+ else if(test_timer__ == 2)
+ test_timer_id__ = CLOCK_PROCESS_CPUTIME_ID;
+ }
+
+ static void
+ test_timer_get_time__(struct timespec* ts)
+ {
+ clock_gettime(test_timer_id__, ts);
+ }
+
+ static double
+ test_timer_diff__(struct timespec start, struct timespec end)
+ {
+ return ((double) end.tv_sec +
+ (double) end.tv_nsec * 10e-9)
+ -
+ ((double) start.tv_sec +
+ (double) start.tv_nsec * 10e-9);
+ }
+
+ static void
+ test_timer_print_diff__(void)
+ {
+ printf("%.6lf secs",
+ test_timer_diff__(test_timer_start__, test_timer_end__));
+ }
+#else
+ typedef int test_timer_type__;
+ static test_timer_type__ test_timer_start__;
+ static test_timer_type__ test_timer_end__;
+
+ void
+ test_timer_init__(void)
+ {}
+
+ static void
+ test_timer_get_time__(int* ts)
+ {
+ (void) ts;
+ }
+
+ static double
+ test_timer_diff__(int start, int end)
+ {
+ return 0.0;
+ }
+
+ static void
+ test_timer_print_diff__(void)
+ {}
+#endif
#define TEST_COLOR_DEFAULT__ 0
#define TEST_COLOR_GREEN__ 1
@@ -215,7 +441,7 @@ test_print_in_color__(int color, const char* fmt, ...)
case TEST_COLOR_GREEN_INTENSIVE__: col_str = "\033[1;32m"; break;
case TEST_COLOR_RED_INTENSIVE__: col_str = "\033[1;31m"; break;
case TEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\033[1m"; break;
- default: col_str = "\033[0m"; break;
+ default: col_str = "\033[0m"; break;
}
printf("%s", col_str);
n = printf("%s", buffer);
@@ -237,7 +463,7 @@ test_print_in_color__(int color, const char* fmt, ...)
case TEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
case TEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
case TEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break;
- default: attr = 0; break;
+ default: attr = 0; break;
}
if(attr != 0)
SetConsoleTextAttribute(h, attr);
@@ -251,6 +477,74 @@ test_print_in_color__(int color, const char* fmt, ...)
#endif
}
+static void
+test_begin_test_line__(const struct test__* test)
+{
+ if(!test_tap__) {
+ if(test_verbose_level__ >= 3) {
+ test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name);
+ test_current_already_logged__++;
+ } else if(test_verbose_level__ >= 1) {
+ int n;
+ char spaces[48];
+
+ n = test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name);
+ memset(spaces, ' ', sizeof(spaces));
+ if(n < (int) sizeof(spaces))
+ printf("%.*s", (int) sizeof(spaces) - n, spaces);
+ } else {
+ test_current_already_logged__ = 1;
+ }
+ }
+}
+
+static void
+test_finish_test_line__(int result)
+{
+ if(test_tap__) {
+ const char* str = (result == 0) ? "ok" : "not ok";
+
+ printf("%s %u - %s\n", str, test_current_index__ + 1, test_current_unit__->name);
+
+ if(result == 0 && test_timer__) {
+ printf("# Duration: ");
+ test_timer_print_diff__();
+ printf("\n");
+ }
+ } else {
+ int color = (result == 0) ? TEST_COLOR_GREEN_INTENSIVE__ : TEST_COLOR_RED_INTENSIVE__;
+ const char* str = (result == 0) ? "OK" : "FAILED";
+ printf("[ ");
+ test_print_in_color__(color, str);
+ printf(" ]");
+
+ if(result == 0 && test_timer__) {
+ printf(" ");
+ test_timer_print_diff__();
+ }
+
+ printf("\n");
+ }
+}
+
+static void
+test_line_indent__(int level)
+{
+ static const char spaces[] = " ";
+ int n = level * 2;
+
+ if(test_tap__ && n > 0) {
+ n--;
+ printf("#");
+ }
+
+ while(n > 16) {
+ printf("%s", spaces);
+ n -= 16;
+ }
+ printf("%.*s", n, spaces);
+}
+
int
test_check__(int cond, const char* file, int line, const char* fmt, ...)
{
@@ -263,11 +557,9 @@ test_check__(int cond, const char* file, int line, const char* fmt, ...)
result_color = TEST_COLOR_GREEN__;
verbose_level = 3;
} else {
- if(!test_current_already_logged__ && test_current_unit__ != NULL) {
- printf("[ ");
- test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED");
- printf(" ]\n");
- }
+ if(!test_current_already_logged__ && test_current_unit__ != NULL)
+ test_finish_test_line__(-1);
+
result_str = "failed";
result_color = TEST_COLOR_RED__;
verbose_level = 2;
@@ -278,8 +570,14 @@ test_check__(int cond, const char* file, int line, const char* fmt, ...)
if(test_verbose_level__ >= verbose_level) {
va_list args;
- printf(" ");
+ if(!test_case_current_already_logged__ && test_case_name__[0]) {
+ test_line_indent__(1);
+ test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__);
+ test_current_already_logged__++;
+ test_case_current_already_logged__++;
+ }
+ test_line_indent__(test_case_name__[0] ? 2 : 1);
if(file != NULL) {
if(test_verbose_level__ < 3) {
#ifdef ACUTEST_WIN__
@@ -309,7 +607,37 @@ test_check__(int cond, const char* file, int line, const char* fmt, ...)
test_current_already_logged__++;
}
- return (cond != 0);
+ test_cond_failed__ = (cond == 0);
+ return !test_cond_failed__;
+}
+
+void
+test_case__(const char* fmt, ...)
+{
+ va_list args;
+
+ if(test_verbose_level__ < 2)
+ return;
+
+ if(test_case_name__[0]) {
+ test_case_current_already_logged__ = 0;
+ test_case_name__[0] = '\0';
+ }
+
+ if(fmt == NULL)
+ return;
+
+ va_start(args, fmt);
+ vsnprintf(test_case_name__, sizeof(test_case_name__) - 1, fmt, args);
+ va_end(args);
+ test_case_name__[sizeof(test_case_name__) - 1] = '\0';
+
+ if(test_verbose_level__ >= 3) {
+ test_line_indent__(1);
+ test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__);
+ test_current_already_logged__++;
+ test_case_current_already_logged__++;
+ }
}
void
@@ -325,7 +653,7 @@ test_message__(const char* fmt, ...)
/* We allow extra message only when something is already wrong in the
* current test. */
- if(!test_current_already_logged__ || test_current_unit__ == NULL)
+ if(test_current_unit__ == NULL || !test_cond_failed__)
return;
va_start(args, fmt);
@@ -338,11 +666,68 @@ test_message__(const char* fmt, ...)
line_end = strchr(line_beg, '\n');
if(line_end == NULL)
break;
- printf(" %.*s\n", (int)(line_end - line_beg), line_beg);
+ test_line_indent__(test_case_name__[0] ? 3 : 2);
+ printf("%.*s\n", (int)(line_end - line_beg), line_beg);
line_beg = line_end + 1;
}
- if(line_beg[0] != '\0')
- printf(" %s\n", line_beg);
+ if(line_beg[0] != '\0') {
+ test_line_indent__(test_case_name__[0] ? 3 : 2);
+ printf("%s\n", line_beg);
+ }
+}
+
+void
+test_dump__(const char* title, const void* addr, size_t size)
+{
+ static const size_t BYTES_PER_LINE = 16;
+ size_t line_beg;
+ size_t truncate = 0;
+
+ if(test_verbose_level__ < 2)
+ return;
+
+ /* We allow extra message only when something is already wrong in the
+ * current test. */
+ if(test_current_unit__ == NULL || !test_cond_failed__)
+ return;
+
+ if(size > TEST_DUMP_MAXSIZE) {
+ truncate = size - TEST_DUMP_MAXSIZE;
+ size = TEST_DUMP_MAXSIZE;
+ }
+
+ test_line_indent__(test_case_name__[0] ? 3 : 2);
+ printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title);
+
+ for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) {
+ size_t line_end = line_beg + BYTES_PER_LINE;
+ size_t off;
+
+ test_line_indent__(test_case_name__[0] ? 4 : 3);
+ printf("%08lx: ", (unsigned long)line_beg);
+ for(off = line_beg; off < line_end; off++) {
+ if(off < size)
+ printf(" %02x", ((unsigned char*)addr)[off]);
+ else
+ printf(" ");
+ }
+
+ printf(" ");
+ for(off = line_beg; off < line_end; off++) {
+ unsigned char byte = ((unsigned char*)addr)[off];
+ if(off < size)
+ printf("%c", (iscntrl(byte) ? '.' : byte));
+ else
+ break;
+ }
+
+ printf("\n");
+ }
+
+ if(truncate > 0) {
+ test_line_indent__(test_case_name__[0] ? 4 : 3);
+ printf(" ... (and more %u bytes)\n", (unsigned) truncate);
+ }
}
static void
@@ -358,15 +743,25 @@ test_list_names__(void)
static void
test_remember__(int i)
{
- if(test_flags__[i])
+ if(test_details__[i].flags & TEST_FLAG_RUN__)
return;
- else
- test_flags__[i] = 1;
- tests__[test_count__] = &test_list__[i];
+ test_details__[i].flags |= TEST_FLAG_RUN__;
test_count__++;
}
+static void
+test_set_success__(int i, int success)
+{
+ test_details__[i].flags |= success ? TEST_FLAG_SUCCESS__ : TEST_FLAG_FAILURE__;
+}
+
+static void
+test_set_duration__(int i, double duration)
+{
+ test_details__[i].duration = duration;
+}
+
static int
test_name_contains_word__(const char* name, const char* pattern)
{
@@ -430,68 +825,7 @@ test_lookup__(const char* pattern)
return n;
}
-/* Call directly the given test unit function. */
-static int
-test_do_run__(const struct test__* test)
-{
- test_current_unit__ = test;
- test_current_failures__ = 0;
- test_current_already_logged__ = 0;
-
- if(test_verbose_level__ >= 3) {
- test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name);
- test_current_already_logged__++;
- } else if(test_verbose_level__ >= 1) {
- int n;
- char spaces[48];
-
- n = test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name);
- memset(spaces, ' ', sizeof(spaces));
- if(n < (int) sizeof(spaces))
- printf("%.*s", (int) sizeof(spaces) - n, spaces);
- } else {
- test_current_already_logged__ = 1;
- }
-
-#ifdef __cplusplus
- try {
-#endif
-
- /* This is good to do for case the test unit e.g. crashes. */
- fflush(stdout);
- fflush(stderr);
-
- test->func();
-#ifdef __cplusplus
- } catch(std::exception& e) {
- const char* what = e.what();
- if(what != NULL)
- test_check__(0, NULL, 0, "Threw std::exception: %s", what);
- else
- test_check__(0, NULL, 0, "Threw std::exception");
- } catch(...) {
- test_check__(0, NULL, 0, "Threw an exception");
- }
-#endif
-
- if(test_verbose_level__ >= 3) {
- switch(test_current_failures__) {
- case 0: test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, " All conditions have passed.\n\n"); break;
- case 1: test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, " One condition has FAILED.\n\n"); break;
- default: test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, " %d conditions have FAILED.\n\n", test_current_failures__); break;
- }
- } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) {
- printf("[ ");
- test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "OK");
- printf(" ]\n");
- }
-
- test_current_unit__ = NULL;
- return (test_current_failures__ == 0) ? 0 : -1;
-}
-
-#if defined(ACUTEST_UNIX__) || defined(ACUTEST_WIN__)
/* Called if anything goes bad in Acutest, or if the unit test ends in other
* way then by normal returning from its function (e.g. exception or some
* abnormal child process termination). */
@@ -504,31 +838,109 @@ test_error__(const char* fmt, ...)
return;
if(test_verbose_level__ <= 2 && !test_current_already_logged__ && test_current_unit__ != NULL) {
- printf("[ ");
- test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED");
- printf(" ]\n");
+ if(test_tap__) {
+ test_finish_test_line__(-1);
+ } else {
+ printf("[ ");
+ test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED");
+ printf(" ]\n");
+ }
}
if(test_verbose_level__ >= 2) {
- test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, " Error: ");
+ test_line_indent__(1);
+ if(test_verbose_level__ >= 3)
+ test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "ERROR: ");
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
printf("\n");
}
+
+ if(test_verbose_level__ >= 3) {
+ printf("\n");
+ }
}
+
+/* Call directly the given test unit function. */
+static int
+test_do_run__(const struct test__* test, int index)
+{
+ test_current_unit__ = test;
+ test_current_index__ = index;
+ test_current_failures__ = 0;
+ test_current_already_logged__ = 0;
+ test_cond_failed__ = 0;
+
+ test_begin_test_line__(test);
+
+#ifdef __cplusplus
+ try {
#endif
+ /* This is good to do for case the test unit e.g. crashes. */
+ fflush(stdout);
+ fflush(stderr);
+
+ test_timer_get_time__(&test_timer_start__);
+ test->func();
+ test_timer_get_time__(&test_timer_end__);
+
+ if(test_verbose_level__ >= 3) {
+ test_line_indent__(1);
+ if(test_current_failures__ == 0) {
+ test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS: ");
+ printf("All conditions have passed.\n");
+
+ if(test_timer__) {
+ test_line_indent__(1);
+ printf("Duration: ");
+ test_timer_print_diff__();
+ printf("\n");
+ }
+ } else {
+ test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED: ");
+ printf("%d condition%s %s failed.\n",
+ test_current_failures__,
+ (test_current_failures__ == 1) ? "" : "s",
+ (test_current_failures__ == 1) ? "has" : "have");
+ }
+ printf("\n");
+ } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) {
+ test_finish_test_line__(0);
+ }
+
+ test_case__(NULL);
+ test_current_unit__ = NULL;
+ return (test_current_failures__ == 0) ? 0 : -1;
+
+#ifdef __cplusplus
+ } catch(std::exception& e) {
+ const char* what = e.what();
+ if(what != NULL)
+ test_error__("Threw std::exception: %s", what);
+ else
+ test_error__("Threw std::exception");
+ return -1;
+ } catch(...) {
+ test_error__("Threw an exception");
+ return -1;
+ }
+#endif
+}
+
/* Trigger the unit test. If possible (and not suppressed) it starts a child
* process who calls test_do_run__(), otherwise it calls test_do_run__()
* directly. */
static void
-test_run__(const struct test__* test)
+test_run__(const struct test__* test, int index, int master_index)
{
int failed = 1;
+ test_timer_type__ start, end;
test_current_unit__ = test;
test_current_already_logged__ = 0;
+ test_timer_get_time__(&start);
if(!test_no_exec__) {
@@ -537,13 +949,17 @@ test_run__(const struct test__* test)
pid_t pid;
int exit_code;
+ /* Make sure the child starts with empty I/O buffers. */
+ fflush(stdout);
+ fflush(stderr);
+
pid = fork();
if(pid == (pid_t)-1) {
test_error__("Cannot fork. %s [%d]", strerror(errno), errno);
failed = 1;
} else if(pid == 0) {
/* Child: Do the test. */
- failed = (test_do_run__(test) != 0);
+ failed = (test_do_run__(test, index) != 0);
exit(failed ? 1 : 0);
} else {
/* Parent: Wait until child terminates and analyze its exit code. */
@@ -584,9 +1000,11 @@ test_run__(const struct test__* test)
/* Windows has no fork(). So we propagate all info into the child
* through a command line arguments. */
_snprintf(buffer, sizeof(buffer)-1,
- "%s --no-exec --no-summary --verbose=%d --color=%s -- \"%s\"",
- test_argv0__, test_verbose_level__,
- test_colorize__ ? "always" : "never", test->name);
+ "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"",
+ test_argv0__, index, test_timer__ ? "--timer" : "",
+ test_tap__ ? "--tap" : "", test_verbose_level__,
+ test_colorize__ ? "always" : "never",
+ test->name);
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(STARTUPINFO);
if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) {
@@ -603,20 +1021,24 @@ test_run__(const struct test__* test)
#else
/* A platform where we don't know how to run child process. */
- failed = (test_do_run__(test) != 0);
+ failed = (test_do_run__(test, index) != 0);
#endif
} else {
/* Child processes suppressed through --no-exec. */
- failed = (test_do_run__(test) != 0);
+ failed = (test_do_run__(test, index) != 0);
}
+ test_timer_get_time__(&end);
test_current_unit__ = NULL;
test_stat_run_units__++;
if(failed)
test_stat_failed_units__++;
+
+ test_set_success__(master_index, !failed);
+ test_set_duration__(master_index, test_timer_diff__(start, end));
}
#if defined(ACUTEST_WIN__)
@@ -634,29 +1056,199 @@ test_exception_filter__(EXCEPTION_POINTERS *ptrs)
#endif
+#define TEST_CMDLINE_OPTFLAG_OPTIONALARG__ 0x0001
+#define TEST_CMDLINE_OPTFLAG_REQUIREDARG__ 0x0002
+
+#define TEST_CMDLINE_OPTID_NONE__ 0
+#define TEST_CMDLINE_OPTID_UNKNOWN__ (-0x7fffffff + 0)
+#define TEST_CMDLINE_OPTID_MISSINGARG__ (-0x7fffffff + 1)
+#define TEST_CMDLINE_OPTID_BOGUSARG__ (-0x7fffffff + 2)
+
+typedef struct TEST_CMDLINE_OPTION__ {
+ char shortname;
+ const char* longname;
+ int id;
+ unsigned flags;
+} TEST_CMDLINE_OPTION__;
+
+static int
+test_cmdline_handle_short_opt_group__(const TEST_CMDLINE_OPTION__* options,
+ const char* arggroup,
+ int (*callback)(int /*optval*/, const char* /*arg*/))
+{
+ const TEST_CMDLINE_OPTION__* opt;
+ int i;
+ int ret = 0;
+
+ for(i = 0; arggroup[i] != '\0'; i++) {
+ for(opt = options; opt->id != 0; opt++) {
+ if(arggroup[i] == opt->shortname)
+ break;
+ }
+
+ if(opt->id != 0 && !(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) {
+ ret = callback(opt->id, NULL);
+ } else {
+ /* Unknown option. */
+ char badoptname[3];
+ badoptname[0] = '-';
+ badoptname[1] = arggroup[i];
+ badoptname[2] = '\0';
+ ret = callback((opt->id != 0 ? TEST_CMDLINE_OPTID_MISSINGARG__ : TEST_CMDLINE_OPTID_UNKNOWN__),
+ badoptname);
+ }
+
+ if(ret != 0)
+ break;
+ }
+
+ return ret;
+}
+
+#define TEST_CMDLINE_AUXBUF_SIZE__ 32
+
+static int
+test_cmdline_read__(const TEST_CMDLINE_OPTION__* options, int argc, char** argv,
+ int (*callback)(int /*optval*/, const char* /*arg*/))
+{
+
+ const TEST_CMDLINE_OPTION__* opt;
+ char auxbuf[TEST_CMDLINE_AUXBUF_SIZE__+1];
+ int after_doubledash = 0;
+ int i = 1;
+ int ret = 0;
+
+ auxbuf[TEST_CMDLINE_AUXBUF_SIZE__] = '\0';
+
+ while(i < argc) {
+ if(after_doubledash || strcmp(argv[i], "-") == 0) {
+ /* Non-option argument. */
+ ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]);
+ } else if(strcmp(argv[i], "--") == 0) {
+ /* End of options. All the remaining members are non-option arguments. */
+ after_doubledash = 1;
+ } else if(argv[i][0] != '-') {
+ /* Non-option argument. */
+ ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]);
+ } else {
+ for(opt = options; opt->id != 0; opt++) {
+ if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) {
+ size_t len = strlen(opt->longname);
+ if(strncmp(argv[i]+2, opt->longname, len) == 0) {
+ /* Regular long option. */
+ if(argv[i][2+len] == '\0') {
+ /* with no argument provided. */
+ if(!(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__))
+ ret = callback(opt->id, NULL);
+ else
+ ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]);
+ break;
+ } else if(argv[i][2+len] == '=') {
+ /* with an argument provided. */
+ if(opt->flags & (TEST_CMDLINE_OPTFLAG_OPTIONALARG__ | TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) {
+ ret = callback(opt->id, argv[i]+2+len+1);
+ } else {
+ sprintf(auxbuf, "--%s", opt->longname);
+ ret = callback(TEST_CMDLINE_OPTID_BOGUSARG__, auxbuf);
+ }
+ break;
+ } else {
+ continue;
+ }
+ }
+ } else if(opt->shortname != '\0' && argv[i][0] == '-') {
+ if(argv[i][1] == opt->shortname) {
+ /* Regular short option. */
+ if(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__) {
+ if(argv[i][2] != '\0')
+ ret = callback(opt->id, argv[i]+2);
+ else if(i+1 < argc)
+ ret = callback(opt->id, argv[++i]);
+ else
+ ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]);
+ break;
+ } else {
+ ret = callback(opt->id, NULL);
+
+ /* There might be more (argument-less) short options
+ * grouped together. */
+ if(ret == 0 && argv[i][2] != '\0')
+ ret = test_cmdline_handle_short_opt_group__(options, argv[i]+2, callback);
+ break;
+ }
+ }
+ }
+ }
+
+ if(opt->id == 0) { /* still not handled? */
+ if(argv[i][0] != '-') {
+ /* Non-option argument. */
+ ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]);
+ } else {
+ /* Unknown option. */
+ char* badoptname = argv[i];
+
+ if(strncmp(badoptname, "--", 2) == 0) {
+ /* Strip any argument from the long option. */
+ char* assignement = strchr(badoptname, '=');
+ if(assignement != NULL) {
+ size_t len = assignement - badoptname;
+ if(len > TEST_CMDLINE_AUXBUF_SIZE__)
+ len = TEST_CMDLINE_AUXBUF_SIZE__;
+ strncpy(auxbuf, badoptname, len);
+ auxbuf[len] = '\0';
+ badoptname = auxbuf;
+ }
+ }
+
+ ret = callback(TEST_CMDLINE_OPTID_UNKNOWN__, badoptname);
+ }
+ }
+ }
+
+ if(ret != 0)
+ return ret;
+ i++;
+ }
+
+ return ret;
+}
+
static void
test_help__(void)
{
printf("Usage: %s [options] [test...]\n", test_argv0__);
+ printf("\n");
printf("Run the specified unit tests; or if the option '--skip' is used, run all\n");
printf("tests in the suite but those listed. By default, if no tests are specified\n");
printf("on the command line, all unit tests in the suite are run.\n");
printf("\n");
printf("Options:\n");
printf(" -s, --skip Execute all unit tests but the listed ones\n");
- printf(" --exec=WHEN If supported, execute unit tests as child processes\n");
+ printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n");
printf(" (WHEN is one of 'auto', 'always', 'never')\n");
+#if defined ACUTEST_WIN__
+ printf(" -t, --timer Measure test duration\n");
+#elif defined ACUTEST_HAS_POSIX_TIMER__
+ printf(" -t, --timer Measure test duration (real time)\n");
+ printf(" --timer=TIMER Measure test duration, using given timer\n");
+ printf(" (TIMER is one of 'real', 'cpu')\n");
+#endif
printf(" -E, --no-exec Same as --exec=never\n");
printf(" --no-summary Suppress printing of test results summary\n");
+ printf(" --tap Produce TAP-compliant output\n");
+ printf(" (See https://testanything.org/)\n");
+ printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n");
printf(" -l, --list List unit tests in the suite and exit\n");
- printf(" -v, --verbose Enable more verbose output\n");
+ printf(" -v, --verbose Make output more verbose\n");
printf(" --verbose=LEVEL Set verbose level to LEVEL:\n");
printf(" 0 ... Be silent\n");
printf(" 1 ... Output one line per test (and summary)\n");
printf(" 2 ... As 1 and failed conditions (this is default)\n");
printf(" 3 ... As 1 and all conditions (and extended summary)\n");
- printf(" --color=WHEN Enable colorized output\n");
+ printf(" --color[=WHEN] Enable colorized output\n");
printf(" (WHEN is one of 'auto', 'always', 'never')\n");
+ printf(" --no-color Same as --color=never\n");
printf(" -h, --help Display this help and exit\n");
if(test_list_size__ < 16) {
@@ -665,6 +1257,147 @@ test_help__(void)
}
}
+static const TEST_CMDLINE_OPTION__ test_cmdline_options__[] = {
+ { 's', "skip", 's', 0 },
+ { 0, "exec", 'e', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ },
+ { 'E', "no-exec", 'E', 0 },
+#if defined ACUTEST_WIN__
+ { 't', "timer", 't', 0 },
+#elif defined ACUTEST_HAS_POSIX_TIMER__
+ { 't', "timer", 't', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ },
+#endif
+ { 0, "no-summary", 'S', 0 },
+ { 0, "tap", 'T', 0 },
+ { 'l', "list", 'l', 0 },
+ { 'v', "verbose", 'v', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ },
+ { 0, "color", 'c', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ },
+ { 0, "no-color", 'C', 0 },
+ { 'h', "help", 'h', 0 },
+ { 0, "worker", 'w', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, /* internal */
+ { 'x', "xml-output", 'x', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ },
+ { 0, NULL, 0, 0 }
+};
+
+static int
+test_cmdline_callback__(int id, const char* arg)
+{
+ switch(id) {
+ case 's':
+ test_skip_mode__ = 1;
+ break;
+
+ case 'e':
+ if(arg == NULL || strcmp(arg, "always") == 0) {
+ test_no_exec__ = 0;
+ } else if(strcmp(arg, "never") == 0) {
+ test_no_exec__ = 1;
+ } else if(strcmp(arg, "auto") == 0) {
+ /*noop*/
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", test_argv0__, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__);
+ exit(2);
+ }
+ break;
+
+ case 'E':
+ test_no_exec__ = 1;
+ break;
+
+ case 't':
+#if defined ACUTEST_WIN__ || defined ACUTEST_HAS_POSIX_TIMER__
+ if(arg == NULL || strcmp(arg, "real") == 0) {
+ test_timer__ = 1;
+ #ifndef ACUTEST_WIN__
+ } else if(strcmp(arg, "cpu") == 0) {
+ test_timer__ = 2;
+ #endif
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --timer.\n", test_argv0__, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__);
+ exit(2);
+ }
+#endif
+ break;
+
+ case 'S':
+ test_no_summary__ = 1;
+ break;
+
+ case 'T':
+ test_tap__ = 1;
+ break;
+
+ case 'l':
+ test_list_names__();
+ exit(0);
+
+ case 'v':
+ test_verbose_level__ = (arg != NULL ? atoi(arg) : test_verbose_level__+1);
+ break;
+
+ case 'c':
+ if(arg == NULL || strcmp(arg, "always") == 0) {
+ test_colorize__ = 1;
+ } else if(strcmp(arg, "never") == 0) {
+ test_colorize__ = 0;
+ } else if(strcmp(arg, "auto") == 0) {
+ /*noop*/
+ } else {
+ fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", test_argv0__, arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__);
+ exit(2);
+ }
+ break;
+
+ case 'C':
+ test_colorize__ = 0;
+ break;
+
+ case 'h':
+ test_help__();
+ exit(0);
+
+ case 'w':
+ test_worker__ = 1;
+ test_worker_index__ = atoi(arg);
+ break;
+ case 'x':
+ test_xml_output__ = fopen(arg, "w");
+ if (!test_xml_output__) {
+ fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno));
+ exit(2);
+ }
+ break;
+
+ case 0:
+ if(test_lookup__(arg) == 0) {
+ fprintf(stderr, "%s: Unrecognized unit test '%s'\n", test_argv0__, arg);
+ fprintf(stderr, "Try '%s --list' for list of unit tests.\n", test_argv0__);
+ exit(2);
+ }
+ break;
+
+ case TEST_CMDLINE_OPTID_UNKNOWN__:
+ fprintf(stderr, "Unrecognized command line option '%s'.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__);
+ exit(2);
+
+ case TEST_CMDLINE_OPTID_MISSINGARG__:
+ fprintf(stderr, "The command line option '%s' requires an argument.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__);
+ exit(2);
+
+ case TEST_CMDLINE_OPTID_BOGUSARG__:
+ fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg);
+ fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__);
+ exit(2);
+ }
+
+ return 0;
+}
+
+
#ifdef ACUTEST_LINUX__
static int
test_is_tracer_present__(void)
@@ -708,14 +1441,12 @@ int
main(int argc, char** argv)
{
int i;
- int seen_double_dash = 0;
-
test_argv0__ = argv[0];
#if defined ACUTEST_UNIX__
test_colorize__ = isatty(STDOUT_FILENO);
#elif defined ACUTEST_WIN__
- #if defined __BORLANDC__
+ #if defined __BORLANDC__
test_colorize__ = isatty(_fileno(stdout));
#else
test_colorize__ = _isatty(_fileno(stdout));
@@ -724,61 +1455,21 @@ main(int argc, char** argv)
test_colorize__ = 0;
#endif
+ test_timer_init__();
+
/* Count all test units */
test_list_size__ = 0;
for(i = 0; test_list__[i].func != NULL; i++)
test_list_size__++;
- tests__ = (const struct test__**) malloc(sizeof(const struct test__*) * test_list_size__);
- test_flags__ = (char*) malloc(sizeof(char) * test_list_size__);
- if(tests__ == NULL || test_flags__ == NULL) {
+ test_details__ = (struct test_detail__*)calloc(test_list_size__, sizeof(struct test_detail__));
+ if(test_details__ == NULL) {
fprintf(stderr, "Out of memory.\n");
exit(2);
}
- memset((void*) test_flags__, 0, sizeof(char) * test_list_size__);
/* Parse options */
- for(i = 1; i < argc; i++) {
- if(seen_double_dash || argv[i][0] != '-') {
- if(test_lookup__(argv[i]) == 0) {
- fprintf(stderr, "%s: Unrecognized unit test '%s'\n", argv[0], argv[i]);
- fprintf(stderr, "Try '%s --list' for list of unit tests.\n", argv[0]);
- exit(2);
- }
- } else if(strcmp(argv[i], "--") == 0) {
- seen_double_dash = 1;
- } else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
- test_help__();
- exit(0);
- } else if(strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
- test_verbose_level__++;
- } else if(strncmp(argv[i], "--verbose=", 10) == 0) {
- test_verbose_level__ = atoi(argv[i] + 10);
- } else if(strcmp(argv[i], "--color=auto") == 0) {
- /* noop (set from above) */
- } else if(strcmp(argv[i], "--color=always") == 0 || strcmp(argv[i], "--color") == 0) {
- test_colorize__ = 1;
- } else if(strcmp(argv[i], "--color=never") == 0 || strcmp(argv[i], "--no-color") == 0) {
- test_colorize__ = 0;
- } else if(strcmp(argv[i], "--skip") == 0 || strcmp(argv[i], "-s") == 0) {
- test_skip_mode__ = 1;
- } else if(strcmp(argv[i], "--exec=auto") == 0) {
- /* noop (set from above) */
- } else if(strcmp(argv[i], "--exec=always") == 0 || strcmp(argv[i], "--exec") == 0) {
- test_no_exec__ = 0;
- } else if(strcmp(argv[i], "--exec=never") == 0 || strcmp(argv[i], "--no-exec") == 0 || strcmp(argv[i], "-E") == 0) {
- test_no_exec__ = 1;
- } else if(strcmp(argv[i], "--no-summary") == 0) {
- test_no_summary__ = 1;
- } else if(strcmp(argv[i], "--list") == 0 || strcmp(argv[i], "-l") == 0) {
- test_list_names__();
- exit(0);
- } else {
- fprintf(stderr, "%s: Unrecognized option '%s'\n", argv[0], argv[i]);
- fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
- exit(2);
- }
- }
+ test_cmdline_read__(test_cmdline_options__, argc, argv, test_cmdline_callback__);
#if defined(ACUTEST_WIN__)
SetUnhandledExceptionFilter(test_exception_filter__);
@@ -787,8 +1478,7 @@ main(int argc, char** argv)
/* By default, we want to run all tests. */
if(test_count__ == 0) {
for(i = 0; test_list__[i].func != NULL; i++)
- tests__[i] = &test_list__[i];
- test_count__ = test_list_size__;
+ test_remember__(i);
}
/* Guess whether we want to run unit tests as child processes. */
@@ -809,17 +1499,27 @@ main(int argc, char** argv)
}
}
- /* Run the tests */
- if(!test_skip_mode__) {
- /* Run the listed tests. */
- for(i = 0; i < (int) test_count__; i++)
- test_run__(tests__[i]);
- } else {
- /* Run all tests except those listed. */
- for(i = 0; test_list__[i].func != NULL; i++) {
- if(!test_flags__[i])
- test_run__(&test_list__[i]);
- }
+ if(test_tap__) {
+ /* TAP requires we know test result ("ok", "not ok") before we output
+ * anything about the test, and this gets problematic for larger verbose
+ * levels. */
+ if(test_verbose_level__ > 2)
+ test_verbose_level__ = 2;
+
+ /* TAP harness should provide some summary. */
+ test_no_summary__ = 1;
+
+ if(!test_worker__)
+ printf("1..%d\n", (int) test_count__);
+ }
+
+ int index = test_worker_index__;
+ for(i = 0; test_list__[i].func != NULL; i++) {
+ int run = (test_details__[i].flags & TEST_FLAG_RUN__);
+ if (test_skip_mode__) /* Run all tests except those listed. */
+ run = !run;
+ if(run)
+ test_run__(&test_list__[i], index++, i);
}
/* Write a summary */
@@ -831,7 +1531,6 @@ main(int argc, char** argv)
printf(" Count of run unit tests: %4d\n", test_stat_run_units__);
printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__);
printf(" Count of skipped unit tests: %4d\n", (int) test_list_size__ - test_stat_run_units__);
- printf(" ");
}
if(test_stat_failed_units__ == 0) {
@@ -839,16 +1538,42 @@ main(int argc, char** argv)
printf(" All unit tests have passed.\n");
} else {
test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED:");
- printf(" %d of %d unit tests have failed.\n",
- test_stat_failed_units__, test_stat_run_units__);
+ printf(" %d of %d unit tests %s failed.\n",
+ test_stat_failed_units__, test_stat_run_units__,
+ (test_stat_failed_units__ == 1) ? "has" : "have");
}
if(test_verbose_level__ >= 3)
printf("\n");
}
- free((void*) tests__);
- free((void*) test_flags__);
+ if (test_xml_output__) {
+#if defined ACUTEST_UNIX__
+ char *suite_name = basename(argv[0]);
+#elif defined ACUTEST_WIN__
+ char suite_name[_MAX_FNAME];
+ _splitpath(argv[0], NULL, NULL, suite_name, NULL);
+#else
+ const char *suite_name = argv[0];
+#endif
+ fprintf(test_xml_output__, "\n");
+ fprintf(test_xml_output__, "\n",
+ suite_name, (int)test_list_size__, test_stat_failed_units__, test_stat_failed_units__,
+ (int)test_list_size__ - test_stat_run_units__);
+ for(i = 0; test_list__[i].func != NULL; i++) {
+ struct test_detail__ *details = &test_details__[i];
+ fprintf(test_xml_output__, " \n", test_list__[i].name, details->duration);
+ if (details->flags & TEST_FLAG_FAILURE__)
+ fprintf(test_xml_output__, " \n");
+ if (!(details->flags & TEST_FLAG_FAILURE__) && !(details->flags & TEST_FLAG_SUCCESS__))
+ fprintf(test_xml_output__, " \n");
+ fprintf(test_xml_output__, " \n");
+ }
+ fprintf(test_xml_output__, " \n");
+ fclose(test_xml_output__);
+ }
+
+ free((void*) test_details__);
return (test_stat_failed_units__ == 0) ? 0 : 1;
}
@@ -857,8 +1582,8 @@ main(int argc, char** argv)
#endif /* #ifndef TEST_NO_MAIN */
#ifdef __cplusplus
-} /* extern "C" */
+ } /* extern "C" */
#endif
-#endif /* #ifndef ACUTEST_H__ */
\ No newline at end of file
+#endif /* #ifndef ACUTEST_H__ */
diff --git a/test/tinyobj_api_tests.c b/test/tinyobj_api_tests.c
index 1fda159..c182ed2 100644
--- a/test/tinyobj_api_tests.c
+++ b/test/tinyobj_api_tests.c
@@ -4,45 +4,31 @@
#include "tinyobj_loader_c.h"
#include "acutest.h"
-void test_tinyobj_attrib_init(void)
-{
- tinyobj_attrib_t attrib;
- tinyobj_attrib_init(&attrib);
-
- TEST_CHECK(attrib.vertices == NULL);
- TEST_CHECK(attrib.num_vertices == 0);
- TEST_CHECK(attrib.normals == NULL);
- TEST_CHECK(attrib.num_normals == 0);
- TEST_CHECK(attrib.texcoords == NULL);
- TEST_CHECK(attrib.num_texcoords == 0);
- TEST_CHECK(attrib.faces == NULL);
- TEST_CHECK(attrib.num_faces == 0);
- TEST_CHECK(attrib.face_num_verts == NULL);
- TEST_CHECK(attrib.num_face_num_verts == 0);
- TEST_CHECK(attrib.material_ids == NULL);
-}
-
-size_t loadFile(const char * filename, char ** buffer)
-{
- *buffer = NULL;
- long string_size = 0, read_size = 0;
- FILE * handler = fopen(filename, "r");
-
- if (handler) {
- fseek(handler, 0, SEEK_END);
- string_size = ftell(handler);
- rewind(handler);
- *buffer = (char *) malloc(sizeof(char) * (string_size + 1));
- read_size = fread(*buffer, sizeof(char), (size_t) string_size, handler);
- (*buffer)[string_size] = '\0';
- if (string_size != read_size) {
- free(buffer);
- *buffer = NULL;
- }
- fclose(handler);
+size_t loadFile(const char *filename, char **buffer) {
+ long string_size = 0, read_size = 0;
+ FILE *handler = fopen(filename, "r");
+ *buffer = NULL;
+
+ if (handler) {
+ if (fseek(handler, 0, SEEK_END) != 0) {
+ fprintf(stderr, "loadFile: fseek %s failure (errno %d)\n", filename,
+ errno);
+ return 0;
}
-
- return (size_t) read_size;
+ string_size = ftell(handler);
+ rewind(handler);
+ *buffer = (char *)malloc(sizeof(char) * (string_size + 1));
+ read_size = (long)fread(*buffer, sizeof(char), string_size, handler);
+ (*buffer)[string_size] = '\0';
+ if (string_size != read_size && errno) {
+ fprintf(stderr, "loadFile: fread %s failure (errno %d)\n", filename,
+ errno);
+ free(*buffer);
+ *buffer = NULL;
+ }
+ fclose(handler);
+ }
+ return (size_t)read_size;
}
void test_tinyobj_parse_mtl_file(void)
@@ -51,67 +37,175 @@ void test_tinyobj_parse_mtl_file(void)
const char * filename = "fixtures/cube.mtl";
tinyobj_material_t * material;
- size_t num_materials;
+ unsigned int num_materials;
+ int ret;
- TEST_CHECK(tinyobj_parse_mtl_file(&material, &num_materials, filename) == TINYOBJ_SUCCESS);
+ ret = tinyobj_parse_mtl_file(&material, &num_materials, filename);
+ TEST_CHECK(ret == TINYOBJ_SUCCESS);
+ if(ret != TINYOBJ_SUCCESS)
+ return;
TEST_CHECK(num_materials == 1);
- TEST_CHECK(strcmp(material->name, "CubeMaterial") == 0);
+ TEST_MSG("num_materials %ld\n", num_materials);
+ TEST_CHECK(strcmp(material[0].name, "CubeMaterial") == 0);
+ TEST_MSG("material name: %s\n", material[0].name);
- TEST_CHECK(material->diffuse[0] == 1.0);
- TEST_CHECK(material->diffuse[1] == 0.0);
- TEST_CHECK(material->diffuse[2] == 0.0);
+ TEST_CHECK(material[0].diffuse.r == 1.0);
+ TEST_CHECK(material[0].diffuse.g == 0.0);
+ TEST_CHECK(material[0].diffuse.b == 0.0);
- TEST_CHECK(material->specular[0] == 0.5);
- TEST_CHECK(material->specular[1] == 0.25);
- TEST_CHECK(material->specular[2] == 0.125);
+ TEST_CHECK(material[0].specular.r == 0.5);
+ TEST_CHECK(material[0].specular.g == 0.25);
+ TEST_CHECK(material[0].specular.b == 0.125);
- TEST_CHECK(material->ambient[0] == 1.0);
- TEST_CHECK(material->ambient[1] == 1.0);
- TEST_CHECK(material->ambient[2] == 1.0);
+ TEST_CHECK(material[0].ambient.r == 1.0);
+ TEST_CHECK(material[0].ambient.g == 1.0);
+ TEST_CHECK(material[0].ambient.b == 1.0);
- TEST_CHECK(material->emission[0] == 0.0);
- TEST_CHECK(material->emission[1] == 1.0);
- TEST_CHECK(material->emission[2] == 0.0);
+ TEST_CHECK(material[0].emission.r == 0.0);
+ TEST_CHECK(material[0].emission.g == 1.0);
+ TEST_CHECK(material[0].emission.b == 0.0);
- TEST_CHECK(material->illum == 2);
- TEST_CHECK(material->dissolve == 1.0);
+ TEST_CHECK(material[0].illum == 2);
+ TEST_CHECK(material[0].dissolve == 1.0);
}
}
-void test_tinyobj_parse_obj(void)
-{
- const char * filename = "fixtures/cube.obj";
-
- tinyobj_shape_t * shape = NULL;
- tinyobj_material_t * material = NULL;
- tinyobj_attrib_t attrib;
-
- unsigned long num_shapes;
- unsigned long num_materials;
-
- tinyobj_attrib_init(&attrib);
-
- char * obj_contents;
- size_t file_size = loadFile(filename, &obj_contents);
-
- int result = tinyobj_parse_obj(&attrib, &shape, &num_shapes, &material, &num_materials, obj_contents, file_size, TINYOBJ_FLAG_TRIANGULATE);
-
- TEST_CHECK(result == TINYOBJ_SUCCESS);
+tinyobj_vertex_normal_t vertex_normal_test_vector[] = {
+ { 0.0f, -1.0f, 0.0f}, // vn1
+ { 0.0f, 1.0f, 0.0f}, // vn2
+ { 1.0f, 0.0f, 0.0f}, // vn3
+ {-0.0f, -0.0f, 1.0f}, // vn4
+ {-1.0f, -0.0f, -0.0f}, // vn5
+ { 0.0f, 0.0f, -1.0f}, // vn6
+};
+
+tinyobj_vertex_t vertex_test_vector[] = {
+ { 1.0f, -1.0f, -1.0f, 1.0f}, // v1
+ { 1.0f, -1.0f, 1.0f, 1.0f}, // v2
+ {-1.0f, -1.0f, 1.0f, 1.0f}, // v3
+ {-1.0f, -1.0f, -1.0f, 1.0f}, // v4
+ { 1.0f, 1.0f, -0.999999f, 1.0f}, // v5
+ { 0.999999f, 1.0f, 1.000001f, 1.0f}, // v6
+ {-1.0f, 1.0f, 1.0f, 1.0f}, // v7
+ {-1.0f, 1.0f, -1.0f, 1.0f}, // v8
+};
+
+/**
+ * Test vector containing the triplets of all faces (without triangulation)
+ **/
+tinyobj_vertex_index_t vi_test_vector[] = {
+ // Face 1
+ {1, TINYOBJ_INVALID_INDEX, 1}, {2, TINYOBJ_INVALID_INDEX, 1},
+ {3, TINYOBJ_INVALID_INDEX, 1}, {4, TINYOBJ_INVALID_INDEX, 1},
+ // Face 2
+ {5, TINYOBJ_INVALID_INDEX, 2}, {8, TINYOBJ_INVALID_INDEX, 2},
+ {7, TINYOBJ_INVALID_INDEX, 2}, {6, TINYOBJ_INVALID_INDEX, 2},
+ // Face 3
+ {1, TINYOBJ_INVALID_INDEX, 3}, {5, TINYOBJ_INVALID_INDEX, 3},
+ {6, TINYOBJ_INVALID_INDEX, 3}, {2, TINYOBJ_INVALID_INDEX, 3},
+ // Face 4
+ {2, TINYOBJ_INVALID_INDEX, 4}, {6, TINYOBJ_INVALID_INDEX, 4},
+ {7, TINYOBJ_INVALID_INDEX, 4}, {3, TINYOBJ_INVALID_INDEX, 4},
+ // Face 5
+ {3, TINYOBJ_INVALID_INDEX, 5}, {7, TINYOBJ_INVALID_INDEX, 5},
+ {8, TINYOBJ_INVALID_INDEX, 5}, {4, TINYOBJ_INVALID_INDEX, 5},
+ // Face 6
+ {5, TINYOBJ_INVALID_INDEX, 6}, {1, TINYOBJ_INVALID_INDEX, 6},
+ {4, TINYOBJ_INVALID_INDEX, 6}, {8, TINYOBJ_INVALID_INDEX, 6},
+};
+
+/**
+ * Test vector containing the triplets of all faces (triangulated)
+ * (already fixed)
+ **/
+tinyobj_vertex_index_t vi_test_vector_trig[] = {
+ // Face 1
+ {0, TINYOBJ_INVALID_INDEX, 0}, {1, TINYOBJ_INVALID_INDEX, 0}, {2, TINYOBJ_INVALID_INDEX, 0},
+ {0, TINYOBJ_INVALID_INDEX, 0}, {2, TINYOBJ_INVALID_INDEX, 0}, {3, TINYOBJ_INVALID_INDEX, 0},
+ // Face 2
+ {4, TINYOBJ_INVALID_INDEX, 1}, {7, TINYOBJ_INVALID_INDEX, 1}, {6, TINYOBJ_INVALID_INDEX, 1},
+ {4, TINYOBJ_INVALID_INDEX, 1}, {6, TINYOBJ_INVALID_INDEX, 1}, {5, TINYOBJ_INVALID_INDEX, 1},
+ // Face 3
+ {0, TINYOBJ_INVALID_INDEX, 2}, {4, TINYOBJ_INVALID_INDEX, 2}, {5, TINYOBJ_INVALID_INDEX, 2},
+ {0, TINYOBJ_INVALID_INDEX, 2}, {5, TINYOBJ_INVALID_INDEX, 2}, {1, TINYOBJ_INVALID_INDEX, 2},
+ // Face 4
+ {1, TINYOBJ_INVALID_INDEX, 3}, {5, TINYOBJ_INVALID_INDEX, 3}, {6, TINYOBJ_INVALID_INDEX, 3},
+ {1, TINYOBJ_INVALID_INDEX, 3}, {6, TINYOBJ_INVALID_INDEX, 3}, {2, TINYOBJ_INVALID_INDEX, 3},
+ // Face 5
+ {2, TINYOBJ_INVALID_INDEX, 4}, {6, TINYOBJ_INVALID_INDEX, 4}, {7, TINYOBJ_INVALID_INDEX, 4},
+ {2, TINYOBJ_INVALID_INDEX, 4}, {7, TINYOBJ_INVALID_INDEX, 4}, {3, TINYOBJ_INVALID_INDEX, 4},
+ // Face 6
+ {4, TINYOBJ_INVALID_INDEX, 5}, {0, TINYOBJ_INVALID_INDEX, 5}, {3, TINYOBJ_INVALID_INDEX, 5},
+ {4, TINYOBJ_INVALID_INDEX, 5}, {3, TINYOBJ_INVALID_INDEX, 5}, {7, TINYOBJ_INVALID_INDEX, 5},
+};
+
+unsigned char test_compare_vertex_texture(tinyobj_vertex_texture_t *vt, tinyobj_vertex_texture_t *vt1) {
+ return (vt->u == vt1->u && vt->v == vt1->v && vt->w == vt1->w);
+}
- // TODO: should these two values be swapped?
- TEST_CHECK(attrib.num_faces == 36);
- TEST_CHECK(attrib.num_face_num_verts == 12);
+unsigned char test_compare_vertex_normal(tinyobj_vertex_normal_t *vn, tinyobj_vertex_normal_t *vn1) {
+ return (vn->i == vn1->i && vn->j == vn1->j && vn->k == vn1->k);
+}
- TEST_CHECK(attrib.num_normals == 6);
- TEST_CHECK(attrib.num_vertices == 8);
- TEST_CHECK(attrib.num_texcoords == 0);
+unsigned char test_compare_vertex(tinyobj_vertex_t *v, tinyobj_vertex_t *v1) {
+ return (v->x == v1->x && v->y == v1->y && v->z == v1->z && v->weight == v1->weight);
+}
- tinyobj_attrib_free(&attrib);
- if (shape) {
- tinyobj_shapes_free(shape, num_shapes);
- }
- if (material) {
- tinyobj_materials_free(material, num_materials);
- }
+/**
+ * Tests .obj parsing function
+ **/
+void test_tinyobj_parse_obj(void) {
+ const char * filename = "fixtures/cube.obj";
+ tinyobj_shape_t * shape = NULL;
+ tinyobj_material_t * material = NULL;
+ tinyobj_attrib_t attrib;
+
+ unsigned int num_shapes;
+ unsigned int num_materials;
+
+ char * obj_contents;
+ size_t file_size = loadFile(filename, &obj_contents);
+
+ int result = tinyobj_parse_obj(&attrib, &shape, &num_shapes, &material, &num_materials, obj_contents, file_size, TINYOBJ_FLAG_TRIANGULATE);
+ TEST_CHECK(result == TINYOBJ_SUCCESS);
+ if(result != TINYOBJ_SUCCESS)
+ return;
+
+ // Face tests
+ TEST_CHECK(attrib.f_count == (sizeof(vi_test_vector)/sizeof(vi_test_vector[0]))/4);
+ { // Test if vertex indices match
+ size_t i = 0;
+ for(i = 0; i < attrib.f_count; i++) {
+ size_t j = 0;
+ TEST_CHECK(attrib.f[i].count == 6);
+ TEST_CHECK(attrib.f[i].smoothing_id == 0);
+
+ for(j = 0; j < attrib.f[i].count; j++) {
+ size_t tpos = attrib.f[i].count*i+j;
+ tinyobj_vertex_index_t vi;
+ memcpy(&vi, &attrib.f[i].triplet_list[j], sizeof(vi));
+ TEST_CASE("Triplet indices match test");
+ TEST_CHECK(vi.v_idx == vi_test_vector_trig[tpos].v_idx);
+ TEST_CHECK(vi.vt_idx == vi_test_vector_trig[tpos].vt_idx
+ || vi.vt_idx == TINYOBJ_INVALID_INDEX && vi_test_vector_trig[tpos].vt_idx == TINYOBJ_INVALID_INDEX);
+ TEST_CHECK(vi.vn_idx == vi_test_vector_trig[tpos].vn_idx
+ || vi.vn_idx == TINYOBJ_INVALID_INDEX && vi_test_vector_trig[tpos].vn_idx == TINYOBJ_INVALID_INDEX);
+ TEST_CASE("Triplet values match test");
+ TEST_CHECK(vi.v_idx < attrib.v_count);
+ TEST_CHECK(test_compare_vertex(&attrib.v[vi.v_idx], &vertex_test_vector[vi.v_idx]));
+ TEST_CHECK(vi.vn_idx < attrib.vn_count);
+ TEST_CHECK(test_compare_vertex_normal(&attrib.vn[vi.vn_idx], &vertex_normal_test_vector[vi.vn_idx]));
+ }
+ }
+ }
+ TEST_CASE(NULL);
+
+ TEST_CHECK(attrib.v_count == 8);
+ TEST_CHECK(attrib.vn_count == 6);
+ TEST_CHECK(attrib.vt_count == 0);
+
+ tinyobj_attrib_free(&attrib);
+ tinyobj_shape_free(shape, num_shapes);
+ tinyobj_material_free(material, num_materials);
}
diff --git a/test/tinyobj_api_tests.h b/test/tinyobj_api_tests.h
index 96726b7..e96e363 100644
--- a/test/tinyobj_api_tests.h
+++ b/test/tinyobj_api_tests.h
@@ -1,7 +1,6 @@
#ifndef TINYOBJ_API_TESTS
#define TINYOBJ_API_TESTS
-void test_tinyobj_attrib_init(void);
void test_tinyobj_parse_mtl_file(void);
void test_tinyobj_parse_obj(void);
diff --git a/test/tinyobj_internal_tests.c b/test/tinyobj_internal_tests.c
index a007fa9..31e8f9c 100644
--- a/test/tinyobj_internal_tests.c
+++ b/test/tinyobj_internal_tests.c
@@ -101,54 +101,48 @@ void test_length_until_newline(void)
// Return value for null-terminated string without line breaks should be
// number of non-null chars.
char test_string[] = "potato";
- TEST_CHECK(length_until_newline(test_string, sizeof(test_string)) == 6);
+ TEST_CHECK(length_until_newline_comment_space(
+ test_string, sizeof(test_string)) == 6);
}
{
// Return value for null terminated string with a linebreak at the end should
// be number of chars to newline.
char test_string[] = "potato\n";
- TEST_CHECK(length_until_newline(test_string, sizeof(test_string)) == 6);
+ TEST_CHECK(length_until_newline_comment_space(
+ test_string, sizeof(test_string)) == 6);
}
{
// Return value for null-terminated string with a linebreak in the middle
// should be number of chars to newline.
char test_string[] = "potato\nmonkey";
- TEST_CHECK(length_until_newline(test_string, sizeof(test_string)) == 6);
+ TEST_CHECK(length_until_newline_comment_space(
+ test_string, sizeof(test_string)) == 6);
}
{
// Return value for null-terminated string with a linebreak in the middle and
// at the end should be number of chars to first newline.
char test_string[] = "potato\nmonkey\n";
- TEST_CHECK(length_until_newline(test_string, sizeof(test_string)) == 6);
+ TEST_CHECK(length_until_newline_comment_space(
+ test_string, sizeof(test_string)) == 6);
}
{
// Return value for empty null-terminated string should be 0.
char test_string[] = "";
- TEST_CHECK(length_until_newline(test_string, sizeof(test_string)) == 0);
+ TEST_CHECK(length_until_newline_comment_space(
+ test_string, sizeof(test_string)) == 0);
}
- {
- // Return value for null-terminated string with a null character in the middle
- // should be the number of non-null chars plus the number of null characters
- // before the end of the string.
- char test_string[] = "pot\0ato";
- TEST_CHECK(length_until_newline(test_string, sizeof(test_string)) == 7);
- }
-}
-
-void test_my_atoi(void)
-{
- // Results for input strings should become corresponding ints.
- TEST_CHECK(my_atoi("1") == 1);
- TEST_CHECK(my_atoi("-1") == -1);
- TEST_CHECK(my_atoi("+1") == 1);
- TEST_CHECK(my_atoi("0") == 0);
- TEST_CHECK(my_atoi("-0") == 0);
- TEST_CHECK(my_atoi("+0") == 0);
+// {
+// // Return value for null-terminated string with a null character in the middle
+// // should be the number of non-null chars plus the number of null characters
+// // before the end of the string.
+// char test_string[] = "pot\0ato";
+// TEST_CHECK(length_until_newline(test_string, sizeof(test_string)) == 7);
+// }
}
void test_fix_index(void)
@@ -637,7 +631,7 @@ void test_my_strdup(void)
// Return value for a regular string should be a new string whose
// contents are equal to the original.
char * test_string = "potato";
- char * result = my_strdup(test_string, strlen(test_string));
+ char * result = strdup_ml(test_string, strlen(test_string));
TEST_CHECK(test_string != result);
TEST_CHECK(strcmp(test_string, result) == 0);
free(result);
@@ -646,7 +640,7 @@ void test_my_strdup(void)
{
// Return value for empty string should be a new empty string.
char * test_string = "";
- char * result = my_strdup(test_string, strlen(test_string));
+ char* result = strdup_ml(test_string, strlen(test_string));
TEST_CHECK(test_string != result);
TEST_CHECK(strcmp(test_string, result) == 0);
free(result);
@@ -659,7 +653,7 @@ void test_my_strndup(void)
// Return value for a regular string and the length of that string should be a new
// string whose contents are equal to the original.
char * test_string = "potato";
- char * result = my_strndup(test_string, 6);
+ char * result = strndup(test_string, 6);
TEST_CHECK(test_string != result);
TEST_CHECK(strcmp(test_string, result) == 0);
free(result);
@@ -668,54 +662,28 @@ void test_my_strndup(void)
{
// Return value for a regular string and zero should be a new empty string.
char * test_string = "potato";
- char * result = my_strndup(test_string, 0);
+ char * result = strndup(test_string, 0);
TEST_CHECK(result == 0);
}
}
-void test_initMaterial(void)
-{
- // Initialised material should have expected defaults.
- int i;
- tinyobj_material_t material;
- initMaterial(&material);
- TEST_CHECK(material.name == NULL);
- TEST_CHECK(material.ambient_texname == NULL);
- TEST_CHECK(material.diffuse_texname == NULL);
- TEST_CHECK(material.specular_texname == NULL);
- TEST_CHECK(material.specular_highlight_texname == NULL);
- TEST_CHECK(material.bump_texname == NULL);
- TEST_CHECK(material.displacement_texname == NULL);
- TEST_CHECK(material.alpha_texname == NULL);
- for (i = 0; i < 3; i++) {
- TEST_CHECK(material.ambient[i] == 0.f);
- TEST_CHECK(material.diffuse[i] == 0.f);
- TEST_CHECK(material.specular[i] == 0.f);
- TEST_CHECK(material.transmittance[i] == 0.f);
- TEST_CHECK(material.emission[i] == 0.f);
- }
- TEST_CHECK(material.illum == 0);
- TEST_CHECK(material.dissolve == 1.f);
- TEST_CHECK(material.shininess == 1.f);
- TEST_CHECK(material.ior == 1.f);
-}
-
+#ifndef TINYOBJ_USE_UTHASH
void test_create_hash_table(void)
{
{
// Initialised hash table should be initialised with a default capacity.
- hash_table_t table;
+ tinyobj_material_table_t table;
create_hash_table(0, &table);
TEST_CHECK(table.hashes != NULL);
TEST_CHECK(table.entries != NULL);
- TEST_CHECK(table.capacity == HASH_TABLE_DEFAULT_SIZE);
+ TEST_CHECK(table.capacity == TINYOBJ_HASH_TABLE_DEFAULT_SIZE);
TEST_CHECK(table.n == 0);
destroy_hash_table(&table);
}
{
// Initialised hash table should be initialised with supplied capacity.
- hash_table_t table;
+ tinyobj_material_table_t table;
create_hash_table(20, &table);
TEST_CHECK(table.hashes != NULL);
TEST_CHECK(table.entries != NULL);
@@ -729,7 +697,7 @@ void test_hash_table_set(void)
{
{
// Values should be stored in the hash table.
- hash_table_t table;
+ tinyobj_material_table_t table;
int foundFirst = 0;
int foundSecond = 0;
int foundOther = 0;
@@ -768,7 +736,7 @@ void test_hash_table_set(void)
{
// Values with different hashes but same % capacity value should exist in the
// the same hash table.
- hash_table_t table;
+ tinyobj_material_table_t table;
int foundFirst = 0;
int foundSecond = 0;
int foundOther = 0;
@@ -812,7 +780,7 @@ void test_hash_table_set(void)
// TODO: This fails. Should the key also be stored in the entry so it can be compared
// with the key being inserted?
// Values with identical hashes but different keys should exist in the same hash table.
- hash_table_t table;
+ tinyobj_material_table_t table;
int foundFirst = 0;
int foundSecond = 0;
int foundOther = 0;
@@ -855,7 +823,7 @@ void test_hash_table_set(void)
void test_hash_table_exists(void)
{
// It should be possible to test for the presence of items in the hash table.
- hash_table_t table;
+ tinyobj_material_table_t table;
create_hash_table(20, &table);
hash_table_set("potato", 3, &table);
@@ -871,7 +839,7 @@ void test_hash_table_exists(void)
void test_hash_table_get(void)
{
// It should be possible to retrieve item values from the hash table.
- hash_table_t table;
+ tinyobj_material_table_t table;
create_hash_table(20, &table);
hash_table_set("potato", 3, &table);
@@ -890,7 +858,7 @@ void test_hash_table_get(void)
void test_hash_table_maybe_grow(void)
{
// It should be possible for the table to grow, preserving all previous values
- hash_table_t table;
+ tinyobj_material_table_t table;
create_hash_table(10, &table);
hash_table_set("Pottery_clay0", 0, &table);
@@ -922,3 +890,5 @@ void test_hash_table_maybe_grow(void)
destroy_hash_table(&table);
}
+
+#endif // TINYOBJ_USE_UTHASH
diff --git a/test/tinyobj_internal_tests.h b/test/tinyobj_internal_tests.h
index 01f7098..f2d5d3d 100644
--- a/test/tinyobj_internal_tests.h
+++ b/test/tinyobj_internal_tests.h
@@ -5,7 +5,6 @@ void test_skip_space(void);
void test_skip_space_and_cr(void);
void test_until_space(void);
void test_length_until_newline(void);
-void test_my_atoi(void);
void test_fix_index(void);
void test_parseRawTriple(void);
void test_parseInt(void);
@@ -15,7 +14,6 @@ void test_parseFloat2(void);
void test_parseFloat3(void);
void test_my_strdup(void);
void test_my_strndup(void);
-void test_initMaterial(void);
void test_create_hash_table(void);
void test_hash_table_set(void);
void test_hash_table_exists(void);
diff --git a/test/tinyobj_regression_tests.c b/test/tinyobj_regression_tests.c
index 5a9d50c..1a6bcf8 100644
--- a/test/tinyobj_regression_tests.c
+++ b/test/tinyobj_regression_tests.c
@@ -16,27 +16,31 @@ static int float_equals(float x, float y)
return 0;
}
-static size_t loadFile(const char * filename, char ** buffer)
-{
- *buffer = NULL;
- long string_size = 0, read_size = 0;
- FILE * handler = fopen(filename, "r");
-
- if (handler) {
- fseek(handler, 0, SEEK_END);
- string_size = ftell(handler);
- rewind(handler);
- *buffer = (char *) malloc(sizeof(char) * (string_size + 1));
- read_size = fread(*buffer, sizeof(char), (size_t) string_size, handler);
- (*buffer)[string_size] = '\0';
- if (string_size != read_size) {
- free(buffer);
- *buffer = NULL;
- }
- fclose(handler);
+size_t loadFile_d(const char *filename, char **buffer) {
+ long string_size = 0, read_size = 0;
+ FILE *handler = fopen(filename, "r");
+ *buffer = NULL;
+
+ if (handler) {
+ if (fseek(handler, 0, SEEK_END) != 0) {
+ fprintf(stderr, "loadFile: fseek %s failure (errno %d)\n", filename,
+ errno);
+ return 0;
}
-
- return (size_t) read_size;
+ string_size = ftell(handler);
+ rewind(handler);
+ *buffer = (char *)malloc(sizeof(char) * (string_size + 1));
+ read_size = (long)fread(*buffer, sizeof(char), string_size, handler);
+ (*buffer)[string_size] = '\0';
+ if (string_size != read_size && errno) {
+ fprintf(stderr, "loadFile: fread %s failure (errno %d)\n", filename,
+ errno);
+ free(*buffer);
+ *buffer = NULL;
+ }
+ fclose(handler);
+ }
+ return (size_t)read_size;
}
void test_tinyobj_crlf_string(void)
@@ -45,9 +49,13 @@ void test_tinyobj_crlf_string(void)
const char * filename = "fixtures/texname-crlf.mtl";
tinyobj_material_t * material;
- size_t num_materials;
+ unsigned int num_materials;
+ int ret;
- TEST_CHECK(tinyobj_parse_mtl_file(&material, &num_materials, filename) == TINYOBJ_SUCCESS);
+ ret = tinyobj_parse_mtl_file(&material, &num_materials, filename);
+ TEST_CHECK(ret == TINYOBJ_SUCCESS);
+ if(ret != TINYOBJ_SUCCESS)
+ return;
TEST_CHECK(num_materials == 1);
TEST_CHECK(strcmp(material->name, "CubeMaterial") == 0);
@@ -68,21 +76,17 @@ void test_tinyobj_negative_exponent(void)
unsigned long num_shapes;
unsigned long num_materials;
- tinyobj_attrib_init(&attrib);
-
char * obj_contents;
- size_t file_size = loadFile(filename, &obj_contents);
+ size_t file_size = loadFile_d(filename, &obj_contents);
int result = tinyobj_parse_obj(&attrib, &shape, &num_shapes, &material, &num_materials, obj_contents, file_size, TINYOBJ_FLAG_TRIANGULATE);
TEST_CHECK(result == TINYOBJ_SUCCESS);
+ if(result != TINYOBJ_SUCCESS)
+ return;
- // TODO: should these two values be swapped?
- TEST_CHECK(attrib.num_faces == 36);
- TEST_CHECK(attrib.num_face_num_verts == 12);
-
- TEST_CHECK(float_equals(attrib.vertices[0], 2.0e+5f));
- TEST_CHECK(float_equals(attrib.vertices[1], 2.0e-5f));
- TEST_CHECK(float_equals(attrib.vertices[2], 2.0e-0f));
+ TEST_CHECK(float_equals(attrib.v[0].x, 2.0e+5f));
+ TEST_CHECK(float_equals(attrib.v[0].y, 2.0e-5f));
+ TEST_CHECK(float_equals(attrib.v[0].z, 2.0e-0f));
}
}
diff --git a/test/tinyobj_tests.c b/test/tinyobj_tests.c
index 0137ed1..035b191 100644
--- a/test/tinyobj_tests.c
+++ b/test/tinyobj_tests.c
@@ -6,7 +6,6 @@
TEST_LIST = {
// API Tests
- { "tinyobj_attrib_init", test_tinyobj_attrib_init },
{ "tinyobj_parse_mtl_file", test_tinyobj_parse_mtl_file },
{ "tinyobj_parse_obj", test_tinyobj_parse_obj },
@@ -15,7 +14,6 @@ TEST_LIST = {
{ "skip_space_and_cr", test_skip_space_and_cr },
{ "until_space", test_until_space },
{ "length_until_newline", test_length_until_newline },
- { "my_atoi", test_my_atoi },
{ "fix_index", test_fix_index },
{ "parseRawTriple", test_parseRawTriple },
{ "parseInt", test_parseInt },
@@ -25,12 +23,13 @@ TEST_LIST = {
{ "parseFloat3", test_parseFloat3 },
{ "my_strdup", test_my_strdup },
{ "my_strndup", test_my_strndup },
- { "initMaterial", test_initMaterial },
+#ifdef TINYOBJ_USE_UTHASH
{ "create_hash_table", test_create_hash_table },
{ "hash_table_set", test_hash_table_set },
{ "hash_table_exists", test_hash_table_exists },
{ "hash_table_get", test_hash_table_get },
{ "hash_table_maybe_grow", test_hash_table_maybe_grow },
+#endif // TINYOBJ_USE_UTHASH
{ "crlf_string", test_tinyobj_crlf_string },
{ "negative_exponent_issue26", test_tinyobj_negative_exponent },
diff --git a/tinyobj_loader_c.h b/tinyobj_loader_c.h
index 30ff22e..ebafa1b 100644
--- a/tinyobj_loader_c.h
+++ b/tinyobj_loader_c.h
@@ -1,47 +1,343 @@
-/*
- The MIT License (MIT)
-
- Copyright (c) 2016 - 2019 Syoyo Fujita and many contributors.
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
+/* About: License
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2019 Syoyo Fujita and many contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#ifndef TINOBJ_LOADER_C_H_
#define TINOBJ_LOADER_C_H_
/* @todo { Remove stddef dependency. size_t? } */
#include
+/* Constant: TINYOBJ_LOADER_C_IMPLEMENTATION
+ * Constant used to mark the .c file that holds the implementation of tinyobj
+ *
+ * Only *one* file should have this definition!
+ */
+
+/* Constant: TINYOBJ_USE_UTHASH
+ * Use hashtable implementation
+ * instead of tinyobj's implementation
+ */
+//#define TINYOBJ_USE_UTHASH
+
+/* Constant: TINYOBJ_ENABLE_OLDER_ATTRIBUTE
+ *
+ * Enable use of older attribute type for objects (deprecated)
+ *
+ * See:
+ * -
+ * -
+ * -
+ */
+#define TINYOBJ_ENABLE_OLDER_ATTRIBUTE
+
+/* Constant: TINYOBJ_FLAG_TRIANGULATE
+ *
+ * Should the faces be triangulated upon loading
+ * When triangulating we parse the vertices as if they were part of
+ * a triangle fan, so every three indices the next is always the first
+ * triplet of this face
+ *
+ * See:
+ *
+ */
+#define TINYOBJ_FLAG_TRIANGULATE (1 << 0)
+
+/* Constant: TINYOBJ_INVALID_INDEX
+ *
+ * Invalid vertex index used when referencing vertices in couples/tuples
+ *
+ * See:
+ * -
+ * -
+ * -
+ */
+#define TINYOBJ_INVALID_INDEX (0x80000000)
+
+/* Constants: Return codes
+ *
+ * TINYOBJ_NO_COMMAND - No command
+ * TINYOBJ_SUCCESS - Success
+ * TINYOBJ_ERROR_NOT_SET - Default error
+ * TINYOBJ_ERROR_MEMORY - Memory failure
+ * TINYOBJ_ERROR_EMPTY - Empty file
+ * TINYOBJ_ERROR_FILE_OPERATION - Failed file operation
+ * TINYOBJ_ERROR_INVALID_PARAMETER - Invalid parameter (function)
+ * TINYOBJ_ERROR_UNKNOWN_PARAMETER - Unknown parameter for object/material
+ * TINYOBJ_ERROR_MALFORMED_PARAMETER - Malformed parameter for object/material
+ */
+#define TINYOBJ_NO_COMMAND (1)
+#define TINYOBJ_SUCCESS (0)
+#define TINYOBJ_ERROR_NOT_SET (-1)
+#define TINYOBJ_ERROR_MEMORY (-2)
+#define TINYOBJ_ERROR_EMPTY (-3)
+#define TINYOBJ_ERROR_FILE_OPERATION (-4)
+#define TINYOBJ_ERROR_INVALID_PARAMETER (-5)
+#define TINYOBJ_ERROR_UNKNOWN_PARAMETER (-6)
+#define TINYOBJ_ERROR_MALFORMED_PARAMETER (-7)
+
+/* Constants: Dynamic array basic parameters
+ *
+ * Initial array lengths and growth factors
+ *
+ * TINYOBJ_POINT_INITIAL_COUNT - Point array initial length
+ * TINYOBJ_POINT_GROWTH_FACTOR - Point array growth factor
+ * TINYOBJ_COUPLE_INITIAL_COUNT - Couple (line) array initial length
+ * TINYOBJ_COUPLE_GROWTH_FACTOR - Couple (line) array growth factor
+ * TINYOBJ_TRIPLET_INITIAL_COUNT - Triplet (face) array initial length
+ * TINYOBJ_TRIPLET_GROWTH_FACTOR - Triplet (face) array growth factor
+ * TINYOBJ_MATERIAL_INITIAL_COUNT - Material array initial length
+ * TINYOBJ_MATERIAL_GROWTH_FACTOR - Material array growth factor
+ */
+#define TINYOBJ_POINT_INITIAL_COUNT (16)
+#define TINYOBJ_POINT_GROWTH_FACTOR (2)
+#define TINYOBJ_COUPLE_INITIAL_COUNT (16)
+#define TINYOBJ_COUPLE_GROWTH_FACTOR (2)
+#define TINYOBJ_TRIPLET_INITIAL_COUNT (16)
+#define TINYOBJ_TRIPLET_GROWTH_FACTOR (2)
+#define TINYOBJ_MATERIAL_INITIAL_COUNT (2)
+#define TINYOBJ_MATERIAL_GROWTH_FACTOR (2)
+
+/***********************************************************************************************
+ * Group: Vertex data
+ ***********************************************************************************************/
+
+/* Structure: tinyobj_vertex_t
+ *
+ * Geometric vertex (v)
+ * Specifies a geometric vertex and its x y z coordinates. Rational
+ * curves and surfaces require a fourth homogeneous coordinate, also
+ * called the weight.
+ * > v x y z w
+ *
+ * Fields:
+ *
+ * x - x coordinate
+ * y - y coordinate
+ * z - z coordinate
+ * weight - (Rational curves/surfaces) Weight (default: 1.0f)
+ */
+typedef struct s_tinyobj_vertex {
+ float x, y, z; //< Coordinates
+ float weight; //< (Rational curves/surfaces) Weight (default: 1.0f)
+} tinyobj_vertex_t;
+
+/* Structure: tinyobj_vertex_normal_t
+ *
+ * Vertex normal (vn)
+ * Specifies a normal vector with components i, j, and k
+ * > vn i j k
+ *
+ * Fields:
+ *
+ * i - i coordinate
+ * j - j coordinate
+ * k - k coordinate
+ */
+typedef struct s_tinyobj_vertex_normal {
+ float i, j, k; //< Coordinates
+} tinyobj_vertex_normal_t;
+
+/* Structure: tinyobj_vertex_texture_t
+ *
+ * Texture vertex (vt)
+ * Specifies a texture vertex and its coordinates
+ * > vt u v w
+ *
+ * Fields:
+ *
+ * u - Horizontal direction
+ * v - (2D and 3D) Vertical direction (Default: 0)
+ * w - (3D) Depth (Default: 0)
+ */
+typedef struct s_tinyobj_vertex_texture {
+ float u; //< Horizontal direction
+ float v; //< (2D and 3D) Vertical direction (Default: 0)
+ float w; //< (3D) Depth (Default: 0)
+} tinyobj_vertex_texture_t;
+
+/* Structure: tinyobj_vertex_param_t
+ *
+ * Parameter space vertex (vp)
+ * Specifies a point in the parameter space of a curve or surface.
+ * > vp u v w
+ *
+ * Fields:
+ *
+ * u - (1D) Space control point
+ * v - (2D) Space control point
+ * weight - (Rational trimming curves) Weight (default: 0)
+ */
+typedef struct s_tinyobj_vertex_param {
+ float u, v;
+ float weight;
+} tinyobj_vertex_param_t;
+
+/***********************************************************************************************
+ * Group: Element data
+ * Polygonal and free-form geometry
+ ***********************************************************************************************/
+
+/* Structure: tinyobj_vertex_index_t
+ * Indices for each vertex type, used for triplets in faces
+ * See:
+ *
+ *
+ * Fields:
+ *
+ * v_idx - Geometric vertex index
+ * vt_idx - Texture vertex index
+ * vn_idx - Normal vertex index
+ */
typedef struct {
- char *name;
+ int v_idx, vt_idx, vn_idx;
+} tinyobj_vertex_index_t;
- float ambient[3];
- float diffuse[3];
- float specular[3];
- float transmittance[3];
- float emission[3];
- float shininess;
- float ior; /* index of refraction */
- float dissolve; /* 1 == opaque; 0 == fully transparent */
- /* illumination model (see http://www.fileformat.info/format/material/) */
- int illum;
+/* Structure: tinyobj_point_t
+ * Point (p)
+ * Specifies a point element and its vertex
+ * > p v1 v2 v3
+ *
+ * Fields:
+ *
+ * v_idx - List of vertices
+ * count - Point count
+ * length - List length
+ */
+typedef struct s_tinyobj_point {
+ int *v_idx; //< List of vertices
+ size_t count; //< Point count
+ size_t length; //< List length
+} tinyobj_point_t;
+
+/* Structure: tinyobj_line_t
+ * Line (l)
+ * Specifies a line and its vertex reference numbers
+ * > l v1/vt1 v2/vt2 v3/vt3
+ *
+ * Fields:
+ *
+ * couple_list - List of couples contained in this line
+ * count - Count of couples
+ * length - Total length of couple list
+ */
+typedef struct s_tinyobj_line {
+ /* Structure: tinyobj_line_t.s_line_vertex_index
+ * List of couples
+ *
+ * Fields:
+ *
+ * v_idx - Geometric vertex
+ * vt_idx - Texture vertex (optional)
+ */
+ struct s_line_vertex_index {
+ int v_idx, //< Geometric Vertex
+ vt_idx; //< Texture Vertex (optional)
+ } * couple_list;
+ size_t count; //< Count of couples
+ size_t length; //< Length of couple list
+} tinyobj_line_t;
+
+/* Structure: tinyobj_face_t
+ * Face (f)
+ * Specifies a face element and its vertex reference number
+ * > f v1/vt1/vn1 v2/vt2/vn2
+ * > fo (deprecated)
+ *
+ * Fields:
+ *
+ * triplet list - A list of triplets contained in this face
+ *
+ * *v* Geometric vertex (minimum of 3)
+ *
+ * *vt* Texture vertex (optional)
+ *
+ * *vn* Vertex normal (optional)
+ *
+ * When an index is empty it is equal to
+ * count - Count of triplets
+ * length - Length of triplet list
+ * triangle_count - Number of triangles in this face
+ * material_id - Material to be applied to this face (defaults to -1)
+ * smoothing_id - Smoothing group to be applied to this face (defaults to 0)
+ */
+typedef struct s_tinyobj_face {
+ tinyobj_vertex_index_t *triplet_list;
+ unsigned int count; //< Count of triplets
+ unsigned int length; //< Length of triplet list
+ unsigned int triangle_count; //< Number of triangles in this face
+ int material_id; //< Material to be applied to this face (defaults to -1)
+ int smoothing_id; //< Smoothing group to be applied to this face (defaults to 0)
+} tinyobj_face_t;
+
+/* Structure: tinyobj_coefficient_t
+ * RGB Color coefficient information used in materials
+ *
+ */
+typedef struct s_coefficient_information {
+ float r, g, b;
+} tinyobj_coefficient_t;
- int pad0;
+/* Structure: tinyobj_material_t
+ * Material attribute
+ * <.mtl format: http://paulbourke.net/dataformats/mtl/>
+ *
+ * Fields:
+ *
+ * name - Material name
+ * ambient - Ambient color coefficient (reflectivity)
+ * diffuse - Diffuse color coefficient (reflectivity)
+ * specular - Specular color coefficient (reflectivity)
+ * transmittance - Transmittance color coefficient (reflectivity)
+ * emission - Emission color coefficient (reflectivity)
+ * shininess - Specular exponent
+ * ior - Index Of Refraction (ior) 'optical density'
+ * dissolve - Non-transparency to be alpha 'dissolve' (0.0f transparent-1.0f opaque)
+ * illum - Illumination model (0-10)
+ * ambient_texname - map_Ka
+ * diffuse_texname - map_Kd
+ * specular_texname - map_Ks
+ * specular_highlight_texname - map_Ns
+ * bump_texname - map_bump, bump
+ * displacement_texname - disp
+ * alpha_texname - map_d
+ */
+typedef struct s_tinyobj_material {
+ char *name; //< Name
+
+ /**
+ * Color coefficients (reflectivity)
+ **/
+ tinyobj_coefficient_t ambient;
+ tinyobj_coefficient_t diffuse;
+ tinyobj_coefficient_t specular;
+ tinyobj_coefficient_t transmittance;
+ tinyobj_coefficient_t emission;
+
+ float shininess; //< Specular exponent
+ float ior; //< Index Of Refraction (ior) 'optical density'
+ float dissolve; //< Non-transparency to be alpha 'dissolve' (0.0f transparent -> 1.0f opaque)
+ int illum; //< Illumination model (0 - 10)
char *ambient_texname; /* map_Ka */
char *diffuse_texname; /* map_Kd */
@@ -52,14 +348,87 @@ typedef struct {
char *alpha_texname; /* map_d */
} tinyobj_material_t;
-typedef struct {
- char *name; /* group name or object name. */
- unsigned int face_offset;
- unsigned int length;
+/* Structure: tinyobj_shape_t
+ * Shapes contained in a given attribute
+ *
+ * Fields:
+ *
+ * name - Group name or object name
+ * face_offset - Face offset of this shape (starting point)
+ * length - Length of this shape (ending point)
+ */
+typedef struct s_tinyobj_shape {
+ char *name;
+ size_t face_offset;
+ size_t length;
} tinyobj_shape_t;
-typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t;
+/* Structure: tinyobj_attrib_t
+ * Object attributes
+ * <.obj format: http://paulbourke.net/dataformats/obj/>
+ *
+ * Fields:
+ *
+ * v - Geometric vertices
+ * v_count - Geometric vertex count
+ * vn - Vertex normals
+ * vn_count - Vertex normal count
+ * vt - Texture vertices
+ * vt_count - Texture vertex count
+ * vp - Parameter space vertices
+ * vp_count - Parameter space vertex count
+ * f - Object faces
+ * f_count - Face count
+ * triangle_count_total - Total count of triangles
+ * l - Lines
+ * l_count - Line count
+ * p - Points
+ */
+typedef struct s_tinyobj_attrib {
+ tinyobj_vertex_t *v; //< Geometric vertices
+ size_t v_count; //< Geometric vertex count
+ tinyobj_vertex_normal_t *vn; //< Vertex normals
+ size_t vn_count; //< Vertex normal count
+ tinyobj_vertex_texture_t *vt; //< Texture vertices
+ size_t vt_count; //< Texture vertex count
+ tinyobj_vertex_param_t *vp; //< Parameter space vertices
+ size_t vp_count; //< Parameter space vertex count
+
+ tinyobj_face_t *f; //< Object faces
+ size_t f_count; //< Face count
+ size_t triangle_count_total; //< Total count of triangles
+
+ tinyobj_line_t *l; //< Lines
+ size_t l_count; //< Line count
+ tinyobj_point_t p; //< Points
+} tinyobj_attrib_t;
+
+#ifdef TINYOBJ_ENABLE_OLDER_ATTRIBUTE
+#ifdef _MSC_VER
+#pragma message( \
+ "warning: Using deprecated attribute type in TINYOBJ (TINYOBJ_ENABLE_OLDER_ATTRIBUTE)")
+#else
+#warning \
+ "Using deprecated attribute type in TINYOBJ (TINYOBJ_ENABLE_OLDER_ATTRIBUTE)"
+#endif
+/* Structure: COMPATtinyobj_attrib_t
+ * Object attributes *(deprecated)*
+ *
+ * Fields:
+ *
+ * vertices - Array of geometric vertices (x0, y0, z0, x1, y1, z1, ...)
+ * num_vertices - Number of vertices in 'vertices' (the actual array length is num_vertices*3)
+ * normals - Array of vertex normals (i0, j0, k0, i1, j1, k1, ...)
+ * num_normals - Number of vertices in 'normals' (the actual array length is num_normals*3)
+ * texcoords - Array of texture vertices (u0, w0, u1, w1, ...)
+ * num_texcoords - Number of vertices in 'texcoords' (the actual array length is num_normals*2)
+ * faces - Array of faces (containing information)
+ * num_faces - Length of 'faces'
+ * face_num_verts - Array of number of vertices in each face (3 for triangulated faces)
+ * num_face_num_verts - Total number of triangles in this object (length of face_num_verts)
+ * material_ids - Array with the id of the material for each face (length is num_faces)
+ **/
typedef struct {
unsigned int num_vertices;
unsigned int num_normals;
@@ -75,77 +444,136 @@ typedef struct {
tinyobj_vertex_index_t *faces;
int *face_num_verts;
int *material_ids;
-} tinyobj_attrib_t;
-
-
-#define TINYOBJ_FLAG_TRIANGULATE (1 << 0)
+} COMPATtinyobj_attrib_t;
-#define TINYOBJ_INVALID_INDEX (0x80000000)
+int tinyobj_new2old(tinyobj_attrib_t *attrib,
+ COMPATtinyobj_attrib_t *out_attrib);
+void tinyobj_attrib_free_compat(COMPATtinyobj_attrib_t *attrib);
+#endif // TINYOBJ_ENABLE_OLDER_ATTRIBUTE
-#define TINYOBJ_SUCCESS (0)
-#define TINYOBJ_ERROR_EMPTY (-1)
-#define TINYOBJ_ERROR_INVALID_PARAMETER (-2)
-#define TINYOBJ_ERROR_FILE_OPERATION (-3)
-
-/* Parse wavefront .obj(.obj string data is expanded to linear char array `buf')
- * flags are combination of TINYOBJ_FLAG_***
- * Returns TINYOBJ_SUCCESS if things goes well.
- * Returns TINYOBJ_ERR_*** when there is an error.
- */
-extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
- size_t *num_shapes, tinyobj_material_t **materials,
- size_t *num_materials, const char *buf, size_t len,
- unsigned int flags);
-extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
- size_t *num_materials_out,
- const char *filename);
-
-extern void tinyobj_attrib_init(tinyobj_attrib_t *attrib);
-extern void tinyobj_attrib_free(tinyobj_attrib_t *attrib);
-extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes);
-extern void tinyobj_materials_free(tinyobj_material_t *materials,
- size_t num_materials);
+int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
+ unsigned int *num_materials_out,
+ const char *filename);
+void tinyobj_material_free(tinyobj_material_t *materials,
+ unsigned int num_materials);
+void tinyobj_shape_free(tinyobj_shape_t *shapes, unsigned int num_shapes);
+void tinyobj_attrib_free(tinyobj_attrib_t *attrib);
+int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
+ unsigned int *num_shapes,
+ tinyobj_material_t **materials_out,
+ unsigned int *num_materials_out, const char *buf,
+ size_t len, unsigned int flags);
#ifdef TINYOBJ_LOADER_C_IMPLEMENTATION
-#include
#include
-#include
#include
+#include
+#include // atoi
+#include
-#if defined(TINYOBJ_MALLOC) && defined(TINYOBJ_REALLOC) && defined(TINYOBJ_CALLOC) && defined(TINYOBJ_FREE)
+#if defined(TINYOBJ_MALLOC) && defined(TINYOBJ_REALLOC) && \
+ defined(TINYOBJ_CALLOC) && defined(TINYOBJ_FREE)
/* ok */
-#elif !defined(TINYOBJ_MALLOC) && !defined(TINYOBJ_REALLOC) && !defined(TINYOBJ_CALLOC) && !defined(TINYOBJ_FREE)
+#elif !defined(TINYOBJ_MALLOC) && !defined(TINYOBJ_REALLOC) && \
+ !defined(TINYOBJ_CALLOC) && !defined(TINYOBJ_FREE)
/* ok */
#else
-#error "Must define all or none of TINYOBJ_MALLOC, TINYOBJ_REALLOC, TINYOBJ_CALLOC and TINYOBJ_FREE."
+#error \
+ "Must define all or none of TINYOBJ_MALLOC, TINYOBJ_REALLOC, TINYOBJ_CALLOC and TINYOBJ_FREE."
#endif
#ifndef TINYOBJ_MALLOC
-#include
#define TINYOBJ_MALLOC malloc
#define TINYOBJ_REALLOC realloc
#define TINYOBJ_CALLOC calloc
#define TINYOBJ_FREE free
#endif
-#define TINYOBJ_MAX_FACES_PER_F_LINE (16)
+#ifdef TINYOBJ_USE_UTHASH
+#include
+#endif
+
+#ifdef _MSC_VER
+#define __func__ __FUNCTION__
+#endif
+
+/***********************************************************************************************
+ * Group: String handling
+ ***********************************************************************************************/
+/* Functions: String handling macros
+ *
+ * IS_SPACE - Is this character a spacing character ' ' or '\t'
+ * IS_DIGIT - Is this character a digit
+ * IS_NEW_LINE - Is this character a new line character '\r','\n','\0'
+ */
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
#define IS_DIGIT(x) ((unsigned int)((x) - '0') < (unsigned int)(10))
#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
+/* Function: is_line_ending
+ * Is given line ending in this character?
+ *
+ * Returns:
+ * True if line is ending
+ **/
+static int is_line_ending(const char *p, size_t i, size_t end_i) {
+ if (p[i] == '\0') return 1;
+ if (p[i] == '\n') return 1; // this includes \r\n
+ if (p[i] == '\r') {
+ if (((i + 1) < end_i) && (p[i + 1] != '\n')) { // detect only \r case
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Function: skip_space
+ * Skips the next spacing characters in given token
+ */
static void skip_space(const char **token) {
while ((*token)[0] == ' ' || (*token)[0] == '\t') {
+ if ((*token)[0] == '\0') return;
(*token)++;
}
}
+/* Function: skip_space_and_cr
+ * Skips the next spacing characters and also carriage return in given token
+ */
static void skip_space_and_cr(const char **token) {
while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') {
+ if ((*token)[0] == '\0') return;
(*token)++;
}
}
+/* Function: length_until_space
+ * Calculates and returns the length until next space character in this token
+ *
+ * Parameters:
+ *
+ * token - Token to be used
+ * n - Maximum number of characters to count
+ *
+ * Returns:
+ *
+ * Length until next space character
+ */
+static size_t length_until_space(const char *token, size_t n) {
+ size_t len = 0;
+ for (len = 0; len < n - 1; len++) {
+ if (token[len] == '\n' || token[len] == ' ' || token[len] == '\t' ||
+ token[len] == '\r')
+ break;
+ }
+ return len;
+}
+
+/* Function: until_space
+ * Skips the next spacing characters
+ * > '\0',' ', '\t', '\r'
+ */
static int until_space(const char *token) {
const char *p = token;
while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') {
@@ -155,150 +583,366 @@ static int until_space(const char *token) {
return (int)(p - token);
}
-static size_t length_until_newline(const char *token, size_t n) {
- size_t len = 0;
-
- /* Assume token[n-1] = '\0' */
- for (len = 0; len < n - 1; len++) {
- if (token[len] == '\n') {
- break;
- }
- if ((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n'))) {
- break;
- }
- }
+/* Function: until_space_cr_slash
+ * Skips the next characters in given token
+ * > '\0','/', ' ', '\t', '\r'
+ */
+static void until_space_cr_slash(const char **token) {
+ while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
+ (*token)[0] != '\t' && (*token)[0] != '\r')
+ (*token)++;
+}
- return len;
+/* Function: length_until_newline_comment_space
+ *
+ * Returns the length until the next characters:
+ * > \n \r\n \0 ' ' \t #
+ *
+ * Parameters:
+ * token - Buffer
+ * n - Maximum number of characters to count
+ *
+ * Returns:
+ *
+ * Length until next character
+ */
+static size_t length_until_newline_comment_space(const char *token, size_t n) {
+ size_t len = 0;
+ for(len = 0; len < n-1; len++) {
+ if(token[len] == '\n')
+ break;
+ if((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n')))
+ break;
+ if(token[len] == '\0' || token[len] == ' ' || token[len] == '\t' || token[len] == '#')
+ break;
+ }
+ return len;
}
+/* Function: length_until_line_feed
+ *
+ * Returns the length until the next line feed
+ *
+ * *Assumes token[n-1] = '\0'*
+ *
+ * Parameters:
+ * token - Buffer
+ * n - Maximum number of characters to count
+ */
static size_t length_until_line_feed(const char *token, size_t n) {
size_t len = 0;
- /* Assume token[n-1] = '\0' */
for (len = 0; len < n; len++) {
- if ((token[len] == '\n') || (token[len] == '\r')) {
- break;
- }
+ if ((token[len] == '\n') || (token[len] == '\r')) break;
}
-
return len;
}
-/* http://stackoverflow.com/questions/5710091/how-does-atoi-function-in-c-work
-*/
-static int my_atoi(const char *c) {
- int value = 0;
- int sign = 1;
- if (*c == '+' || *c == '-') {
- if (*c == '-') sign = -1;
- c++;
+/* Function: strdup_ml
+ * Duplicates 'max_length' characters of given string
+ * (strdup with max length specified)
+ *
+ * Parameters:
+ *
+ * s - String to be duplicated
+ * max_length - Maximum length to be duplicated
+ *
+ * Returns:
+ *
+ * Duplicated string (*should be freed by the caller*)
+ */
+static char *strdup_ml(const char *s, size_t max_length) {
+ char *d;
+ size_t len;
+
+ if (s == NULL) return NULL;
+
+ /* Do not consider CRLF line ending(#19) */
+ len = length_until_line_feed(s, max_length);
+ /* len = strlen(s); */
+
+ /* trim line ending and append '\0' */
+ d = TINYOBJ_MALLOC(len + 1); /* + '\0' */
+ memcpy(d, s, len);
+ d[len] = '\0';
+
+ return d;
+}
+
+#ifndef strndup
+/* Function: strndup
+ * strndup implementation for non-GNU compliant compilers
+ */
+char *strndup(const char *s, size_t len) {
+ char *d;
+ size_t slen;
+
+ if (s == NULL || len == 0) return NULL;
+
+ d = TINYOBJ_MALLOC(len + 1); /* + '\0' */
+ if (d == NULL) return NULL;
+
+ slen = strlen(s);
+ if (slen < len) {
+ memcpy(d, s, slen);
+ d[slen] = '\0';
+ } else {
+ memcpy(d, s, len);
+ d[len] = '\0';
+ }
+
+ return d;
+}
+#endif
+
+/* Function: dynamic_fgets
+ * fgets with a dynamic buffer
+ */
+static char *dynamic_fgets(char **buf, int *size, FILE *file) {
+ char *offset;
+ char *ret;
+ int old_size;
+
+ if (!(ret = fgets(*buf, (int)*size, file))) {
+ return ret;
}
- while (((*c) >= '0') && ((*c) <= '9')) { /* isdigit(*c) */
- value *= 10;
- value += (int)(*c - '0');
- c++;
+
+ if (NULL != strchr(*buf, '\n')) {
+ return ret;
}
- return value * sign;
+
+ do {
+ old_size = *size;
+ *size *= 2;
+ *buf = TINYOBJ_REALLOC(*buf, *size);
+ offset = &((*buf)[old_size - 1]);
+
+ ret = fgets(offset, (int)(old_size + 1), file);
+ } while (ret && (NULL == strchr(*buf, '\n')));
+
+ return ret;
}
-/* Make index zero-base, and also support relative index. */
+/***********************************************************************************************
+ * Group: Triplet handling
+ ***********************************************************************************************/
+
+/* Function: fixIndex
+ *
+ * Converts given absolute index, from triplet to zero-base, supports relative
+ * indices
+ *
+ * See:
+ *
+ * -
+ * -
+ * -
+ *
+ *
+ * Parameters:
+ *
+ * idx - Index to be converted
+ * n - Position of this couple/tuple regarding to the vertex type of this index
+ *
+ * Returns:
+ *
+ * - Fixed index value that's able to be used when accessing vertex arrays
+ * - when there's no equivalent index
+ */
static int fixIndex(int idx, size_t n) {
if (idx > 0) return idx - 1;
if (idx == 0) return 0;
- return (int)n + idx; /* negative value = relative */
+ if (idx == TINYOBJ_INVALID_INDEX) return TINYOBJ_INVALID_INDEX;
+
+ return (int)n + idx; // Negative value = relative
}
-/* Parse raw triples: i, i/j/k, i//k, i/j */
+/* Function: parseRawTriple
+ *
+ * Parses raw triplets of given tokens
+ * > v, v/vt/vn, v//vn, v/vt
+ *
+ * > Valid triplets:
+ * > 1//1
+ * > 1//1 2//2 3//3 4//4
+ * > 1/1/1 2/2/2 3/3/3 4/4/4
+ * > The following are examples of illegal statements
+ * > 1/1/1 2/2/2 3//3 4//4
+ * > 1/ 1/1 2/2/2 3/3/3 4/4/4
+ *
+ * Parameters:
+ * token - Token to parse
+ *
+ * Returns:
+ * Indices parsed from given token, if one of the indices is not present it's
+ * equal to
+ */
static tinyobj_vertex_index_t parseRawTriple(const char **token) {
tinyobj_vertex_index_t vi;
- /* 0x80000000 = -2147483648 = invalid */
- vi.v_idx = (int)(0x80000000);
- vi.vn_idx = (int)(0x80000000);
- vi.vt_idx = (int)(0x80000000);
- vi.v_idx = my_atoi((*token));
- while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
- (*token)[0] != '\t' && (*token)[0] != '\r') {
- (*token)++;
- }
- if ((*token)[0] != '/') {
- return vi;
- }
+ vi.v_idx = (int)(TINYOBJ_INVALID_INDEX);
+ vi.vn_idx = (int)(TINYOBJ_INVALID_INDEX);
+ vi.vt_idx = (int)(TINYOBJ_INVALID_INDEX);
+
+ // v
+ vi.v_idx = atoi((*token));
+ until_space_cr_slash(token);
+ if ((*token)[0] != '/') return vi;
(*token)++;
- /* i//k */
+ // v//vn
if ((*token)[0] == '/') {
(*token)++;
- vi.vn_idx = my_atoi((*token));
- while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
- (*token)[0] != '\t' && (*token)[0] != '\r') {
- (*token)++;
- }
+ vi.vn_idx = atoi((*token));
+ until_space_cr_slash(token);
return vi;
}
- /* i/j/k or i/j */
- vi.vt_idx = my_atoi((*token));
- while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
- (*token)[0] != '\t' && (*token)[0] != '\r') {
- (*token)++;
- }
- if ((*token)[0] != '/') {
- return vi;
- }
+ // v/vt/vn or v/vt
+ vi.vt_idx = atoi((*token));
+ until_space_cr_slash(token);
+ if ((*token)[0] != '/') return vi;
+
+ // v/vt/vn
+ (*token)++; // skip '/'
+ vi.vn_idx = atoi((*token));
+ until_space_cr_slash(token);
- /* i/j/k */
- (*token)++; /* skip '/' */
- vi.vn_idx = my_atoi((*token));
- while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
- (*token)[0] != '\t' && (*token)[0] != '\r') {
- (*token)++;
- }
return vi;
}
+/***********************************************************************************************
+ * Group: Integer/Double/Float handling
+ ***********************************************************************************************/
+
+/* Function: parseInt
+ * Returns equivalent integer of next non-space character, the token is advanced
+ * until next space
+ */
static int parseInt(const char **token) {
int i = 0;
skip_space(token);
- i = my_atoi((*token));
+ i = atoi((*token));
(*token) += until_space((*token));
return i;
}
-/*
- * Tries to parse a floating point number located at s.
+/* Function: tryParseDouble_assemble
*
- * s_end should be a location in the string where reading should absolutely
- * stop. For example at the end of the string, to prevent buffer overflows.
+ * Last step of
*
- * Parses the following EBNF grammar:
- * sign = "+" | "-" ;
- * END = ? anything not in digit ?
- * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
- * integer = [sign] , digit , {digit} ;
- * decimal = integer , ["." , integer] ;
- * float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ;
+ * Assembles given information into double
*
- * Valid strings are for example:
- * -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2
+ * Parameters:
+ * sign - Sign of this double
+ * mantissa - Mantissa
+ * exp_sign - Exponent sign
+ * exponent - Exponent
*
- * If the parsing is a success, result is set to the parsed value and true
- * is returned.
- *
- * The function is greedy and will parse until any of the following happens:
- * - a non-conforming character is encountered.
- * - s_end is reached.
+ * Returns:
*
- * The following situations triggers a failure:
- * - s >= s_end.
- * - parse failure.
+ * Assembled double
*/
-static int tryParseDouble(const char *s, const char *s_end, double *result) {
- double mantissa = 0.0;
- /* This exponent is base 2 rather than 10.
- * However the exponent we parse is supposed to be one of ten,
- * thus we must take care to convert the exponent/and or the
- * mantissa to a * 2^E, where a is the mantissa and E is the
+static double tryParseDouble_assemble(char sign, double mantissa, char exp_sign,
+ int exponent) {
+ double a = 1.0; /* = pow(5.0, exponent); */
+ double b = 1.0; /* = 2.0^exponent */
+ int i;
+
+ for (i = 0; i < exponent; i++) a = a * 5.0;
+
+ for (i = 0; i < exponent; i++) b = b * 2.0;
+
+ if (exp_sign == '-') {
+ a = 1.0 / a;
+ b = 1.0 / b;
+ }
+
+ /* (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent),
+ exponent); */
+ return (sign == '+' ? 1 : -1) * (mantissa * a * b);
+}
+
+/* Function: tryParseDouble_integer
+ *
+ * Part of
+ *
+ * Parses integer part of a double (advances curr)
+ *
+ * Parameters:
+ *
+ * curr - Current position in a buffer
+ * s_end - End of the buffer
+ * sign - [out] Sign to be filled
+ * integer - [out] Integer to be filled
+ *
+ * Returns:
+ * True integer was successfuly parsed, false if nothing was read
+ */
+static unsigned char tryParseDouble_integer(const char **curr,
+ const char *s_end, char *sign,
+ int *integer) {
+ unsigned int read = 0;
+ *sign = '+'; // Default sign
+
+ // Read sign
+ if ((*curr)[0] == '+' || (*curr)[0] == '-') {
+ (*sign) = (*curr)[0];
+ if ((*curr)++ == s_end) return 0; // No more data to read (invalid number)
+ } else if (!IS_DIGIT((*curr)[0])) {
+ return 0; // A digit is expected
+ }
+
+ // Read integer
+ for (; (*curr) != s_end && IS_DIGIT(((*curr)[0])); (*curr)++, read++) {
+ (*integer) *= 10;
+ (*integer) += (int)((*curr)[0] - 0x30);
+ }
+ return (read > 0);
+}
+
+/* Function: tryParseDouble
+ *
+ * Tries to parse a floating point number located at s.
+ * > Parses the following EBNF grammar:
+ * > sign = "+" | "-" ;
+ * > END = ? anything not in digit ?
+ * > digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
+ * > integer = [sign] , digit , {digit} ;
+ * > decimal = integer , ["." , integer] ;
+ * > float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ;
+ *
+ * > Valid strings are for example:
+ * > -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2
+ *
+ * The function is greedy and will parse until any of the following happens:
+ *
+ * - a non-conforming character is encountered.
+ * - s_end is reached.
+ *
+ * The following situations triggers a failure:
+ *
+ * - s >= s_end.
+ * - parse failure.
+ *
+ * Parameters:
+ *
+ * s - String to be parsed
+ * s_end - Location in the string where reading should halt, e.g. the end of
+ * the string result - [out] Parsed double
+ *
+ * Returns
+ * If the parsing is a success, result is set to the parsed value and true
+ * is returned.
+ */
+static unsigned char tryParseDouble(const char *s, const char *s_end,
+ double *result) {
+ double mantissa = 0.0;
+ int integer = 0;
+ /* This exponent is base 2 rather than 10.
+ * However the exponent we parse is supposed to be one of ten,
+ * thus we must take care to convert the exponent/and or the
+ * mantissa to a * 2^E, where a is the mantissa and E is the
* exponent.
* To get the final double we will use ldexp, it requires the
* exponent to be in base 2.
@@ -314,261 +958,182 @@ static int tryParseDouble(const char *s, const char *s_end, double *result) {
/* How many characters were read in a loop. */
int read = 0;
- /* Tells whether a loop terminated due to reaching s_end. */
- int end_not_reached = 0;
-
- /*
- BEGIN PARSING.
- */
-
- if (s >= s_end) {
- return 0; /* fail */
- }
- /* Find out what sign we've got. */
- if (*curr == '+' || *curr == '-') {
- sign = *curr;
- curr++;
- } else if (IS_DIGIT(*curr)) { /* Pass through. */
- } else {
- goto fail;
- }
+ /* BEGIN PARSING. */
- /* Read the integer part. */
- end_not_reached = (curr != s_end);
- while (end_not_reached && IS_DIGIT(*curr)) {
- mantissa *= 10;
- mantissa += (int)(*curr - 0x30);
- curr++;
- read++;
- end_not_reached = (curr != s_end);
- }
+ if (s >= s_end) // Passed stopping point
+ return 0;
- /* We must make sure we actually got something. */
- if (read == 0) goto fail;
- /* We allow numbers of form "#", "###" etc. */
- if (!end_not_reached) goto assemble;
-
- /* Read the decimal part. */
- if (*curr == '.') {
- curr++;
- read = 1;
- end_not_reached = (curr != s_end);
- while (end_not_reached && IS_DIGIT(*curr)) {
- /* pow(10.0, -read) */
- double frac_value = 1.0;
- int f;
- for (f = 0; f < read; f++) {
- frac_value *= 0.1;
+ // Read integer and sign
+ if (!tryParseDouble_integer(&curr, s_end, &sign, &integer))
+ return 0; // Nothing was read
+ mantissa = integer;
+
+ while (curr != s_end) {
+ switch (*curr) {
+ // Read decimal
+ case '.': {
+ if (curr++ == s_end) return 0;
+ read = 1;
+ while (curr != s_end && IS_DIGIT(*curr)) {
+ /* pow(10.0, -read) */
+ double frac_value = 1.0;
+ int f;
+ for (f = 0; f < read; f++) frac_value *= 0.1;
+ mantissa += (int)(*curr - 0x30) * frac_value;
+ read++;
+ curr++;
+ }
+ break;
}
- mantissa += (int)(*curr - 0x30) * frac_value;
- read++;
- curr++;
- end_not_reached = (curr != s_end);
- }
- } else if (*curr == 'e' || *curr == 'E') {
- } else {
- goto assemble;
- }
-
- if (!end_not_reached) goto assemble;
-
- /* Read the exponent part. */
- if (*curr == 'e' || *curr == 'E') {
- curr++;
- /* Figure out if a sign is present and if it is. */
- end_not_reached = (curr != s_end);
- if (end_not_reached && (*curr == '+' || *curr == '-')) {
- exp_sign = *curr;
- curr++;
- } else if (IS_DIGIT(*curr)) { /* Pass through. */
- } else {
- /* Empty E is not allowed. */
- goto fail;
- }
-
- read = 0;
- end_not_reached = (curr != s_end);
- while (end_not_reached && IS_DIGIT(*curr)) {
- exponent *= 10;
- exponent += (int)(*curr - 0x30);
- curr++;
- read++;
- end_not_reached = (curr != s_end);
- }
- if (read == 0) goto fail;
- }
-
-assemble :
-
- {
- double a = 1.0; /* = pow(5.0, exponent); */
- double b = 1.0; /* = 2.0^exponent */
- int i;
- for (i = 0; i < exponent; i++) {
- a = a * 5.0;
- }
-
- for (i = 0; i < exponent; i++) {
- b = b * 2.0;
- }
-
- if (exp_sign == '-') {
- a = 1.0 / a;
- b = 1.0 / b;
+ // Read Exponent
+ case 'E':
+ case 'e': {
+ if (curr++ == s_end) return 0;
+ if (!tryParseDouble_integer(&curr, s_end, &exp_sign, &exponent))
+ return 0; // Empty E is not allowed
+ break;
+ }
+ default:
+ curr++;
+ break;
}
-
- *result =
- /* (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent),
- exponent); */
- (sign == '+' ? 1 : -1) * (mantissa * a * b);
}
-
+ *result = tryParseDouble_assemble(sign, mantissa, exp_sign, exponent);
return 1;
-fail:
- return 0;
}
-static float parseFloat(const char **token) {
+/* Function: try_parse_float
+ *
+ * Tries to parse next float, if there's none fails
+ *
+ * Parameters:
+ *
+ * token - Buffer to be read
+ * result - [out] Result, not set if return is false
+ *
+ * Returns:
+ *
+ * If the parsing is a success, result is set to the parsed value and true
+ * is returned.
+ **/
+static unsigned char try_parse_float(const char **token, float *result) {
const char *end;
double val = 0.0;
float f = 0.0f;
+ unsigned char retval;
+
skip_space(token);
end = (*token) + until_space((*token));
val = 0.0;
- tryParseDouble((*token), end, &val);
- f = (float)(val);
+ retval = tryParseDouble((*token), end, &val);
+ *result = (retval) ? (float)val : f;
(*token) = end;
+ return retval;
+}
+
+/* Function: parseFloat
+ *
+ * Returns equivalent float of next non-space character, the token is advanced
+ * until next space
+ */
+static float parseFloat(const char **token) {
+ float f = 0.0f;
+ try_parse_float(token, &f);
return f;
}
+/* Function: parseFloat2
+ *
+ * Parses the two next floats in given token
+ *
+ * See:
+ *
+ *
+ */
static void parseFloat2(float *x, float *y, const char **token) {
(*x) = parseFloat(token);
(*y) = parseFloat(token);
}
+/* Function: parseFloat3
+ *
+ * Parses the three next floats in given token
+ *
+ * See:
+ *
+ *
+ */
static void parseFloat3(float *x, float *y, float *z, const char **token) {
(*x) = parseFloat(token);
(*y) = parseFloat(token);
(*z) = parseFloat(token);
}
-static size_t my_strnlen(const char *s, size_t n) {
- const char *p = memchr(s, 0, n);
- return p ? (size_t)(p - s) : n;
-}
-
-static char *my_strdup(const char *s, size_t max_length) {
- char *d;
- size_t len;
-
- if (s == NULL) return NULL;
-
- /* Do not consider CRLF line ending(#19) */
- len = length_until_line_feed(s, max_length);
- /* len = strlen(s); */
-
- /* trim line ending and append '\0' */
- d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */
- memcpy(d, s, (size_t)(len));
- d[len] = '\0';
-
- return d;
-}
-
-static char *my_strndup(const char *s, size_t len) {
- char *d;
- size_t slen;
-
- if (s == NULL) return NULL;
- if (len == 0) return NULL;
-
- slen = my_strnlen(s, len);
- d = (char *)TINYOBJ_MALLOC(slen + 1); /* + '\0' */
- if (!d) {
- return NULL;
- }
- memcpy(d, s, slen);
- d[slen] = '\0';
-
- return d;
-}
-
-char *dynamic_fgets(char **buf, size_t *size, FILE *file) {
- char *offset;
- char *ret;
- size_t old_size;
-
- if (!(ret = fgets(*buf, (int)*size, file))) {
- return ret;
- }
-
- if (NULL != strchr(*buf, '\n')) {
- return ret;
- }
-
- do {
- old_size = *size;
- *size *= 2;
- *buf = (char*)TINYOBJ_REALLOC(*buf, *size);
- offset = &((*buf)[old_size - 1]);
-
- ret = fgets(offset, (int)(old_size + 1), file);
- } while(ret && (NULL == strchr(*buf, '\n')));
-
- return ret;
-}
-
-static void initMaterial(tinyobj_material_t *material) {
- int i;
- material->name = NULL;
- material->ambient_texname = NULL;
- material->diffuse_texname = NULL;
- material->specular_texname = NULL;
- material->specular_highlight_texname = NULL;
- material->bump_texname = NULL;
- material->displacement_texname = NULL;
- material->alpha_texname = NULL;
- for (i = 0; i < 3; i++) {
- material->ambient[i] = 0.f;
- material->diffuse[i] = 0.f;
- material->specular[i] = 0.f;
- material->transmittance[i] = 0.f;
- material->emission[i] = 0.f;
- }
- material->illum = 0;
- material->dissolve = 1.f;
- material->shininess = 1.f;
- material->ior = 1.f;
-}
-
-/* Implementation of string to int hashtable */
+/***********************************************************************************************
+ * Group: TINYOBJ hashtable implementation
+ * String to int hashtable
+ ***********************************************************************************************/
-#define HASH_TABLE_ERROR 1
-#define HASH_TABLE_SUCCESS 0
+/* Constants: Hash table return codes
+ *
+ * TINYOBJ_HASH_TABLE_SUCCESS - Success
+ * TINYOBJ_HASH_TABLE_ERROR - Failure
+ */
+#define TINYOBJ_HASH_TABLE_SUCCESS (0)
+#define TINYOBJ_HASH_TABLE_ERROR (-1)
-#define HASH_TABLE_DEFAULT_SIZE 10
+/* Constant: TINYOBJ_HASH_TABLE_DEFAULT_SIZE
+ * Default starting size for tinyobj own hash table implementation tables
+ */
+#define TINYOBJ_HASH_TABLE_DEFAULT_SIZE (10)
-typedef struct hash_table_entry_t
-{
+#ifndef TINYOBJ_USE_UTHASH
+/* Structure: hash_table_entry_t
+ *
+ * Entries used in tinyobj's own hash table implementation
+ *
+ * Fields:
+ *
+ * hash - Hash value
+ * filled - Is this entry filled
+ * value - Value
+ * next - Next entry
+ */
+typedef struct s_hash_table_entry_t {
unsigned long hash;
int filled;
int pad0;
long value;
- struct hash_table_entry_t* next;
+ struct hash_table_entry_t *next;
} hash_table_entry_t;
-typedef struct
-{
- unsigned long* hashes;
- hash_table_entry_t* entries;
+/* Structure: tinyobj_material_table_t
+ *
+ * Hash table used in tinyobj's own hash table implementation
+ *
+ * Fields:
+ *
+ * hashes - Hashes used
+ * entries - Linked list of entries in this table
+ * capacity - Total capacity of this table
+ * n - Count of elements
+ */
+typedef struct {
+ unsigned long *hashes;
+ hash_table_entry_t *entries;
size_t capacity;
size_t n;
-} hash_table_t;
+} tinyobj_material_table_t;
+
+// Group: TINYOBJ hashtable implementation
+// String to int hashtable
-static unsigned long hash_djb2(const unsigned char* str)
-{
+/* Function: hash_djb2
+ * Converts given string to hash
+ */
+static unsigned long hash_djb2(const unsigned char *str) {
unsigned long hash = 5381;
int c;
@@ -579,37 +1144,61 @@ static unsigned long hash_djb2(const unsigned char* str)
return hash;
}
-static void create_hash_table(size_t start_capacity, hash_table_t* hash_table)
-{
- if (start_capacity < 1)
- start_capacity = HASH_TABLE_DEFAULT_SIZE;
- hash_table->hashes = (unsigned long*) TINYOBJ_MALLOC(start_capacity * sizeof(unsigned long));
- hash_table->entries = (hash_table_entry_t*) TINYOBJ_CALLOC(start_capacity, sizeof(hash_table_entry_t));
+/* Function: create_hash_table
+ *
+ * Creates a new hash table in 'hash_table'
+ *
+ * Parameters:
+ *
+ * start_capacity - Starting capacity, if equal to zero is
+ * hash_table - [in/out] Table to be initialized
+ */
+static void create_hash_table(size_t start_capacity,
+ tinyobj_material_table_t *hash_table) {
+ if (start_capacity < 1) start_capacity = TINYOBJ_HASH_TABLE_DEFAULT_SIZE;
+ hash_table->hashes =
+ (unsigned long *)TINYOBJ_MALLOC(start_capacity * sizeof(unsigned long));
+ hash_table->entries = (hash_table_entry_t *)TINYOBJ_CALLOC(
+ start_capacity, sizeof(hash_table_entry_t));
hash_table->capacity = start_capacity;
hash_table->n = 0;
}
-static void destroy_hash_table(hash_table_t* hash_table)
-{
- TINYOBJ_FREE(hash_table->entries);
- TINYOBJ_FREE(hash_table->hashes);
+/* Function: destroy_hash_table
+ * Finalizes given hash_table
+ */
+static void destroy_hash_table(tinyobj_material_table_t *hash_table) {
+ if (!hash_table) return;
+ if (hash_table->entries) TINYOBJ_FREE(hash_table->entries);
+ if (hash_table->hashes) TINYOBJ_FREE(hash_table->hashes);
}
-/* Insert with quadratic probing */
-static int hash_table_insert_value(unsigned long hash, long value, hash_table_t* hash_table)
-{
+/* Function: hash_table_insert_value
+ *
+ * Subroutine of
+ * Inserts given value in hash, uses quadratic probing
+ *
+ * Parameters:
+ *
+ * hash - Hash to be used
+ * value - Value of this entity
+ * hash_table - Table to be used
+ *
+ * Returns:
+ * upon failure or after succeding
+ */
+static int hash_table_insert_value(unsigned long hash, long value,
+ tinyobj_material_table_t *hash_table) {
/* Insert value */
size_t start_index = hash % hash_table->capacity;
size_t index = start_index;
- hash_table_entry_t* start_entry = hash_table->entries + start_index;
+ hash_table_entry_t *start_entry = hash_table->entries + start_index;
size_t i;
- hash_table_entry_t* entry;
+ hash_table_entry_t *entry;
- for (i = 1; hash_table->entries[index].filled; i++)
- {
- if (i >= hash_table->capacity)
- return HASH_TABLE_ERROR;
- index = (start_index + (i * i)) % hash_table->capacity;
+ for (i = 1; hash_table->entries[index].filled; i++) {
+ if (i >= hash_table->capacity) return TINYOBJ_HASH_TABLE_ERROR;
+ index = (start_index + (i * i)) % hash_table->capacity;
}
entry = hash_table->entries + index;
@@ -618,79 +1207,125 @@ static int hash_table_insert_value(unsigned long hash, long value, hash_table_t*
entry->value = value;
if (index != start_index) {
- /* This is a new entry, but not the start entry, hence we need to add a next pointer to our entry */
+ /* This is a new entry, but not the start entry, hence we need to add a next
+ * pointer to our entry */
entry->next = start_entry->next;
start_entry->next = entry;
}
- return HASH_TABLE_SUCCESS;
+ return TINYOBJ_HASH_TABLE_SUCCESS;
}
-static int hash_table_insert(unsigned long hash, long value, hash_table_t* hash_table)
-{
+/* Function: hash_table_insert
+ *
+ * Inserts given value in hash, calls
+ *
+ * Parameters:
+ *
+ * hash - Hash to be used
+ * value - Value of this entity
+ * hash_table - Table to be used
+ *
+ * Returns:
+ * upon failure or after succeding
+ */
+static int hash_table_insert(unsigned long hash, long value,
+ tinyobj_material_table_t *hash_table) {
int ret = hash_table_insert_value(hash, value, hash_table);
- if (ret == HASH_TABLE_SUCCESS)
- {
+ if (ret == TINYOBJ_HASH_TABLE_SUCCESS) {
hash_table->hashes[hash_table->n] = hash;
hash_table->n++;
}
return ret;
}
-static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* hash_table)
-{
- hash_table_entry_t* entry = hash_table->entries + (hash % hash_table->capacity);
- while (entry)
- {
- if (entry->hash == hash && entry->filled)
- {
- return entry;
- }
+/* Function: hash_table_find
+ *
+ * Tries to find given hash in the table
+ *
+ * Parameters:
+ *
+ * hash - Hash to be found
+ * hash_table - Table to be used
+ *
+ * Returns:
+ *
+ * Entry upon succeding or NULL if failure to find
+ */
+static hash_table_entry_t *hash_table_find(
+ unsigned long hash, tinyobj_material_table_t *hash_table) {
+ hash_table_entry_t *entry =
+ hash_table->entries + (hash % hash_table->capacity);
+ while (entry) {
+ if (entry->hash == hash && entry->filled) return entry;
entry = entry->next;
}
return NULL;
}
-static void hash_table_maybe_grow(size_t new_n, hash_table_t* hash_table)
-{
+/* Function: hash_table_maybe_grow
+ * Grows given hash_table to new_n if new_n is less or equal to capacity
+ */
+static void hash_table_maybe_grow(size_t new_n,
+ tinyobj_material_table_t *hash_table) {
size_t new_capacity;
- hash_table_t new_hash_table;
+ tinyobj_material_table_t new_hash_table;
size_t i;
- if (new_n <= hash_table->capacity) {
- return;
- }
- new_capacity = 2 * ((2 * hash_table->capacity) > new_n ? hash_table->capacity : new_n);
- /* Create a new hash table. We're not calling create_hash_table because we want to realloc the hash array */
- new_hash_table.hashes = hash_table->hashes = (unsigned long*) TINYOBJ_REALLOC((void*) hash_table->hashes, sizeof(unsigned long) * new_capacity);
- new_hash_table.entries = (hash_table_entry_t*) TINYOBJ_CALLOC(new_capacity, sizeof(hash_table_entry_t));
+ if (new_n <= hash_table->capacity) return;
+ new_capacity =
+ 2 * ((2 * hash_table->capacity) > new_n ? hash_table->capacity : new_n);
+ /* Create a new hash table. We're not calling create_hash_table because we
+ * want to realloc the hash array */
+ new_hash_table.hashes = hash_table->hashes = (unsigned long *)TINYOBJ_REALLOC(
+ (void *)hash_table->hashes, sizeof(unsigned long) * new_capacity);
+ new_hash_table.entries = (hash_table_entry_t *)TINYOBJ_CALLOC(
+ new_capacity, sizeof(hash_table_entry_t));
new_hash_table.capacity = new_capacity;
new_hash_table.n = hash_table->n;
/* Rehash */
- for (i = 0; i < hash_table->capacity; i++)
- {
- hash_table_entry_t* entry = hash_table_find(hash_table->hashes[i], hash_table);
- hash_table_insert_value(hash_table->hashes[i], entry->value, &new_hash_table);
+ for (i = 0; i < hash_table->capacity; i++) {
+ hash_table_entry_t *entry =
+ hash_table_find(hash_table->hashes[i], hash_table);
+ hash_table_insert_value(hash_table->hashes[i], entry->value,
+ &new_hash_table);
}
TINYOBJ_FREE(hash_table->entries);
(*hash_table) = new_hash_table;
}
-static int hash_table_exists(const char* name, hash_table_t* hash_table)
-{
- return hash_table_find(hash_djb2((const unsigned char*)name), hash_table) != NULL;
+/* Function: hash_table_exists
+ * Tries to find 'name' in hash_table, returns true if successful
+ *
+ * See:
+ *
+ * -
+ * -
+ */
+static int hash_table_exists(const char *name,
+ tinyobj_material_table_t *hash_table) {
+ return hash_table_find(hash_djb2((const unsigned char *)name), hash_table) !=
+ NULL;
}
-static void hash_table_set(const char* name, size_t val, hash_table_t* hash_table)
-{
+/* Function: hash_table_set
+ * Inserts value into hash_table, in name; creates new entry if it doesn't
+ * exist
+ *
+ * See:
+ *
+ * -
+ * -
+ */
+static void hash_table_set(const char *name, size_t val,
+ tinyobj_material_table_t *hash_table) {
/* Hash name */
unsigned long hash = hash_djb2((const unsigned char *)name);
- hash_table_entry_t* entry = hash_table_find(hash, hash_table);
- if (entry)
- {
+ hash_table_entry_t *entry = hash_table_find(hash, hash_table);
+ if (entry) {
entry->value = (long)val;
return;
}
@@ -698,850 +1333,1827 @@ static void hash_table_set(const char* name, size_t val, hash_table_t* hash_tabl
/* Expand if necessary
* Grow until the element has been added
*/
- do
- {
+ do {
hash_table_maybe_grow(hash_table->n + 1, hash_table);
- }
- while (hash_table_insert(hash, (long)val, hash_table) != HASH_TABLE_SUCCESS);
+ } while (hash_table_insert(hash, (long)val, hash_table) !=
+ TINYOBJ_HASH_TABLE_SUCCESS);
}
-static long hash_table_get(const char* name, hash_table_t* hash_table)
-{
- hash_table_entry_t* ret = hash_table_find(hash_djb2((const unsigned char*)(name)), hash_table);
- return ret->value;
+/* Function: hash_table_get
+ * Returns value of entity with name, -1 when failing
+ */
+static long hash_table_get(const char *name,
+ tinyobj_material_table_t *hash_table) {
+ hash_table_entry_t *ret =
+ hash_table_find(hash_djb2((const unsigned char *)(name)), hash_table);
+ return (ret) ? ret->value : -1;
}
+#else
+/***********************************************************************************************
+ * Group: UTHash interface to tinyobj
+ ***********************************************************************************************/
-static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev,
- size_t num_materials,
- tinyobj_material_t *new_mat) {
- tinyobj_material_t *dst;
- dst = (tinyobj_material_t *)TINYOBJ_REALLOC(
- prev, sizeof(tinyobj_material_t) * (num_materials + 1));
-
- dst[num_materials] = (*new_mat); /* Just copy pointer for char* members */
- return dst;
-}
+/* Sturcture: tinyobj_material_table_tEntry
+ *
+ * Material table entry information
+ *
+ * Fields:
+ *
+ * name - Nul terminated name (key)
+ * value - Value of this entity
+ * hh - UTHash handle
+ */
+typedef struct s_material_table_entry {
+ char *name; // Nul terminated name (key)
+ long value;
-static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out,
- size_t *num_materials_out,
- const char *filename,
- hash_table_t* material_table) {
- tinyobj_material_t material;
- size_t buffer_size = 128;
- char *linebuf;
- FILE *fp;
- size_t num_materials = 0;
- tinyobj_material_t *materials = NULL;
- int has_previous_material = 0;
- const char *line_end = NULL;
+ UT_hash_handle hh;
+} tinyobj_material_table_tEntry;
- if (materials_out == NULL) {
- return TINYOBJ_ERROR_INVALID_PARAMETER;
- }
+/* Structure: tinyobj_material_table_t
+ * Material table
+ */
+typedef struct s_material_table {
+ tinyobj_material_table_tEntry *head;
+} tinyobj_material_table_t;
- if (num_materials_out == NULL) {
- return TINYOBJ_ERROR_INVALID_PARAMETER;
- }
+// Group: UTHash interface to tinyobj
- (*materials_out) = NULL;
- (*num_materials_out) = 0;
+/* Constant: tinyobj_reserved
+ * Reserved key used by the hashtable implementation so the table isn't deleted
+ */
+const char *tinyobj_reserved = "_RESERVED_KEY_";
- fp = fopen(filename, "r");
- if (!fp) {
- fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno);
- return TINYOBJ_ERROR_FILE_OPERATION;
- }
+/* Function: tinyobj_hash_find
+ *
+ * Tries to find object with 'name' as key
+ *
+ * Parameters:
+ *
+ * table - Table to be used
+ * name - Key
+ * entryOUT - [out] Result
+ *
+ * Returns:
+ *
+ * Sets entry to a valid result if entry was found, otherwise sets it to NULL
+ */
+static void tinyobj_hash_find(tinyobj_material_table_t *table, const char *name,
+ tinyobj_material_table_tEntry **entryOUT) {
+ tinyobj_material_table_tEntry *entry = NULL;
+ if (table) HASH_FIND_STR(table->head, name, entry);
+ *entryOUT = entry;
+}
- /* Create a default material */
- initMaterial(&material);
+/* Function: tinyobj_hash_add
+ *
+ * Adds a material to the given material table
+ * If the material was already added its value is replaced
+ *
+ * Parameters:
+ *
+ * table - Table to be used
+ * name - Key
+ * length - Name length
+ * value - Value of the new entity
+ *
+ * Returns:
+ * Returns true if successful, false otherwise
+ */
+static int tinyobj_hash_add(tinyobj_material_table_t *table, const char *name,
+ size_t length, long value) {
+ tinyobj_material_table_tEntry *entry = NULL;
- linebuf = (char*)TINYOBJ_MALLOC(buffer_size);
- while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) {
- const char *token = linebuf;
+ if (!table) return TINYOBJ_HASH_TABLE_ERROR;
- line_end = token + strlen(token);
+ HASH_FIND_STR(table->head, name, entry);
+ if (entry) {
+ entry->value = value;
+ return TINYOBJ_HASH_TABLE_SUCCESS;
+ }
+ entry = TINYOBJ_MALLOC(sizeof(*entry));
+ if (!entry) return TINYOBJ_HASH_TABLE_ERROR;
- /* Skip leading space. */
- token += strspn(token, " \t");
+ entry->name = name;
+ entry->value = value;
- assert(token);
- if (token[0] == '\0') continue; /* empty line */
+ HASH_ADD_KEYPTR(hh, table->head, name, length, entry);
+ return TINYOBJ_HASH_TABLE_SUCCESS;
+}
- if (token[0] == '#') continue; /* comment line */
+/* Function: tinyobj_hash_free
+ *
+ * Frees all entries of a given tinyobj_material_table_t
+ *
+ * Parameters:
+ *
+ * table - Table to be freed
+ * bfree_name - Should the key (name) be freed
+ *
+ * Usually the name pointer is owned by the materials list by this point
+ *
+ * See , freeing could lead to a segfault
+ */
+static void tinyobj_hash_free(tinyobj_material_table_t *table,
+ unsigned char bfree_name) {
+ tinyobj_material_table_tEntry *entry, *tmp;
+ if (!table) return;
+
+ HASH_ITER(hh, table->head, entry, tmp) {
+ HASH_DEL(table->head, entry);
+ if (entry->name && bfree_name && entry->value != -1)
+ TINYOBJ_FREE(entry->name);
+ TINYOBJ_FREE(entry);
+ }
+}
- /* new mtl */
- if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
- char namebuf[4096];
-
- /* flush previous material. */
- if (has_previous_material) {
- materials = tinyobj_material_add(materials, num_materials, &material);
- num_materials++;
- } else {
- has_previous_material = 1;
- }
+/* Function: tinyobj_hash_init
+ * Initialises given table, and adds a reserved key
+ */
+static void tinyobj_hash_init(tinyobj_material_table_t *table) {
+ table->head = NULL;
+ tinyobj_hash_add(table, tinyobj_reserved, strlen(tinyobj_reserved), -1);
+}
- /* initial temporary material */
- initMaterial(&material);
+#endif // TINYOBJ_USE_UTHASH
+/***********************************************************************************************
+ * Group: Material handling (.mtl)
+ ***********************************************************************************************/
- /* set new mtl name */
- token += 7;
-#ifdef _MSC_VER
- sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
-#else
- sscanf(token, "%s", namebuf);
-#endif
- material.name = my_strdup(namebuf, (size_t) (line_end - token));
+/* Function: initMaterial
+ * Defaults given material
+ */
+static void initMaterial(tinyobj_material_t *material) {
+ material->name = NULL;
+ material->ambient_texname = NULL;
+ material->diffuse_texname = NULL;
+ material->specular_texname = NULL;
+ material->specular_highlight_texname = NULL;
+ material->bump_texname = NULL;
+ material->displacement_texname = NULL;
+ material->alpha_texname = NULL;
+ material->ambient.r = material->ambient.g = material->ambient.b = 0.f;
+ material->diffuse.r = material->diffuse.g = material->diffuse.b = 0.f;
+ material->specular.r = material->specular.g = material->specular.b = 0.f;
+ material->transmittance.r = material->transmittance.g =
+ material->transmittance.b = 0.f;
+ material->emission.r = material->emission.g = material->emission.b = 0.f;
- /* Add material to material table */
- if (material_table)
- hash_table_set(material.name, num_materials, material_table);
+ material->illum = 0;
+ material->dissolve = 1.f;
+ material->shininess = 1.f;
+ material->ior = 1.f;
+}
- continue;
+/* Function: tinyobj_mtl_parse_map
+ *
+ * Parses a texture name from token ('map_')
+ * > Supported map types:
+ * > map_Ka - ambient texture
+ * > map_Kd - diffuse texture
+ * > map_Ks - specular texture
+ * > map_Ns - specular highlight texture
+ * > map_bump - bump texture
+ * > map_d - alpha texture
+ *
+ * Parameters:
+ *
+ * material - Material to be filled
+ * token - Buffer
+ * line_end - Buffer end (halting point)
+ *
+ * Returns:
+ * -
+ * -
+ */
+static int tinyobj_mtl_parse_map(tinyobj_material_t *material,
+ const char **token, const char *line_end) {
+ *token += 4;
+ switch ((*token)[0]) {
+ case 'K': {
+ char subtype = (*token)[1];
+ if (!IS_SPACE((*token)[2])) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ *token += 3;
+ switch (subtype) {
+ case 'a': // ambient texture
+ material->ambient_texname = strdup_ml(*token, (line_end - (*token)));
+ break;
+ case 'd': // diffuse texture
+ material->diffuse_texname = strdup_ml(*token, (line_end - (*token)));
+ break;
+ case 's': // specular texture
+ material->specular_texname = strdup_ml(*token, (line_end - (*token)));
+ break;
+ default:
+ return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ }
+ break;
}
+ case 'N': // specular highlight texture
+ if ((*token)[1] != 's' || !IS_SPACE((*token)[2]))
+ return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ *token += 3;
+ material->specular_highlight_texname =
+ strdup_ml(*token, (line_end - (*token)));
+ break;
+ case 'b': // bump texture
+ if (strncmp(*token, "bump", 4) && !IS_SPACE((*token)[5]))
+ return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ *token += 6;
+ material->bump_texname = strdup_ml(*token, (line_end - (*token)));
+ break;
+ case 'd': // alpha texture
+ if (!IS_SPACE((*token)[1])) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ *token += 2;
+ material->alpha_texname = strdup_ml(*token, (line_end - (*token)));
+ break;
+ }
+ return TINYOBJ_SUCCESS;
+}
- /* ambient */
- if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) {
- float r, g, b;
- token += 2;
- parseFloat3(&r, &g, &b, &token);
- material.ambient[0] = r;
- material.ambient[1] = g;
- material.ambient[2] = b;
- continue;
- }
+/* Function: tinyobj_mtl_parse_optical
+ *
+ * Parses an optical coefficient from token ('N')
+ * > Supported coefficient types:
+ * > Ni - Index Of Refraction (ior) 'optical density'
+ * > Ns - Specular exponent 'shininess'
+ *
+ * Parameters:
+ *
+ * material - Material to be filled
+ * token - Buffer
+ *
+ * Returns:
+ * -
+ * -
+ */
+static int tinyobj_mtl_parse_optical(tinyobj_material_t *material,
+ const char **token) {
+ char subtype = (*token)[1];
- /* diffuse */
- if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) {
- float r, g, b;
- token += 2;
- parseFloat3(&r, &g, &b, &token);
- material.diffuse[0] = r;
- material.diffuse[1] = g;
- material.diffuse[2] = b;
- continue;
- }
+ if (!IS_SPACE(((*token)[2]))) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
- /* specular */
- if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) {
- float r, g, b;
- token += 2;
- parseFloat3(&r, &g, &b, &token);
- material.specular[0] = r;
- material.specular[1] = g;
- material.specular[2] = b;
- continue;
- }
+ *token += 3;
+ switch (subtype) {
+ case 'i': // ior (index of refraction)
+ material->ior = parseFloat(token);
+ break;
+ case 's': // shininess
+ material->shininess = parseFloat(token);
+ break;
+ default:
+ return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ }
+ return TINYOBJ_SUCCESS;
+}
- /* transmittance */
- if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
- float r, g, b;
- token += 2;
- parseFloat3(&r, &g, &b, &token);
- material.transmittance[0] = r;
- material.transmittance[1] = g;
- material.transmittance[2] = b;
- continue;
- }
+/* Function: tinyobj_mtl_parse_color
+ *
+ * Parses a color coefficient from token ('K')
+ * > Supported coefficient types:
+ * > Ka - Ambient
+ * > Kd - Diffuse
+ * > Ks - Specular
+ * > Kt - Transmittance
+ * > Ke - Emissive
+ *
+ * Parameters:
+ *
+ * material - Material to be filled
+ * token - Buffer
+ *
+ * Returns:
+ * -
+ * -
+ */
+static int tinyobj_mtl_parse_color(tinyobj_material_t *material,
+ const char **token) {
+ float r, g, b;
+ char subtype = (*token)[1];
+
+ if (!IS_SPACE(((*token)[2]))) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+
+ *token += 3;
+ parseFloat3(&r, &g, &b, token);
+ switch (subtype) {
+ case 'a': // ambient
+ material->ambient.r = r;
+ material->ambient.g = g;
+ material->ambient.b = b;
+ break;
+ case 'd': // diffuse
+ material->diffuse.r = r;
+ material->diffuse.g = g;
+ material->diffuse.b = b;
+ break;
+ case 's': // specular
+ material->specular.r = r;
+ material->specular.g = g;
+ material->specular.b = b;
+ break;
+ case 't': // transmittance
+ material->transmittance.r = r;
+ material->transmittance.g = g;
+ material->transmittance.b = b;
+ break;
+ case 'e': // emission
+ material->emission.r = r;
+ material->emission.g = g;
+ material->emission.b = b;
+ break;
+ default:
+ return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ }
+ return TINYOBJ_SUCCESS;
+}
- /* ior(index of refraction) */
- if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) {
- token += 2;
- material.ior = parseFloat(&token);
- continue;
- }
+/* Function: tinyobj_parse_and_index_mtl_file
+ *
+ * Parses and indexes a given material file
+ *
+ * Parameters:
+ *
+ * materials_out - [out] Completed list of materials
+ * num_materials_out - [out] Length of materials_out
+ * filename - File to be parsed
+ * material_table - Table to be filled (can be NULL)
+ *
+ * Returns:
+ * -
+ * -
+ * -
+ * -
+ */
+static int tinyobj_parse_and_index_mtl_file(
+ tinyobj_material_t **materials_out, unsigned int *num_materials_out,
+ const char *filename, tinyobj_material_table_t *material_table) {
+ int buffer_size = 128; // dynamic_fgets supports only integers as sizes
+ char *linebuf;
+ FILE *fp;
+ tinyobj_material_t *material = NULL;
+ tinyobj_material_t *material_list = NULL;
+ size_t material_list_length = 0;
+ unsigned int material_count = 0;
+ const char *line_end = NULL;
- /* emission */
- if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) {
- float r, g, b;
- token += 2;
- parseFloat3(&r, &g, &b, &token);
- material.emission[0] = r;
- material.emission[1] = g;
- material.emission[2] = b;
- continue;
- }
+ if (materials_out == NULL || num_materials_out == NULL)
+ return TINYOBJ_ERROR_INVALID_PARAMETER;
- /* shininess */
- if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) {
- token += 2;
- material.shininess = parseFloat(&token);
- continue;
- }
+ (*materials_out) = NULL;
+ (*num_materials_out) = 0;
- /* illum model */
- if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) {
- token += 6;
- material.illum = parseInt(&token);
- continue;
- }
+ material_list =
+ malloc(sizeof(*material_list) * TINYOBJ_MATERIAL_INITIAL_COUNT);
+ material_list_length = TINYOBJ_MATERIAL_INITIAL_COUNT;
+ if (!material_list) return TINYOBJ_ERROR_MEMORY;
+ material = &material_list[0];
+ initMaterial(material);
- /* dissolve */
- if ((token[0] == 'd' && IS_SPACE(token[1]))) {
- token += 1;
- material.dissolve = parseFloat(&token);
- continue;
- }
- if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
- token += 2;
- /* Invert value of Tr(assume Tr is in range [0, 1]) */
- material.dissolve = 1.0f - parseFloat(&token);
- continue;
- }
+ fp = fopen(filename, "r");
+ if (!fp) {
+ free(material_list);
+ return TINYOBJ_ERROR_FILE_OPERATION;
+ }
- /* ambient texture */
- if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
- token += 7;
- material.ambient_texname = my_strdup(token, (size_t) (line_end - token));
- continue;
- }
+ linebuf = TINYOBJ_MALLOC(buffer_size);
+ while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) {
+ const char *token = linebuf;
+ int retval = TINYOBJ_ERROR_NOT_SET;
+ line_end = token + strlen(token);
- /* diffuse texture */
- if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) {
- token += 7;
- material.diffuse_texname = my_strdup(token, (size_t) (line_end - token));
- continue;
- }
+ // Skip leading space
+ token += strspn(token, " \t");
- /* specular texture */
- if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) {
- token += 7;
- material.specular_texname = my_strdup(token, (size_t) (line_end - token));
- continue;
- }
+ assert(token);
+ if (token[0] == '\0') continue; // Empty line
+ if (token[0] == '#') continue; // Comment line
- /* specular highlight texture */
- if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) {
+ // New material
+ if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
+ char namebuf[4096];
+ material_count++;
+ // Update current material
+ if (material->name) {
+ if (material_count == material_list_length) {
+ tinyobj_material_t *temp;
+ size_t temp_length;
+ temp_length = material_list_length * TINYOBJ_MATERIAL_GROWTH_FACTOR;
+ temp = TINYOBJ_REALLOC(material_list,
+ sizeof(*material_list) * temp_length);
+ if (!temp) {
+ free(material_list);
+ return TINYOBJ_ERROR_MEMORY;
+ }
+ material_list_length = temp_length;
+ material_list = temp;
+ }
+ material = &material_list[material_count];
+ }
+ initMaterial(material);
token += 7;
- material.specular_highlight_texname = my_strdup(token, (size_t) (line_end - token));
- continue;
- }
-
- /* bump texture */
- if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) {
- token += 9;
- material.bump_texname = my_strdup(token, (size_t) (line_end - token));
- continue;
- }
-
- /* alpha texture */
- if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) {
- token += 6;
- material.alpha_texname = my_strdup(token, (size_t) (line_end - token));
+#ifdef _MSC_VER
+ sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
+#else
+ sscanf(token, "%s", namebuf);
+#endif
+ material->name = strdup_ml(namebuf, (line_end - token));
+ // Add material to the table
+ if (material_table) {
+#ifndef TINYOBJ_USE_UTHASH
+ hash_table_set(material->name, material_count, material_table);
+#else
+ tinyobj_hash_add(material_table, material->name, strlen(material->name),
+ material_count);
+#endif // TINYOBJ_USE_UTHASH
+ }
continue;
}
- /* bump texture */
- if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) {
- token += 5;
- material.bump_texname = my_strdup(token, (size_t) (line_end - token));
- continue;
+ switch (token[0]) {
+ case 'K': // Color coefficients
+ retval = tinyobj_mtl_parse_color(material, &token);
+ break;
+ case 'N': // Optical coefficients
+ retval = tinyobj_mtl_parse_optical(material, &token);
+ break;
+ case 'd': // Non-transparency to be alpha 'dissolve' 'd'
+ // 'disp' Displacement texture
+ if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
+ token += 5;
+ material->displacement_texname = strdup_ml(token, (line_end - token));
+ continue;
+ }
+ // Fall-through
+ case 'T': { // Transparency to be alpha
+ float tr;
+ unsigned char type;
+
+ if (!(token[1] == 'r' && IS_SPACE(token[2])) &&
+ !(token[0] == 'd' && IS_SPACE(token[1]))) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ type = (token[1] == 'r') ? 2 : 1;
+ token += type;
+ tr = parseFloat(&token);
+ if (tr < 0 || tr > 1) { // Must be in range [0, 1]
+ fprintf(stderr,
+ "%s: Error reading file '%s': Invalid transparency parameter "
+ "'%f', expected range [0, 1]\n",
+ __func__, filename, tr);
+ return TINYOBJ_ERROR_MALFORMED_PARAMETER;
+ }
+ // Invert value of Tr
+ material->dissolve = (type == 2) ? 1.0f - tr : tr;
+ continue;
+ }
+ case 'm': // texture maps
+ if (strncmp(token, "map_", 4)) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ retval = tinyobj_mtl_parse_map(material, &token, line_end);
+ break;
+ case 'i': // illum model
+ if (strncmp(token, "illum", 5) || !IS_SPACE(token[5])) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ token += 6;
+ material->illum = parseInt(&token);
+ continue;
+ case 'b':
+ if (strncmp(token, "bump", 4) || !IS_SPACE(token[4])) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ token += 5;
+ material->bump_texname = strdup_ml(token, (line_end - token));
+ continue;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\0':
+ continue;
+ default:
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
}
-
- /* displacement texture */
- if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
- token += 5;
- material.displacement_texname = my_strdup(token, (size_t) (line_end - token));
- continue;
+ if (retval == TINYOBJ_ERROR_UNKNOWN_PARAMETER) {
+ char *command_name = NULL;
+ size_t length;
+ length = length_until_space(token, ((line_end - token)) + 1);
+ command_name = strndup(token, length);
+ fprintf(stderr, "%s: Unsupported command '%s'\n", __func__, command_name);
+ TINYOBJ_FREE(command_name);
}
-
- /* @todo { unknown parameter } */
}
- if (material.name) {
- /* Flush last material element */
- materials = tinyobj_material_add(materials, num_materials, &material);
- num_materials++;
- }
+ (*num_materials_out) = material_count;
+ (*materials_out) = material_list;
- (*num_materials_out) = num_materials;
- (*materials_out) = materials;
-
- if (linebuf) {
- TINYOBJ_FREE(linebuf);
- }
+ if (linebuf) TINYOBJ_FREE(linebuf);
return TINYOBJ_SUCCESS;
}
+// Group: Aplication Programming Interface (.mtl)
+
+/* Function: tinyobj_parse_mtl_file
+ * Parses a material file, see
+ */
int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
- size_t *num_materials_out,
+ unsigned int *num_materials_out,
const char *filename) {
- return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL);
-}
-
-
-typedef enum {
- COMMAND_EMPTY,
- COMMAND_V,
- COMMAND_VN,
- COMMAND_VT,
- COMMAND_F,
- COMMAND_G,
- COMMAND_O,
- COMMAND_USEMTL,
- COMMAND_MTLLIB
-
-} CommandType;
-
-typedef struct {
- float vx, vy, vz;
- float nx, ny, nz;
- float tx, ty;
-
- /* @todo { Use dynamic array } */
- tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
- size_t num_f;
-
- int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE];
- size_t num_f_num_verts;
+ int retval = tinyobj_parse_and_index_mtl_file(
+ materials_out, num_materials_out, filename, NULL);
+ return retval;
+}
- const char *group_name;
- unsigned int group_name_len;
- int pad0;
+/* Function: tinyobj_material_free
+ *
+ * Frees material data
+ *
+ * Parameters:
+ *
+ * materials - Material list to be freed
+ * num_materials - Length of materials
+ */
+void tinyobj_material_free(tinyobj_material_t *materials,
+ unsigned int num_materials) {
+ unsigned int i;
+ if (!materials || !num_materials) return;
- const char *object_name;
- unsigned int object_name_len;
- int pad1;
+ for (i = 0; i < num_materials; i++) {
+ if (materials[i].name) TINYOBJ_FREE(materials[i].name);
+ if (materials[i].ambient_texname)
+ TINYOBJ_FREE(materials[i].ambient_texname);
+ if (materials[i].diffuse_texname)
+ TINYOBJ_FREE(materials[i].diffuse_texname);
+ if (materials[i].specular_texname)
+ TINYOBJ_FREE(materials[i].specular_texname);
+ if (materials[i].specular_highlight_texname)
+ TINYOBJ_FREE(materials[i].specular_highlight_texname);
+ if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname);
+ if (materials[i].displacement_texname)
+ TINYOBJ_FREE(materials[i].displacement_texname);
+ if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname);
+ }
+ TINYOBJ_FREE(materials);
+}
- const char *material_name;
- unsigned int material_name_len;
- int pad2;
+/***********************************************************************************************
+ * Group: Object handling (.obj)
+ ***********************************************************************************************/
- const char *mtllib_name;
- unsigned int mtllib_name_len;
+/* Enum: CommandType
+ *
+ * Command identifiers used when parsing
+ *
+ *
+ *
+ * COMMAND_EMPTY - No command
+ * COMMAND_V - 'v' Vertex
+ * COMMAND_VN - 'vn' Vertex normal
+ * COMMAND_VT - 'vt' Texture coordinate
+ * COMMAND_VP - 'vp' Parameter space vertex
+ * COMMAND_F - 'f' Face
+ * COMMAND_P - 'p' Point
+ * COMMAND_L - 'l' Line
+ * COMMAND_G - 'g' Group name
+ * COMMAND_O - 'o' Object name
+ * COMMAND_S - 's' Smoothing group
+ * COMMAND_USEMTL - 'usemtl' Use material
+ * COMMAND_MTLLIB - 'mtllib' Load material
+ */
+typedef enum {
+ COMMAND_EMPTY, //< No command
+ // @see tinyobj_obj_parse_vertex
+ COMMAND_V, //< 'v' Vertex
+ COMMAND_VN, //< 'vn' Vertex normal
+ COMMAND_VT, //< 'vt' Texture coordinate
+ COMMAND_VP, //< 'vp' Parameter space vertex
+
+ COMMAND_F, //< 'f' Face @see tinyobj_obj_parse_face
+ COMMAND_P, //< 'p' Point @see tinyobj_obj_parse_point
+ COMMAND_L, //< 'l' Line @see tinyobj_obj_parse_line
+
+ COMMAND_G, //< 'g' Group name
+ COMMAND_O, //< 'o' Object name
+ COMMAND_S, //< 's' Smoothing group
+ COMMAND_USEMTL, //< 'usemtl' Use material
+ COMMAND_MTLLIB //< 'mtllib' Load material
+} CommandType;
+/* Structure: Command
+ *
+ * Command information used to parse obj files
+ *
+ *
+ * Fields:
+ *
+ * type - Command
+ * info - Specific command information
+ */
+typedef struct s_tinyobj_command {
CommandType type;
+ /* Structure: Command.u_information
+ *
+ * Specific command information
+ *
+ * Fields:
+ *
+ * v - Geometric vertex
+ * vn - Vertex normal
+ * vt - Texture vertex
+ * vp - Parameter space vertex
+ * f - Face
+ * l - Line
+ * p - Point
+ * g - Group
+ * o - Object
+ * usemtl - Use material
+ * mtllib - Material lib (file)
+ * smoothing_id - Smoothing group
+ *
+ * See:
+ *
+ *
+ */
+ union u_information {
+ /**
+ * Vertex Data
+ **/
+ tinyobj_vertex_t v;
+ tinyobj_vertex_normal_t vn;
+ tinyobj_vertex_texture_t vt;
+ tinyobj_vertex_param_t vp;
+ /**
+ * Element data
+ **/
+ tinyobj_face_t f;
+ tinyobj_line_t l;
+ tinyobj_point_t p;
+ /* Structure: Command.s_group_information
+ *
+ * Grouping and Display/Render attributes
+ *
+ * Fields:
+ *
+ * Name - Name of group
+ * len - Group name length
+ */
+ struct s_group_information {
+ const char *name; //< Name of group
+ size_t len; //< Group name length
+ } g, o, usemtl, mtllib;
+ int smoothing_id; //< 's' Smoothing group
+ } info;
} Command;
-static int parseLine(Command *command, const char *p, size_t p_len,
- int triangulate) {
- char linebuf[4096];
- const char *token;
- assert(p_len < 4095);
-
- memcpy(linebuf, p, p_len);
- linebuf[p_len] = '\0';
-
- token = linebuf;
-
- command->type = COMMAND_EMPTY;
-
- /* Skip leading space. */
- skip_space(&token);
-
- assert(token);
- if (token[0] == '\0') { /* empty line */
- return 0;
- }
+/* Structure: CommandInformation
+ *
+ * General information regarding the commands of a given file
+ *
+ * Fields:
+ *
+ * command_list - Array of commands
+ * mtllib_line_index - Index of MLLIB in command list (-1 no command)
+ * count - command_list count
+ * counter - Counter of command types
+ */
+typedef struct s_command_information {
+ Command *command_list; //< Array of commands
+ int mtllib_line_index; //< Index of MLLIB in command list (-1 no command)
+ size_t count; //< Command count
+ /* Structure: CommandInformation.s_counter
+ * Counter of command types
+ *
+ * Fields:
+ *
+ * v - Count of geometric vertices
+ * vn - Count of vertex normals
+ * vt - Count of texture vertices
+ * vp - Count of parameter space vertices
+ * f - Count of faces
+ * l - Count of lines
+ * p - Count of points
+ * shapes - Shapes
+ */
+ struct s_counter {
+ size_t v, vn, vt, vp;
+ size_t f, l, p;
+ size_t shapes;
+ } counter;
+} CommandInformation;
- if (token[0] == '#') { /* comment line */
- return 0;
- }
+// Group: Object handling (.obj)
- /* vertex */
- if (token[0] == 'v' && IS_SPACE((token[1]))) {
- float x, y, z;
- token += 2;
- parseFloat3(&x, &y, &z, &token);
- command->vx = x;
- command->vy = y;
- command->vz = z;
- command->type = COMMAND_V;
- return 1;
- }
+/* Function: tinyobj_obj_free_point
+ * Frees allocated data of a given point
+ */
+static void tinyobj_obj_free_point(tinyobj_point_t *p) {
+ if (p) return;
+ if (p->v_idx) TINYOBJ_FREE(p->v_idx);
+}
- /* normal */
- if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
- float x, y, z;
- token += 3;
- parseFloat3(&x, &y, &z, &token);
- command->nx = x;
- command->ny = y;
- command->nz = z;
- command->type = COMMAND_VN;
- return 1;
- }
+/* Function: tinyobj_obj_parse_point
+ *
+ * Parses a point from token ('p')
+ *
+ * Parameters:
+ *
+ * command - Command information to be filled
+ * token - Token to be parsed
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ * -
+ */
+static int tinyobj_obj_parse_point(Command *command, const char **token) {
+ tinyobj_point_t *p;
+ if (!IS_SPACE(((*token)[1]))) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
- /* texcoord */
- if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
- float x, y;
- token += 3;
- parseFloat2(&x, &y, &token);
- command->tx = x;
- command->ty = y;
- command->type = COMMAND_VT;
- return 1;
+ *token += 2;
+ skip_space(token);
+ if ((*token)[0] == '#' || !IS_DIGIT(*token[0]))
+ return TINYOBJ_ERROR_MALFORMED_PARAMETER;
+
+ p = &command->info.p;
+ p->count = 0;
+ p->length = TINYOBJ_POINT_INITIAL_COUNT;
+ p->v_idx = TINYOBJ_MALLOC(sizeof(*p->v_idx) * p->length);
+ if (!p->v_idx) return TINYOBJ_ERROR_MEMORY;
+
+ command->type = COMMAND_P;
+ while (!IS_NEW_LINE((*token)[0])) {
+ if ((*token)[0] == '#') break;
+ if (p->count == p->length) {
+ int *tmp;
+ p->length *= TINYOBJ_POINT_GROWTH_FACTOR;
+ tmp = TINYOBJ_REALLOC(p->v_idx, sizeof(*p->v_idx) * p->length);
+ if (tmp) {
+ p->v_idx = tmp;
+ continue;
+ }
+ TINYOBJ_FREE(p->v_idx);
+ return TINYOBJ_ERROR_MEMORY;
+ }
+ p->v_idx[p->count] = parseInt(token);
+ p->count++;
+ skip_space(token);
}
+ return TINYOBJ_SUCCESS;
+}
- /* face */
- if (token[0] == 'f' && IS_SPACE((token[1]))) {
- size_t num_f = 0;
-
- tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
- token += 2;
- skip_space(&token);
+/* Function: tinyobj_triplet_list_grow
+ *
+ * Grows the given triplet list to a new size
+ * If the growth fails the list is unchanged
+ *
+ * Parameters:
+ *
+ * list - List to grow
+ * new_len - New length
+ *
+ * Returns:
+ *
+ * True if successful, false otherwise
+ */
+unsigned char tinyobj_triplet_list_grow(tinyobj_vertex_index_t **list,
+ size_t new_len) {
+ tinyobj_vertex_index_t *new_list;
- while (!IS_NEW_LINE(token[0])) {
- tinyobj_vertex_index_t vi = parseRawTriple(&token);
- skip_space_and_cr(&token);
+ new_list = TINYOBJ_REALLOC(*list, new_len * sizeof(**list));
+ if (!new_list) return 0;
+ *list = new_list;
+ return 1;
+}
- f[num_f] = vi;
- num_f++;
- }
+/* Function: tinyobj_triplet_list_new
+ *
+ * Allocates memory for a new triplet list
+ *
+ * Parameters:
+ *
+ * len - Initial length of list
+ *
+ * Returns:
+ *
+ * Pointer to new list when successful, NULL if failure
+ */
+static tinyobj_vertex_index_t *tinyobj_triplet_list_new(size_t len) {
+ return TINYOBJ_MALLOC(sizeof(tinyobj_vertex_index_t) * len);
+}
- command->type = COMMAND_F;
+/* Function: tinyobj_obj_free_line
+ * Frees allocated data of a given line
+ */
+static void tinyobj_obj_free_line(tinyobj_line_t *l) {
+ if (l) return;
+ if (l->couple_list) TINYOBJ_FREE(l->couple_list);
+}
- if (triangulate) {
- size_t k;
- size_t n = 0;
+/* Function: tinyuobj_obj_parse_line
+ *
+ * Parses a line from token ('l')
+ *
+ * Parameters:
+ *
+ * command - Command information to be filled
+ * token - Token to be parsed
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ * -
+ */
+static int tinyobj_obj_parse_line(Command *command, const char **token) {
+ tinyobj_line_t *l;
+ if (!IS_SPACE(((*token)[1]))) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
- tinyobj_vertex_index_t i0 = f[0];
- tinyobj_vertex_index_t i1;
- tinyobj_vertex_index_t i2 = f[1];
+ l = &command->info.l;
+ l->count = 0;
+ l->length = TINYOBJ_COUPLE_INITIAL_COUNT;
+ l->couple_list = TINYOBJ_MALLOC(sizeof(*l->couple_list) * l->length);
+ if (!l->couple_list) return TINYOBJ_ERROR_MEMORY;
- assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
+ command->type = COMMAND_L;
- for (k = 2; k < num_f; k++) {
- i1 = i2;
- i2 = f[k];
- command->f[3 * n + 0] = i0;
- command->f[3 * n + 1] = i1;
- command->f[3 * n + 2] = i2;
+ *token += 2;
+ skip_space(token);
- command->f_num_verts[n] = 3;
- n++;
- }
- command->num_f = 3 * n;
- command->num_f_num_verts = n;
+ while (!IS_NEW_LINE((*token)[0])) {
+ tinyobj_vertex_index_t vi = parseRawTriple(token);
+ skip_space_and_cr(token);
- } else {
- size_t k = 0;
- assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
- for (k = 0; k < num_f; k++) {
- command->f[k] = f[k];
+ if (vi.vn_idx != TINYOBJ_INVALID_INDEX) { // This is a triple not a couple
+ TINYOBJ_FREE(l->couple_list);
+ return TINYOBJ_ERROR_MALFORMED_PARAMETER;
+ }
+ l->couple_list->v_idx = vi.v_idx;
+ l->couple_list->vt_idx = vi.vt_idx;
+ l->count++;
+ if (l->count == l->length) {
+ tinyobj_line_t *tmp;
+ l->length *= TINYOBJ_TRIPLET_GROWTH_FACTOR;
+ tmp =
+ TINYOBJ_REALLOC(l->couple_list, sizeof(*l->couple_list) * l->length);
+ if (tmp) {
+ l->couple_list = tmp;
+ continue;
}
-
- command->num_f = num_f;
- command->f_num_verts[0] = (int)num_f;
- command->num_f_num_verts = 1;
+ TINYOBJ_FREE(l->couple_list);
+ return TINYOBJ_ERROR_MALFORMED_PARAMETER;
}
+ }
- return 1;
+ if (l->count < 2) {
+ // A line must have at least 2 geometric vertices
+ fprintf(stderr,
+ "%s: malformed line with %ld geometric vertices (minimum of l)\n",
+ __func__, l->count);
+ TINYOBJ_FREE(l->couple_list);
+ return TINYOBJ_ERROR_MALFORMED_PARAMETER;
}
+ return TINYOBJ_SUCCESS;
+}
+
+/* Function: tinyobj_obj_free_face
+ * Frees allocated data of a given face
+ */
+static void tinyobj_obj_free_face(tinyobj_face_t *f) {
+ if (!f) return;
+ if (f->triplet_list) TINYOBJ_FREE(f->triplet_list);
+}
+
+/* Function: tinyobj_obj_parse_face
+ *
+ * Parses a face from token ('f') and fills given command with face information
+ *
+ * Parameters:
+ *
+ * command - Command information to be filled
+ * token - Token to be parsed
+ * triangulate - (bool) Should this face be triangulated?
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ * -
+ */
+static int tinyobj_obj_parse_face(Command *command, const char **token,
+ int triangulate) {
+ tinyobj_face_t *f;
+ tinyobj_vertex_index_t *temp_triplet_list;
+ unsigned int triplet_count, triplet_length, triangle_count;
+ unsigned char last_corner =
+ 0; // Is the next vertex index the last corner of a face?
- /* use mtl */
- if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) {
- token += 7;
+ if (!IS_SPACE(((*token)[1]))) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
- skip_space(&token);
- command->material_name = p + (token - linebuf);
- command->material_name_len = (unsigned int)length_until_newline(
- token, (p_len - (size_t)(token - linebuf)) + 1);
- command->type = COMMAND_USEMTL;
+ f = &command->info.f;
- return 1;
- }
+ temp_triplet_list = tinyobj_triplet_list_new(TINYOBJ_TRIPLET_INITIAL_COUNT);
+ triplet_length = TINYOBJ_TRIPLET_INITIAL_COUNT;
+ triplet_count = 0;
+ triangle_count = 0;
+ if (!temp_triplet_list) return TINYOBJ_ERROR_MEMORY;
- /* load mtl */
- if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
- /* By specification, `mtllib` should be appear only once in .obj */
- token += 7;
+ command->type = COMMAND_F;
- skip_space(&token);
- command->mtllib_name = p + (token - linebuf);
- command->mtllib_name_len = (unsigned int)length_until_newline(
- token, p_len - (size_t)(token - linebuf)) +
- 1;
- command->type = COMMAND_MTLLIB;
+ *token += 2;
+ skip_space(token);
- return 1;
+ if ((*token)[0] == '#') { // Unexpected comment
+ TINYOBJ_FREE(temp_triplet_list);
+ return TINYOBJ_ERROR_MALFORMED_PARAMETER;
}
- /* group name */
- if (token[0] == 'g' && IS_SPACE((token[1]))) {
- /* @todo { multiple group name. } */
- token += 2;
-
- command->group_name = p + (token - linebuf);
- command->group_name_len = (unsigned int)length_until_newline(
- token, p_len - (size_t)(token - linebuf)) +
- 1;
- command->type = COMMAND_G;
+ while (!IS_NEW_LINE((*token)[0])) {
+ tinyobj_vertex_index_t vi = parseRawTriple(token);
+ skip_space_and_cr(token);
+ if ((*token)[0] == '#') break;
+
+ /**
+ * When triangulating we parse the vertices as if they were part of
+ * a triangle fan, so every three indices the next is always the first
+ * triplet of this face
+ **/
+ if (triangulate && !last_corner && triplet_count > 2) {
+ if (triplet_count == 3) triangle_count++; // Count first triangle
+ temp_triplet_list[triplet_count] = temp_triplet_list[0];
+ temp_triplet_list[triplet_count + 1] =
+ temp_triplet_list[triplet_count - 1];
+ temp_triplet_list[triplet_count + 2] = vi;
+ triplet_count += 3;
+ triangle_count++;
+ last_corner = 1;
+ } else {
+ temp_triplet_list[triplet_count] = vi;
+ triplet_count++;
+ last_corner = 0;
+ }
- return 1;
+ if (triplet_count == triplet_length) {
+ triplet_length *= TINYOBJ_TRIPLET_GROWTH_FACTOR;
+ if (tinyobj_triplet_list_grow(&temp_triplet_list, triplet_length))
+ continue;
+ TINYOBJ_FREE(temp_triplet_list);
+ return TINYOBJ_ERROR_MEMORY;
+ }
}
-
- /* object name */
- if (token[0] == 'o' && IS_SPACE((token[1]))) {
- /* @todo { multiple object name? } */
- token += 2;
-
- command->object_name = p + (token - linebuf);
- command->object_name_len = (unsigned int)length_until_newline(
- token, p_len - (size_t)(token - linebuf)) +
- 1;
- command->type = COMMAND_O;
-
- return 1;
+ if (f->count < 3) {
+ // A face must have at least 3 geometric vertices
+ fprintf(stderr,
+ "%s: malformed face with %ld geometric vertices (minimum of 3)\n",
+ __func__, f->count);
+ TINYOBJ_FREE(temp_triplet_list);
+ return TINYOBJ_ERROR_MALFORMED_PARAMETER;
}
- return 0;
+ f->triplet_list = temp_triplet_list;
+ f->count = triplet_count;
+ f->length = triplet_length;
+ f->triangle_count = (triangulate) ? triangle_count : 1;
+ return TINYOBJ_SUCCESS;
}
-typedef struct {
- size_t pos;
- size_t len;
-} LineInfo;
-
-static int is_line_ending(const char *p, size_t i, size_t end_i) {
- if (p[i] == '\0') return 1;
- if (p[i] == '\n') return 1; /* this includes \r\n */
- if (p[i] == '\r') {
- if (((i + 1) < end_i) && (p[i + 1] != '\n')) { /* detect only \r case */
- return 1;
- }
+/* Function: tinyobj_obj_parse_vertex
+ *
+ * Parses a vertex from token ('v')
+ * > v, vn, vt, vp
+ *
+ * Parameters:
+ *
+ * command - Command information to be filled
+ * token - Token to be parsed
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ * -
+ */
+static int tinyobj_obj_parse_vertex(Command *command, const char **token) {
+ float tmp;
+ unsigned char retval;
+ char subtype = (*token)[1];
+ char type = (IS_SPACE(subtype)) ? 2 : 3;
+
+ if (!IS_SPACE(((*token)[type - 1]))) return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ *token += type;
+
+ switch (subtype) {
+ case ' ':
+ case '\t': // Geometric vertex
+ command->type = COMMAND_V;
+ parseFloat3(&command->info.v.x, &command->info.v.y, &command->info.v.z,
+ token);
+ command->info.v.weight = (try_parse_float(token, &tmp)) ? tmp : 1.0f;
+ break;
+ case 'n': // Vertex normal
+ command->type = COMMAND_VN;
+ parseFloat3(&command->info.vn.i, &command->info.vn.j, &command->info.vn.k,
+ token);
+ break;
+ case 't': // Texture vertex
+ command->type = COMMAND_VT;
+ command->info.vt.u = parseFloat(token);
+ retval = try_parse_float(token, &tmp);
+ command->info.vt.v = (retval) ? tmp : 0.0f;
+ if (retval)
+ command->info.vt.w = (try_parse_float(token, &tmp)) ? tmp : 0.0f;
+ break;
+ case 'p': // Parameter space vertex
+ command->type = COMMAND_VP;
+ command->info.vp.u = parseFloat(token);
+ retval = try_parse_float(token, &tmp);
+ command->info.vp.v = (retval) ? tmp : 0.0f;
+ if (retval)
+ command->info.vp.weight = (try_parse_float(token, &tmp)) ? tmp : 0.0f;
+ break;
+ default:
+ return TINYOBJ_ERROR_UNKNOWN_PARAMETER;
}
- return 0;
+ return TINYOBJ_SUCCESS;
}
-int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
- size_t *num_shapes, tinyobj_material_t **materials_out,
- size_t *num_materials_out, const char *buf, size_t len,
- unsigned int flags) {
- LineInfo *line_infos = NULL;
- Command *commands = NULL;
- size_t num_lines = 0;
-
- size_t num_v = 0;
- size_t num_vn = 0;
- size_t num_vt = 0;
- size_t num_f = 0;
- size_t num_faces = 0;
-
- int mtllib_line_index = -1;
+/* Function: tinyobj_parse_obj_line
+ *
+ * Parses line of an .obj file
+ *
+ * Parameters:
+ *
+ * command_info - Command information list
+ * pos - Current position in command information
+ * p - String containing line
+ * p_len - Line length
+ * flags - Reading flags
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ * -
+ */
+static int tinyobj_parse_obj_line(CommandInformation *command_info, int pos,
+ const char *p, size_t p_len, int flags) {
+ char linebuf[4096];
+ const char *token;
+ int retval = TINYOBJ_ERROR_NOT_SET;
+ Command *command = NULL;
+ assert(p_len < 4095);
- tinyobj_material_t *materials = NULL;
- size_t num_materials = 0;
+ memcpy(linebuf, p, p_len);
+ linebuf[p_len] = '\0';
- hash_table_t material_table;
+ token = linebuf;
+ command = &command_info->command_list[pos];
+ command->type = COMMAND_EMPTY;
- if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER;
- if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
- if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
- if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
- if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
- if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
- if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
+ /* Skip leading space. */
+ skip_space(&token);
- tinyobj_attrib_init(attrib);
- /* 1. Find '\n' and create line data. */
- {
- size_t i;
- size_t end_idx = len;
- size_t prev_pos = 0;
- size_t line_no = 0;
- size_t last_line_ending = 0;
-
- /* Count # of lines. */
- for (i = 0; i < end_idx; i++) {
- if (is_line_ending(buf, i, end_idx)) {
- num_lines++;
- last_line_ending = i;
+ assert(token);
+ if (token[0] == '\0' || token[0] == '\r')
+ return TINYOBJ_NO_COMMAND; // Empty line
+ if (token[0] == '#') return TINYOBJ_NO_COMMAND; // Comment line
+
+ /**
+ * Initial parameter parsing
+ **/
+ switch (token[0]) {
+ /**
+ * Vertex data
+ **/
+ case 'v': // v, vt, vn, vp
+ retval = tinyobj_obj_parse_vertex(command, &token);
+ break;
+ /**
+ * @todo {
+ * . rational or non-rational forms of curve or surface type:
+ * basis matrix, Bezier, B-spline, Cardinal, Taylor (cstype)
+ * . degree (deg)
+ * . basis matrix (bmat)
+ * . step size (step)
+ * }
+ **/
+ /**
+ * Elements
+ **/
+ case 'f': // Faces
+ retval = tinyobj_obj_parse_face(command, &token,
+ flags & TINYOBJ_FLAG_TRIANGULATE);
+ break;
+ case 'l': // Lines
+ retval = tinyobj_obj_parse_line(command, &token);
+ case 'p': // Points
+ retval = tinyobj_obj_parse_point(command, &token);
+ break;
+ // @todo { curve (curv); 2D curve (curv2); surface (surf) }
+ /**
+ * Free-form curve/surface body statements
+ **/
+ /**
+ * @todo {
+ * . parameter values (parm)
+ * . outer trimming loop (trim)
+ * . inner trimming loop (hole)
+ * . special curve (scrv)
+ * . special point (sp)
+ * . end statement (end)
+ * }
+ **/
+ /**
+ * Connectivity between free-form surfaces
+ **/
+ // @todo { connect (con) }
+ /**
+ * Grouping
+ **/
+ case 'g': // Group name
+ /* @todo { multiple group name. } */
+ if (!IS_SPACE(token[1])) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
}
- }
- /* The last char from the input may not be a line
- * ending character so add an extra line if there
- * are more characters after the last line ending
- * that was found. */
- if (end_idx - last_line_ending > 0) {
- num_lines++;
- }
-
- if (num_lines == 0) return TINYOBJ_ERROR_EMPTY;
+ token += 2;
+ command->info.g.name = p + (token - linebuf);
+ command->info.g.len =
+ length_until_newline_comment_space(token, p_len - (token - linebuf)) +
+ 1;
+ command->type = COMMAND_G;
+ retval = TINYOBJ_SUCCESS;
+ break;
+ case 'o': // Object name
+ /* @todo { multiple object name? } */
+ if (!IS_SPACE(token[1])) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ token += 2;
+ command->info.o.name = p + (token - linebuf);
+ command->info.o.len =
+ length_until_newline_comment_space(token, p_len - (token - linebuf)) +
+ 1;
+ command->type = COMMAND_O;
+ retval = TINYOBJ_SUCCESS;
+ break;
+ case 's': // Smoothing group
+ if (!IS_SPACE(token[1])) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ token += 2;
+ command->type = COMMAND_S;
+ if (IS_DIGIT(token[0]))
+ command->info.smoothing_id = parseInt(&token);
+ else
+ command->info.smoothing_id = (token[1] == 'f') ? 0 : 1;
+ retval = TINYOBJ_SUCCESS;
+ break;
+ // @todo { merging group (mg) }
+ /**
+ * Display/Render attributes
+ **/
+ case 'u': // Material name
+ if (strncmp(token, "usemtl", 6) || !IS_SPACE((token[6]))) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ token += 7;
+ skip_space(&token);
+ command->info.usemtl.name = p + (token - linebuf);
+ command->info.usemtl.len = length_until_newline_comment_space(
+ token, (token, (p_len - (token - linebuf)) + 1));
+ command->type = COMMAND_USEMTL;
+ retval = TINYOBJ_SUCCESS;
+ break;
+ case 'm': // Material library
+ if (strncmp(token, "mtllib", 6) || !IS_SPACE((token[6]))) {
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ // By specification, `mtllib` should be appear only once in .obj
+ token += 7;
+ skip_space(&token);
+ command->info.mtllib.name = p + (token - linebuf);
+ command->info.mtllib.len =
+ length_until_newline_comment_space(token, p_len - (token - linebuf)) +
+ 1;
+ command->type = COMMAND_MTLLIB;
+ retval = TINYOBJ_SUCCESS;
+ break;
+ /**
+ * @todo {
+ * . bevel interpolation (bevel)
+ * . color interpolation (c_interp)
+ * . dissolve interpolation (d_interp)
+ * . level of detail (lod)
+ * . shadow casting (shadow_obj)
+ * . ray tracing (trace_obj)
+ * . curve approximation technique (ctech)
+ * . surface approximation technique (stech)
+ * }
+ **/
+ default:
+ retval = TINYOBJ_ERROR_UNKNOWN_PARAMETER;
+ break;
+ }
+ if (retval == TINYOBJ_ERROR_UNKNOWN_PARAMETER) {
+ char *command_name = NULL;
+ size_t length;
+ length = length_until_space(token, (p_len - (token - linebuf)) + 1);
+ command_name = strndup(token, length);
+ fprintf(stderr, "%s: Unsupported command '%s'\n", __func__, command_name);
+ TINYOBJ_FREE(command_name);
+ return retval;
+ }
+ // Increment specific counters
+ switch (command->type) {
+ case COMMAND_V:
+ command_info->counter.v++;
+ break;
+ case COMMAND_VN:
+ command_info->counter.vn++;
+ break;
+ case COMMAND_VT:
+ command_info->counter.vt++;
+ break;
+ case COMMAND_VP:
+ command_info->counter.vp++;
+ break;
+ case COMMAND_F:
+ command_info->counter.f++;
+ break;
+ case COMMAND_P:
+ command_info->counter.p += command->info.p.length;
+ break;
+ case COMMAND_L:
+ command_info->counter.l++;
+ break;
- line_infos = (LineInfo *)TINYOBJ_MALLOC(sizeof(LineInfo) * num_lines);
+ case COMMAND_G:
+ case COMMAND_O:
+ command_info->counter.shapes++;
+ break;
+ case COMMAND_MTLLIB:
+ command_info->mtllib_line_index = pos;
+ break;
+ default:
+ break;
+ }
+ return retval;
+}
- /* Fill line infos. */
- for (i = 0; i < end_idx; i++) {
- if (is_line_ending(buf, i, end_idx)) {
- line_infos[line_no].pos = prev_pos;
- line_infos[line_no].len = i - prev_pos;
- prev_pos = i + 1;
- line_no++;
- }
+/* Function: tinyobj_parse_obj_line_traverse
+ *
+ * Traverses and parses all lines of given buffer
+ *
+ * Parameters:
+ *
+ * buf - Buffer to be parsed
+ * len - Buffer length
+ * command_info - Command information to be filled
+ * flags - Parsing flags
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ */
+static int tinyobj_parse_obj_line_traverse(const char *buf, size_t len,
+ CommandInformation *command_info,
+ unsigned int flags) {
+ size_t i;
+ unsigned int pos;
+ size_t end_idx = len;
+ unsigned int prev_pos = 0;
+ unsigned int line_no = 0;
+ size_t last_line_ending = 0;
+ unsigned int num_lines = 0;
+
+ // Count number of lines
+ for (i = 0; i < end_idx; i++) {
+ if (is_line_ending(buf, i, end_idx)) {
+ num_lines++;
+ last_line_ending = i;
}
- if (end_idx - last_line_ending > 0) {
- line_infos[line_no].pos = prev_pos;
- line_infos[line_no].len = end_idx - 1 - last_line_ending;
+ }
+ /**
+ * The last char from the input may not be a line
+ * ending character so add an extra line if there
+ * are more characters after the last line ending
+ * that was found.
+ **/
+ if (end_idx - last_line_ending > 0) num_lines++;
+
+ if (num_lines == 0) return TINYOBJ_ERROR_EMPTY;
+
+ command_info->command_list =
+ TINYOBJ_MALLOC(sizeof(*command_info->command_list) * num_lines);
+ if (!command_info->command_list) return TINYOBJ_ERROR_MEMORY;
+ command_info->mtllib_line_index = -1;
+ command_info->count = 0;
+ memset(&command_info->counter, 0, sizeof(command_info->counter));
+
+ pos = 0;
+ for (i = 0; i < end_idx; i++) {
+ if (is_line_ending(buf, i, end_idx)) {
+ int ret;
+ ret = tinyobj_parse_obj_line(command_info, pos, &buf[prev_pos],
+ i - prev_pos, flags);
+
+ prev_pos = i + 1;
+ line_no++;
+ if (ret == TINYOBJ_SUCCESS || ret == TINYOBJ_NO_COMMAND) {
+ pos++;
+ assert(pos < num_lines);
+ command_info->count++;
+ } else
+ fprintf(stderr, "%s: Failed to parse line (%d): TINYOBJ ERROR: %d\n",
+ __func__, line_no, ret);
}
}
+ if (end_idx - last_line_ending > 0) {
+ int ret;
+ assert(pos < num_lines);
+ ret = tinyobj_parse_obj_line(command_info, pos, &buf[prev_pos],
+ end_idx - 1 - last_line_ending, flags);
+ if (ret == TINYOBJ_SUCCESS) command_info->count++;
+ }
+ return TINYOBJ_SUCCESS;
+}
- commands = (Command *)TINYOBJ_MALLOC(sizeof(Command) * num_lines);
-
- create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table);
-
- /* 2. parse each line */
- {
- size_t i = 0;
- for (i = 0; i < num_lines; i++) {
- int ret = parseLine(&commands[i], &buf[line_infos[i].pos],
- line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE);
- if (ret) {
- if (commands[i].type == COMMAND_V) {
- num_v++;
- } else if (commands[i].type == COMMAND_VN) {
- num_vn++;
- } else if (commands[i].type == COMMAND_VT) {
- num_vt++;
- } else if (commands[i].type == COMMAND_F) {
- num_f += commands[i].num_f;
- num_faces += commands[i].num_f_num_verts;
- }
+/* Function: tinyobj_command_info_free
+ * Frees given command information
+ */
+static void tinyobj_command_info_free(CommandInformation *command_info) {
+ if (!command_info) return;
+ if (command_info->command_list) TINYOBJ_FREE(command_info->command_list);
+}
- if (commands[i].type == COMMAND_MTLLIB) {
- mtllib_line_index = (int)i;
+/* Function: tinyobj_shape_construct
+ *
+ * Constructs shape information
+ *
+ * Parameters:
+ *
+ * shapes - [in/out] Pointer to be filled
+ * num_shapes - [out] Length of shapes
+ * command_info - Information used to construct the shapes
+ */
+static void tinyobj_shape_construct(tinyobj_shape_t **shapes,
+ unsigned int *num_shapes,
+ CommandInformation *command_info) {
+ unsigned int face_count = 0;
+
+ const char *shape_name = NULL;
+ size_t shape_name_len = 0;
+ unsigned int shape_idx = 0;
+
+ const char *prev_shape_name = NULL;
+ size_t prev_shape_name_len = 0;
+ unsigned int prev_shape_face_offset = 0;
+ unsigned int prev_face_offset = 0;
+ tinyobj_shape_t prev_shape = {NULL, 0, 0};
+
+ Command *command_list = command_info->command_list;
+ unsigned int i;
+
+ /**
+ * Allocate array of shapes with maximum possible size(+1 for unnamed
+ * group/object).
+ * Actual # of shapes found in .obj is determined later
+ **/
+ (*shapes) =
+ TINYOBJ_MALLOC(sizeof(**shapes) * (command_info->counter.shapes + 1));
+
+ for (i = 0; i < command_info->count; i++) {
+ switch (command_list[i].type) {
+ case COMMAND_O:
+ case COMMAND_G:
+ if (command_list[i].type == COMMAND_O) {
+ shape_name = command_list[i].info.o.name;
+ shape_name_len = command_list[i].info.o.len;
+ } else {
+ shape_name = command_list[i].info.g.name;
+ shape_name_len = command_list[i].info.g.len;
}
- }
+ if (face_count == 0) {
+ // 'o' or 'g' appears before any 'f'
+ prev_shape_name = shape_name;
+ prev_shape_name_len = shape_name_len;
+ prev_shape_face_offset = face_count;
+ prev_face_offset = face_count;
+ continue;
+ }
+ if (shape_idx == 0) {
+ // 'o' or 'g' after some 'v' lines
+ (*shapes)[shape_idx].name =
+ strndup(prev_shape_name, prev_shape_name_len); // may be NULL
+ (*shapes)[shape_idx].face_offset = prev_shape.face_offset;
+ (*shapes)[shape_idx].length = face_count - prev_face_offset;
+ shape_idx++;
+ prev_face_offset = face_count;
+ } else if ((face_count - prev_face_offset) > 0) {
+ (*shapes)[shape_idx].name =
+ strndup(prev_shape_name, prev_shape_name_len);
+ (*shapes)[shape_idx].face_offset = prev_face_offset;
+ (*shapes)[shape_idx].length = face_count - prev_face_offset;
+ shape_idx++;
+ prev_face_offset = face_count;
+ }
+ // Record shape info for succeeding 'o' or 'g' command
+ prev_shape_name = shape_name;
+ prev_shape_name_len = shape_name_len;
+ prev_shape_face_offset = face_count;
+ continue;
+ case COMMAND_F:
+ face_count++;
+ continue;
+ default:
+ continue;
}
}
- /* line_infos are not used anymore. Release memory. */
- if (line_infos) {
- TINYOBJ_FREE(line_infos);
- }
-
- /* Load material(if exits) */
- if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name &&
- commands[mtllib_line_index].mtllib_name_len > 0) {
- char *filename = my_strndup(commands[mtllib_line_index].mtllib_name,
- commands[mtllib_line_index].mtllib_name_len);
-
- int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table);
-
- if (ret != TINYOBJ_SUCCESS) {
- /* warning. */
- fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret);
+ if ((face_count - prev_face_offset) > 0) {
+ unsigned int length = face_count - prev_shape_face_offset;
+ if (length > 0) {
+ (*shapes)[shape_idx].name = strndup(prev_shape_name, prev_shape_name_len);
+ (*shapes)[shape_idx].face_offset = prev_face_offset;
+ (*shapes)[shape_idx].length = face_count - prev_face_offset;
+ shape_idx++;
}
+ } else {
+ /**
+ * Guess no 'v' line occurrence after 'o' or 'g', so discards current
+ * shape information.
+ **/
+ }
+ (*num_shapes) = shape_idx;
+ TINYOBJ_FREE(*shapes);
+ *shapes = NULL;
+}
- TINYOBJ_FREE(filename);
+/* Function: tinyobj_shape_free
+ *
+ * Frees shape data
+ *
+ * Parameters:
+ *
+ * shapes - Shape information to be freed
+ * num_shapes - Length of shapes
+ */
+void tinyobj_shape_free(tinyobj_shape_t *shapes, unsigned int num_shapes) {
+ unsigned int i;
+ if (!shapes || !num_shapes) return;
+ for (i = 0; i < num_shapes; i++) {
+ if (shapes[i].name) TINYOBJ_FREE(shapes[i].name);
}
+ TINYOBJ_FREE(shapes);
+}
- /* Construct attributes */
-
- {
- size_t v_count = 0;
- size_t n_count = 0;
- size_t t_count = 0;
- size_t f_count = 0;
- size_t face_count = 0;
- int material_id = -1; /* -1 = default unknown material. */
- size_t i = 0;
-
- attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3);
- attrib->num_vertices = (unsigned int)num_v;
- attrib->normals = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vn * 3);
- attrib->num_normals = (unsigned int)num_vn;
- attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2);
- attrib->num_texcoords = (unsigned int)num_vt;
- attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC(
- sizeof(tinyobj_vertex_index_t) * num_f);
- attrib->num_faces = (unsigned int)num_f;
- attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
- attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
- attrib->num_face_num_verts = (unsigned int)num_faces;
-
- for (i = 0; i < num_lines; i++) {
- if (commands[i].type == COMMAND_EMPTY) {
- continue;
- } else if (commands[i].type == COMMAND_USEMTL) {
- /* @todo
- if (commands[t][i].material_name &&
- commands[t][i].material_name_len > 0) {
- std::string material_name(commands[t][i].material_name,
- commands[t][i].material_name_len);
-
- if (material_map.find(material_name) != material_map.end()) {
- material_id = material_map[material_name];
- } else {
- // Assign invalid material ID
- material_id = -1;
- }
- }
- */
- if (commands[i].material_name &&
- commands[i].material_name_len >0)
+/* Function: tinyobj_attrib_construct
+ *
+ * Constructs attribute data with given information
+ *
+ * Parameters:
+ *
+ * attrib - [in/out] Attribute to be filled
+ * command_info - Information used to fill attrib
+ * material_table - Material table used (can be NULL)
+ */
+static void tinyobj_attrib_construct(tinyobj_attrib_t *attrib,
+ CommandInformation *command_info,
+ tinyobj_material_table_t *material_table) {
+ int mtl_material_id =
+ -1; //< COMMAND_USEMTL material id defaults to -1 'Unknown material'
+ int smoothing_id = 0; //< Defaults to 0, no group
+ struct {
+ unsigned int v, vn, vt, vp;
+ unsigned int f, /* p,*/ l;
+ } count;
+ unsigned int i;
+ Command *command_list = command_info->command_list;
+ memset(&count, 0, sizeof(count));
+
+ attrib->v_count = command_info->counter.v;
+ attrib->v = (attrib->v_count)
+ ? TINYOBJ_MALLOC(sizeof(*attrib->v) * attrib->v_count)
+ : NULL;
+
+ attrib->vn_count = command_info->counter.vn;
+ attrib->vn = (attrib->vn_count)
+ ? TINYOBJ_MALLOC(sizeof(*attrib->vn) * attrib->vn_count)
+ : NULL;
+
+ attrib->vt_count = command_info->counter.vt;
+ attrib->vt = (attrib->vt_count)
+ ? TINYOBJ_MALLOC(sizeof(*attrib->vt) * attrib->vt_count)
+ : NULL;
+
+ attrib->vp_count = command_info->counter.vp;
+ attrib->vp = (attrib->vp_count)
+ ? TINYOBJ_MALLOC(sizeof(*attrib->vp) * attrib->vp_count)
+ : NULL;
+
+ attrib->f_count = command_info->counter.f;
+ attrib->f = (attrib->f_count)
+ ? TINYOBJ_MALLOC(sizeof(*attrib->f) * attrib->f_count)
+ : NULL;
+ attrib->triangle_count_total = 0;
+
+ attrib->l_count = command_info->counter.l;
+ attrib->l = (attrib->l_count)
+ ? TINYOBJ_MALLOC(sizeof(*attrib->l) * attrib->l_count)
+ : NULL;
+
+ attrib->p.count = 0;
+ attrib->p.length = command_info->counter.p;
+ attrib->p.v_idx =
+ (attrib->p.length)
+ ? TINYOBJ_MALLOC(sizeof(*attrib->p.v_idx) * attrib->p.length)
+ : NULL;
+
+ for (i = 0; i < command_info->count; i++) {
+ switch (command_list[i].type) {
+ case COMMAND_USEMTL: {
+ char *material_name_null_term;
+ if (!command_list[i].info.usemtl.name ||
+ !command_list[i].info.usemtl.len)
+ continue;
+ if (!material_table) continue;
+ material_name_null_term =
+ TINYOBJ_MALLOC(command_list[i].info.usemtl.len + 1);
+ memcpy(material_name_null_term, command_list[i].info.usemtl.name,
+ command_list[i].info.usemtl.len);
+ material_name_null_term[command_list[i].info.usemtl.len] = '\0';
+#ifndef TINYOBJ_USE_UTHASH
+ if (hash_table_exists(material_name_null_term, material_table))
+ mtl_material_id =
+ hash_table_get(material_name_null_term, material_table);
+ else
+ mtl_material_id = -1;
+#else
{
- /* Create a null terminated string */
- char* material_name_null_term = (char*) TINYOBJ_MALLOC(commands[i].material_name_len + 1);
- memcpy((void*) material_name_null_term, (const void*) commands[i].material_name, commands[i].material_name_len);
- material_name_null_term[commands[i].material_name_len - 1] = 0;
-
- if (hash_table_exists(material_name_null_term, &material_table))
- material_id = (int)hash_table_get(material_name_null_term, &material_table);
- else
- material_id = -1;
-
- TINYOBJ_FREE(material_name_null_term);
+ tinyobj_material_table_tEntry *entry = NULL;
+ tinyobj_hash_find(material_table, material_name_null_term, &entry);
+ mtl_material_id = (entry) ? entry->value : -1;
}
- } else if (commands[i].type == COMMAND_V) {
- attrib->vertices[3 * v_count + 0] = commands[i].vx;
- attrib->vertices[3 * v_count + 1] = commands[i].vy;
- attrib->vertices[3 * v_count + 2] = commands[i].vz;
- v_count++;
- } else if (commands[i].type == COMMAND_VN) {
- attrib->normals[3 * n_count + 0] = commands[i].nx;
- attrib->normals[3 * n_count + 1] = commands[i].ny;
- attrib->normals[3 * n_count + 2] = commands[i].nz;
- n_count++;
- } else if (commands[i].type == COMMAND_VT) {
- attrib->texcoords[2 * t_count + 0] = commands[i].tx;
- attrib->texcoords[2 * t_count + 1] = commands[i].ty;
- t_count++;
- } else if (commands[i].type == COMMAND_F) {
- size_t k = 0;
- for (k = 0; k < commands[i].num_f; k++) {
- tinyobj_vertex_index_t vi = commands[i].f[k];
- int v_idx = fixIndex(vi.v_idx, v_count);
- int vn_idx = fixIndex(vi.vn_idx, n_count);
- int vt_idx = fixIndex(vi.vt_idx, t_count);
- attrib->faces[f_count + k].v_idx = v_idx;
- attrib->faces[f_count + k].vn_idx = vn_idx;
- attrib->faces[f_count + k].vt_idx = vt_idx;
+#endif
+ if (mtl_material_id < 0)
+ fprintf(stderr, "%s: USEMTL %s not found in table!\n", __func__,
+ material_name_null_term);
+ TINYOBJ_FREE(material_name_null_term);
+ continue;
+ }
+ case COMMAND_S:
+ smoothing_id = command_list[i].info.smoothing_id;
+ continue;
+ case COMMAND_V:
+ assert(attrib->v != NULL);
+ memcpy(&attrib->v[count.v], &command_list[i].info.v,
+ sizeof(*attrib->v));
+ count.v++;
+ continue;
+ case COMMAND_VN:
+ assert(attrib->vn != NULL);
+ memcpy(&attrib->vn[count.vn], &command_list[i].info.vn,
+ sizeof(*attrib->vn));
+ count.vn++;
+ continue;
+ case COMMAND_VT:
+ assert(attrib->vt != NULL);
+ memcpy(&attrib->vt[count.vt], &command_list[i].info.vt,
+ sizeof(*attrib->vt));
+ count.vt++;
+ continue;
+ case COMMAND_VP:
+ assert(attrib->vp != NULL);
+ memcpy(&attrib->vp[count.vp], &command_list[i].info.vp,
+ sizeof(*attrib->vp));
+ count.vp++;
+ continue;
+ case COMMAND_F: {
+ unsigned int k = 0;
+ assert(attrib->f != NULL);
+ attrib->f[count.f].triangle_count =
+ command_list[i].info.f.triangle_count;
+ attrib->f[count.f].triplet_list = command_list[i].info.f.triplet_list;
+ attrib->f[count.f].count = command_list[i].info.f.count;
+ attrib->f[count.f].length = command_list[i].info.f.length;
+ // Fix triplet index
+ for (k = 0; k < command_list[i].info.f.count; k++) {
+ tinyobj_vertex_index_t *vi = &command_list[i].info.f.triplet_list[k];
+ attrib->f[count.f].triplet_list[k].v_idx =
+ fixIndex(vi->v_idx, count.v);
+ attrib->f[count.f].triplet_list[k].vt_idx =
+ fixIndex(vi->vt_idx, count.vt);
+ attrib->f[count.f].triplet_list[k].vn_idx =
+ fixIndex(vi->vn_idx, count.vn);
}
-
- for (k = 0; k < commands[i].num_f_num_verts; k++) {
- attrib->material_ids[face_count + k] = material_id;
- attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k];
+ attrib->f[count.f].material_id = mtl_material_id;
+ attrib->f[count.f].smoothing_id = smoothing_id;
+ count.f++;
+ attrib->triangle_count_total += command_list[i].info.f.triangle_count;
+ continue;
+ }
+ case COMMAND_P: {
+ size_t copy_len = command_list[i].info.p.count;
+ assert(attrib->p.v_idx != NULL);
+ if (copy_len + attrib->p.count > attrib->p.length) {
+ fprintf(stderr, "%s: Point count is greater than counter length!\n",
+ __func__);
+ continue;
}
-
- f_count += commands[i].num_f;
- face_count += commands[i].num_f_num_verts;
+ memcpy(&attrib->p.v_idx[attrib->p.count], command_list[i].info.p.v_idx,
+ sizeof(*attrib->p.v_idx) * copy_len);
+ attrib->p.count += copy_len;
+ tinyobj_obj_free_point(&command_list[i].info.p);
+ continue;
}
- }
- }
-
- /* 5. Construct shape information. */
- {
- unsigned int face_count = 0;
- size_t i = 0;
- size_t n = 0;
- size_t shape_idx = 0;
-
- const char *shape_name = NULL;
- unsigned int shape_name_len = 0;
- const char *prev_shape_name = NULL;
- unsigned int prev_shape_name_len = 0;
- unsigned int prev_shape_face_offset = 0;
- unsigned int prev_face_offset = 0;
- tinyobj_shape_t prev_shape = {NULL, 0, 0};
-
- /* Find the number of shapes in .obj */
- for (i = 0; i < num_lines; i++) {
- if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
- n++;
+ case COMMAND_L: {
+ unsigned int k = 0;
+ assert(attrib->l != NULL);
+ attrib->l[count.l].couple_list = command_list[i].info.l.couple_list;
+ attrib->l[count.l].count = command_list[i].info.l.count;
+ attrib->l[count.l].length = command_list[i].info.l.length;
+ // Fix couple index
+ for (k = 0; k < command_list[i].info.l.count; k++) {
+ attrib->l[count.l].couple_list[k].v_idx =
+ fixIndex(command_list[i].info.l.couple_list[k].v_idx, count.v);
+ attrib->l[count.l].couple_list[k].vt_idx =
+ fixIndex(command_list[i].info.l.couple_list[k].vt_idx, count.vt);
+ }
+ count.f++;
}
+ default:
+ case COMMAND_EMPTY:
+ continue;
}
+ }
+}
- /* Allocate array of shapes with maximum possible size(+1 for unnamed
- * group/object).
- * Actual # of shapes found in .obj is determined in the later */
- (*shapes) = (tinyobj_shape_t*)TINYOBJ_MALLOC(sizeof(tinyobj_shape_t) * (n + 1));
+// Group: Aplication Programming Interface (.obj)
- for (i = 0; i < num_lines; i++) {
- if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
- if (commands[i].type == COMMAND_O) {
- shape_name = commands[i].object_name;
- shape_name_len = commands[i].object_name_len;
- } else {
- shape_name = commands[i].group_name;
- shape_name_len = commands[i].group_name_len;
- }
+/* Function: tinyobj_attrib_free
+ * Frees attribute data
+ */
+void tinyobj_attrib_free(tinyobj_attrib_t *attrib) {
+ unsigned int i;
+ if (!attrib) return;
+ if (attrib->v) TINYOBJ_FREE(attrib->v);
+ if (attrib->vn) TINYOBJ_FREE(attrib->vn);
+ if (attrib->vt) TINYOBJ_FREE(attrib->vt);
+ if (attrib->vp) TINYOBJ_FREE(attrib->vp);
+ if (attrib->f) {
+ for (i = 0; i < attrib->f_count; i++) tinyobj_obj_free_face(&attrib->f[i]);
+ }
+ if (attrib->l) {
+ for (i = 0; i < attrib->l_count; i++) tinyobj_obj_free_line(&attrib->l[i]);
+ }
+ if (attrib->p.v_idx) tinyobj_obj_free_point(&attrib->p);
+}
- if (face_count == 0) {
- /* 'o' or 'g' appears before any 'f' */
- prev_shape_name = shape_name;
- prev_shape_name_len = shape_name_len;
- prev_shape_face_offset = face_count;
- prev_face_offset = face_count;
- } else {
- if (shape_idx == 0) {
- /* 'o' or 'g' after some 'v' lines. */
- (*shapes)[shape_idx].name = my_strndup(
- prev_shape_name, prev_shape_name_len); /* may be NULL */
- (*shapes)[shape_idx].face_offset = prev_shape.face_offset;
- (*shapes)[shape_idx].length = face_count - prev_face_offset;
- shape_idx++;
-
- prev_face_offset = face_count;
-
- } else {
- if ((face_count - prev_face_offset) > 0) {
- (*shapes)[shape_idx].name =
- my_strndup(prev_shape_name, prev_shape_name_len);
- (*shapes)[shape_idx].face_offset = prev_face_offset;
- (*shapes)[shape_idx].length = face_count - prev_face_offset;
- shape_idx++;
- prev_face_offset = face_count;
- }
- }
+/* Function: tinyobj_attrib_init
+ * Initializes given attribute information to default values
+ */
+static void tinyobj_attrib_init(tinyobj_attrib_t *attrib) {
+ if (!attrib) return;
+ attrib->f = NULL;
+ attrib->l = NULL;
+ attrib->v = NULL;
+ attrib->vn = NULL;
+ attrib->vp = NULL;
+ attrib->vt = NULL;
+ attrib->p.v_idx = NULL;
+ attrib->p.count = 0;
+ attrib->p.length = 0;
+
+ attrib->f_count = 0;
+ attrib->l_count = 0;
+ attrib->v_count = 0;
+ attrib->vn_count = 0;
+ attrib->vp_count = 0;
+ attrib->vt_count = 0;
+ // memset(&attrib, 0, sizeof(*attrib));
+}
- /* Record shape info for succeeding 'o' or 'g' command. */
- prev_shape_name = shape_name;
- prev_shape_name_len = shape_name_len;
- prev_shape_face_offset = face_count;
- }
- }
- if (commands[i].type == COMMAND_F) {
- face_count++;
- }
- }
+/* Function: tinyobj_parse_obj
+ *
+ * Parses .obj file
+ *
+ * Parameters:
+ *
+ * attrib - [in/out] Attributes to be filled
+ * shapes - [in/out] Shapes to be filled
+ * num_shapes - [in/out] Length of shapes
+ * materials_out - [in/out] Materials to be filled
+ * num_materials_out - [in/out] Length of materials
+ * buf - Buffer to be read
+ * len - Buffer length
+ * flags - Parsing flags
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ */
+int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
+ unsigned int *num_shapes,
+ tinyobj_material_t **materials_out,
+ unsigned int *num_materials_out, const char *buf,
+ size_t len, unsigned int flags) {
+ CommandInformation command_info;
+ Command *command_list = NULL;
+ tinyobj_material_table_t material_table;
- if ((face_count - prev_face_offset) > 0) {
- size_t length = face_count - prev_shape_face_offset;
- if (length > 0) {
- (*shapes)[shape_idx].name =
- my_strndup(prev_shape_name, prev_shape_name_len);
- (*shapes)[shape_idx].face_offset = prev_face_offset;
- (*shapes)[shape_idx].length = face_count - prev_face_offset;
- shape_idx++;
- }
- } else {
- /* Guess no 'v' line occurrence after 'o' or 'g', so discards current
- * shape information. */
- }
+ tinyobj_material_t *materials = NULL;
+ unsigned int num_materials = 0;
+ int ret;
+
+ // Verify if parameters are valid
+ if (len < 1 || buf == NULL || attrib == NULL || shapes == NULL ||
+ num_shapes == NULL || materials_out == NULL ||
+ num_materials_out == NULL) {
+ fprintf(stderr, "%s: Failed to parse object, invalid parameters\n",
+ __func__);
+ return TINYOBJ_ERROR_INVALID_PARAMETER;
+ }
- (*num_shapes) = shape_idx;
+ tinyobj_attrib_init(attrib);
+ // Traverse lines and fill command information
+ command_info.command_list = NULL;
+ ret = tinyobj_parse_obj_line_traverse(buf, len, &command_info, flags);
+ if (ret != TINYOBJ_SUCCESS) {
+ tinyobj_command_info_free(&command_info);
+ fprintf(stderr, "%s: Failed to traverse lines for given object: %d\n",
+ __func__, ret);
+ return ret;
}
+ command_list = command_info.command_list;
+#ifndef TINYOBJ_USE_UTHASH
+ create_hash_table(TINYOBJ_HASH_TABLE_DEFAULT_SIZE, &material_table);
+#else
+ tinyobj_hash_init(&material_table);
+#endif
- if (commands) {
- TINYOBJ_FREE(commands);
+ // Load material if 'mtllib' is defined
+ if (command_info.mtllib_line_index >= 0 &&
+ command_list[command_info.mtllib_line_index].type == COMMAND_MTLLIB &&
+ command_list[command_info.mtllib_line_index].info.mtllib.len > 0) {
+ char *filename =
+ strndup(command_list[command_info.mtllib_line_index].info.mtllib.name,
+ command_list[command_info.mtllib_line_index].info.mtllib.len);
+ ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename,
+ &material_table);
+ if (ret != TINYOBJ_SUCCESS) {
+ fprintf(stderr, "%s: Failed to parse material file '%s': '%d'\n",
+ __func__, filename, ret);
+ }
+ TINYOBJ_FREE(filename);
}
+ // Construct attributes
+ tinyobj_attrib_construct(attrib, &command_info, &material_table);
+#ifndef TINYOBJ_USE_UTHASH
destroy_hash_table(&material_table);
-
+#else
+ tinyobj_hash_free(&material_table, 0);
+#endif
+
+ tinyobj_shape_construct(shapes, num_shapes, &command_info);
+
+ tinyobj_command_info_free(&command_info);
(*materials_out) = materials;
(*num_materials_out) = num_materials;
return TINYOBJ_SUCCESS;
}
-void tinyobj_attrib_init(tinyobj_attrib_t *attrib) {
- attrib->vertices = NULL;
- attrib->num_vertices = 0;
- attrib->normals = NULL;
- attrib->num_normals = 0;
- attrib->texcoords = NULL;
- attrib->num_texcoords = 0;
- attrib->faces = NULL;
- attrib->num_faces = 0;
- attrib->face_num_verts = NULL;
- attrib->num_face_num_verts = 0;
- attrib->material_ids = NULL;
-}
+#ifdef TINYOBJ_ENABLE_OLDER_ATTRIBUTE
+// Group: Compatibility with older versions (deprecated)
-void tinyobj_attrib_free(tinyobj_attrib_t *attrib) {
+/* Function: tinyobj_attrib_free_compat
+ * Frees given attribute
+ */
+void tinyobj_attrib_free_compat(COMPATtinyobj_attrib_t *attrib) {
+ if (!attrib) return;
if (attrib->vertices) TINYOBJ_FREE(attrib->vertices);
if (attrib->normals) TINYOBJ_FREE(attrib->normals);
if (attrib->texcoords) TINYOBJ_FREE(attrib->texcoords);
@@ -1550,37 +3162,121 @@ void tinyobj_attrib_free(tinyobj_attrib_t *attrib) {
if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids);
}
-void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes) {
- size_t i;
- if (shapes == NULL) return;
-
- for (i = 0; i < num_shapes; i++) {
- if (shapes[i].name) TINYOBJ_FREE(shapes[i].name);
+/* Function: tinyobj_new2old
+ *
+ * Converts new attribute format to the old one
+ * This expects that the faces were triangulated
+ *
+ * Parameters:
+ *
+ * attrib - [in] Attribute to be converted
+ * out_attrib - [out] Attribute to be filled
+ *
+ * Returns:
+ *
+ * -
+ * -
+ * -
+ */
+int tinyobj_new2old(tinyobj_attrib_t *attrib,
+ COMPATtinyobj_attrib_t *out_attrib) {
+ unsigned int i, j, face_pos, l;
+
+ if (!attrib || !out_attrib) return TINYOBJ_ERROR_INVALID_PARAMETER;
+
+ // Initialize old attributes
+ out_attrib->vertices = NULL;
+ out_attrib->num_vertices = 0;
+ out_attrib->normals = NULL;
+ out_attrib->num_normals = 0;
+ out_attrib->texcoords = NULL;
+ out_attrib->num_texcoords = 0;
+ out_attrib->faces = NULL;
+ out_attrib->num_faces = 0;
+ out_attrib->face_num_verts = NULL;
+ out_attrib->num_face_num_verts = 0;
+ out_attrib->material_ids = NULL;
+
+ assert(attrib->v_count > 0);
+ out_attrib->num_vertices = attrib->v_count;
+ out_attrib->vertices = TINYOBJ_MALLOC(sizeof(*out_attrib->vertices) *
+ out_attrib->num_vertices * 3);
+ if (!out_attrib->vertices) return TINYOBJ_ERROR_MEMORY;
+ for (i = 0, j = 0; i < attrib->v_count; i++, j += 3) {
+ out_attrib->vertices[j + 0] = attrib->v[i].x;
+ out_attrib->vertices[j + 1] = attrib->v[i].y;
+ out_attrib->vertices[j + 2] = attrib->v[i].z;
}
- TINYOBJ_FREE(shapes);
-}
-
-void tinyobj_materials_free(tinyobj_material_t *materials,
- size_t num_materials) {
- size_t i;
- if (materials == NULL) return;
+ if (attrib->vn_count) {
+ out_attrib->num_normals = attrib->vn_count;
+ out_attrib->normals = TINYOBJ_MALLOC(sizeof(*out_attrib->normals) *
+ out_attrib->num_normals * 3);
+ if (!out_attrib->normals) {
+ tinyobj_attrib_free_compat(out_attrib);
+ return TINYOBJ_ERROR_MEMORY;
+ }
+ for (i = 0, j = 0; i < attrib->vn_count; i++, j += 3) {
+ out_attrib->normals[j + 0] = attrib->vn[i].i;
+ out_attrib->normals[j + 1] = attrib->vn[i].j;
+ out_attrib->normals[j + 2] = attrib->vn[i].k;
+ }
+ }
+ if (attrib->vt_count) {
+ out_attrib->num_texcoords = attrib->vt_count;
+ out_attrib->texcoords = TINYOBJ_MALLOC(sizeof(*out_attrib->texcoords) *
+ out_attrib->num_texcoords * 2);
+ if (!out_attrib->texcoords) {
+ tinyobj_attrib_free_compat(out_attrib);
+ return TINYOBJ_ERROR_MEMORY;
+ }
+ for (i = 0, j = 0; i < attrib->vt_count; i++, j += 2) {
+ out_attrib->texcoords[j + 0] = attrib->vt[i].u;
+ out_attrib->texcoords[j + 1] = attrib->vt[i].v;
+ }
+ }
- for (i = 0; i < num_materials; i++) {
- if (materials[i].name) TINYOBJ_FREE(materials[i].name);
- if (materials[i].ambient_texname) TINYOBJ_FREE(materials[i].ambient_texname);
- if (materials[i].diffuse_texname) TINYOBJ_FREE(materials[i].diffuse_texname);
- if (materials[i].specular_texname) TINYOBJ_FREE(materials[i].specular_texname);
- if (materials[i].specular_highlight_texname)
- TINYOBJ_FREE(materials[i].specular_highlight_texname);
- if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname);
- if (materials[i].displacement_texname)
- TINYOBJ_FREE(materials[i].displacement_texname);
- if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname);
+ out_attrib->num_faces = 0;
+ for (i = 0; i < attrib->f_count; i++)
+ out_attrib->num_faces += attrib->f[i].count;
+ out_attrib->faces =
+ TINYOBJ_MALLOC(sizeof(*out_attrib->faces) * out_attrib->num_faces);
+ out_attrib->material_ids =
+ TINYOBJ_MALLOC(sizeof(*out_attrib->material_ids) * attrib->f_count);
+ out_attrib->num_face_num_verts = attrib->triangle_count_total;
+ out_attrib->face_num_verts = TINYOBJ_MALLOC(
+ sizeof(*out_attrib->face_num_verts) * out_attrib->num_face_num_verts);
+ if (!out_attrib->faces || !out_attrib->face_num_verts ||
+ !out_attrib->material_ids) {
+ tinyobj_attrib_free_compat(out_attrib);
+ return TINYOBJ_ERROR_MEMORY;
+ }
+ face_pos = 0;
+ l = 0;
+ for (i = 0; i < attrib->f_count; i++) {
+ unsigned int m;
+ for (j = 0; j < attrib->f[i].count; j++) {
+ out_attrib->faces[face_pos].v_idx = attrib->f[i].triplet_list[j].v_idx;
+ out_attrib->faces[face_pos].vn_idx = attrib->f[i].triplet_list[j].vn_idx;
+ out_attrib->faces[face_pos].vt_idx = attrib->f[i].triplet_list[j].vt_idx;
+ face_pos++;
+ }
+ if (attrib->f[i].count % 3 == 0) {
+ for (m = 0; m < attrib->f[i].triangle_count; m++) {
+ out_attrib->face_num_verts[l] = 3;
+ l++;
+ }
+ } else {
+ out_attrib->face_num_verts[l] = 1;
+ l++;
+ }
+ out_attrib->material_ids[i] = attrib->f[i].material_id;
}
- TINYOBJ_FREE(materials);
+ return TINYOBJ_SUCCESS;
}
-#endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */
+#endif // TINYOBJ_ENABLE_OLDER_ATTRIBUTE
+
+#endif // TINYOBJ_LOADER_C_IMPLEMENTATION
-#endif /* TINOBJ_LOADER_C_H_ */
+#endif // TINOBJ_LOADER_C_H_