Coverage Report

Created: 2026-07-25 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinyobjloader/tiny_obj_c_impl.inc
Line
Count
Source
1
/*
2
 * tiny_obj_c_impl.inc -- per-precision OBJ parser + scene teardown.
3
 *
4
 * Included twice from tiny_obj_c.c with (TOBJ_REAL, TOBJ_SUF) = (float, _f)
5
 * and (double, _d). Every file-scope name is suffixed via TOBJ_T() so the two
6
 * inclusions do not collide in the single translation unit. Do not include
7
 * directly.
8
 */
9
10
/* ----------------------------------------------------------------------- */
11
/* builder                                                                 */
12
/* ----------------------------------------------------------------------- */
13
14
typedef struct TOBJ_T(tobj_builder) {
15
  const tobj_allocator *alloc;
16
  tobj_arena *arena;
17
  tobj_diag *diag;
18
  const tobj_load_config *cfg;
19
  tobj_caps caps;
20
  tobj_result err;
21
  size_t line; /* 1-based current line for diagnostics */
22
23
  bool triangulate;
24
25
  /* attrib temp vectors (reals / structs) */
26
  tobj_vec av;   /* vertices xyz */
27
  tobj_vec avw;  /* vertex weights (lazy) */
28
  tobj_vec avn;  /* normals xyz */
29
  tobj_vec avt;  /* texcoords uv */
30
  tobj_vec avtw; /* texcoord w (lazy) */
31
  tobj_vec acol; /* colors rgb (lazy) */
32
  bool color_seen;
33
34
  /* materials + name->id map (filled by the .mtl parser) */
35
  tobj_vec materials;   /* TOBJ_T(tobj_material) */
36
  tobj_vec matmap_key;  /* const char* (interned) */
37
  tobj_vec matmap_id;   /* int */
38
  tobj_vec mat_unknown; /* tobj_pending_kv */
39
40
  /* finalized shapes */
41
  tobj_vec shapes; /* TOBJ_T(tobj_shape) */
42
43
  /* current shape accumulation */
44
  const char *cur_name;
45
  bool cur_has_prims;
46
  tobj_vec m_idx;    /* tobj_index */
47
  tobj_vec m_fv;     /* unsigned face-vertex counts */
48
  tobj_vec m_mid;    /* int material ids */
49
  tobj_vec m_sg;     /* unsigned smoothing ids */
50
  tobj_vec l_idx;    /* tobj_index */
51
  tobj_vec l_cnt;    /* int per-line counts */
52
  tobj_vec p_idx;    /* tobj_index */
53
54
  /* per-face scratch (reused) */
55
  tobj_vec corners;  /* tobj_index */
56
  tobj_vec ringpos;  /* TOBJ_REAL */
57
  tobj_vec tris;     /* uint32_t */
58
  tobj_vec tscratch; /* uint8_t */
59
60
  /* free-form geometry (only used when cfg->parse_freeform) */
61
  tobj_vec ff_vp;      /* reals (global parameter vertices) */
62
  int ff_vp_comps;
63
  bool ff_present;     /* any free-form element in current shape */
64
  bool ff_active;      /* between curv/curv2/surf and end */
65
  int ff_kind;         /* 1=curv 2=curv2 3=surf */
66
  tobj_cstype ff_cstype;
67
  bool ff_rational;
68
  int ff_degu, ff_degv;
69
  TOBJ_REAL ff_u0, ff_u1, ff_s0, ff_s1, ff_t0, ff_t1;
70
  tobj_vec ff_cp_int;  /* int control points (curv: v idx, curv2: vp idx) */
71
  tobj_vec ff_cp_idx;  /* tobj_index control points (surf) */
72
  tobj_vec ff_knu;     /* reals (parm u) */
73
  tobj_vec ff_knv;     /* reals (parm v) */
74
  tobj_vec ff_trim;    /* TOBJ_T(tobj_trim_loop) */
75
  tobj_vec ff_hole;
76
  tobj_vec ff_scrv;
77
  tobj_vec sh_curves;   /* TOBJ_T(tobj_curve) */
78
  tobj_vec sh_curves2;  /* TOBJ_T(tobj_curve2) */
79
  tobj_vec sh_surfaces; /* TOBJ_T(tobj_surface) */
80
81
  /* parser state */
82
  int cur_material;
83
  unsigned cur_smooth;
84
85
  /* multithreading pass 2: vertices already parsed; indices resolve against
86
   * running counts instead of the (already full) attrib arrays */
87
  bool prefilled;
88
  size_t run_numv, run_numvt, run_numvn, run_numvp;
89
} TOBJ_T(tobj_builder);
90
91
/* Counts used for index fixup: running counts in MT pass 2, else live counts. */
92
36.2M
static size_t TOBJ_T(cnt_v)(TOBJ_T(tobj_builder) * b) {
93
36.2M
  return b->prefilled ? b->run_numv : b->av.count / 3u;
94
36.2M
}
tiny_obj_c.c:cnt_v_f
Line
Count
Source
92
36.2M
static size_t TOBJ_T(cnt_v)(TOBJ_T(tobj_builder) * b) {
93
36.2M
  return b->prefilled ? b->run_numv : b->av.count / 3u;
94
36.2M
}
Unexecuted instantiation: tiny_obj_c.c:cnt_v_d
95
36.2M
static size_t TOBJ_T(cnt_vt)(TOBJ_T(tobj_builder) * b) {
96
36.2M
  return b->prefilled ? b->run_numvt : b->avt.count / 2u;
97
36.2M
}
tiny_obj_c.c:cnt_vt_f
Line
Count
Source
95
36.2M
static size_t TOBJ_T(cnt_vt)(TOBJ_T(tobj_builder) * b) {
96
36.2M
  return b->prefilled ? b->run_numvt : b->avt.count / 2u;
97
36.2M
}
Unexecuted instantiation: tiny_obj_c.c:cnt_vt_d
98
36.2M
static size_t TOBJ_T(cnt_vn)(TOBJ_T(tobj_builder) * b) {
99
36.2M
  return b->prefilled ? b->run_numvn : b->avn.count / 3u;
100
36.2M
}
tiny_obj_c.c:cnt_vn_f
Line
Count
Source
98
36.2M
static size_t TOBJ_T(cnt_vn)(TOBJ_T(tobj_builder) * b) {
99
36.2M
  return b->prefilled ? b->run_numvn : b->avn.count / 3u;
100
36.2M
}
Unexecuted instantiation: tiny_obj_c.c:cnt_vn_d
101
102
0
static bool TOBJ_T(b_fail)(TOBJ_T(tobj_builder) * b, tobj_result e) {
103
0
  if (b->err == TOBJ_OK) b->err = e;
104
0
  return false;
105
0
}
Unexecuted instantiation: tiny_obj_c.c:b_fail_f
Unexecuted instantiation: tiny_obj_c.c:b_fail_d
106
107
42.9M
static void TOBJ_T(b_warn)(TOBJ_T(tobj_builder) * b, const char *msg) {
108
42.9M
  tobj_diag_append(b->diag, b->alloc, TOBJ_SEV_WARNING, b->line, msg);
109
42.9M
}
tiny_obj_c.c:b_warn_f
Line
Count
Source
107
42.9M
static void TOBJ_T(b_warn)(TOBJ_T(tobj_builder) * b, const char *msg) {
108
42.9M
  tobj_diag_append(b->diag, b->alloc, TOBJ_SEV_WARNING, b->line, msg);
109
42.9M
}
Unexecuted instantiation: tiny_obj_c.c:b_warn_d
110
111
/* ----------------------------------------------------------------------- */
112
/* index fixup (1-based -> 0-based, negative -> relative)                  */
113
/* ----------------------------------------------------------------------- */
114
115
2.44M
static bool TOBJ_T(fix_index)(int idx, size_t n, int *out) {
116
2.44M
  if (idx > 0) {
117
2.39M
    *out = idx - 1;
118
2.39M
    return true;
119
2.39M
  }
120
51.2k
  if (idx == 0) {
121
10.0k
    *out = -1;
122
10.0k
    return false; /* zero index: invalid per spec */
123
10.0k
  }
124
41.1k
  long long r = (long long)n + idx;
125
41.1k
  if (r < 0) {
126
17.1k
    *out = -1;
127
17.1k
    return false;
128
17.1k
  }
129
24.0k
  *out = (int)r;
130
24.0k
  return true;
131
41.1k
}
tiny_obj_c.c:fix_index_f
Line
Count
Source
115
2.44M
static bool TOBJ_T(fix_index)(int idx, size_t n, int *out) {
116
2.44M
  if (idx > 0) {
117
2.39M
    *out = idx - 1;
118
2.39M
    return true;
119
2.39M
  }
120
51.2k
  if (idx == 0) {
121
10.0k
    *out = -1;
122
10.0k
    return false; /* zero index: invalid per spec */
123
10.0k
  }
124
41.1k
  long long r = (long long)n + idx;
125
41.1k
  if (r < 0) {
126
17.1k
    *out = -1;
127
17.1k
    return false;
128
17.1k
  }
129
24.0k
  *out = (int)r;
130
  return true;
131
41.1k
}
Unexecuted instantiation: tiny_obj_c.c:fix_index_d
132
133
/* Parse one corner "v[/[vt][/vn]]" against explicit counts. Returns true if a
134
 * (possibly relative) vertex index was parsed and is in range. */
135
static bool TOBJ_T(parse_corner_n)(const char *tok, size_t len, size_t numv,
136
                                   size_t numvt, size_t numvn,
137
36.2M
                                   tobj_index *out) {
138
36.2M
  out->vertex_index = -1;
139
36.2M
  out->normal_index = -1;
140
36.2M
  out->texcoord_index = -1;
141
142
36.2M
  tobj_cursor c;
143
36.2M
  tobj_cur_init(&c, tok, len);
144
36.2M
  int vi;
145
36.2M
  if (!tobj_scan_int(&c.p, c.end, &vi)) return false;
146
36.2M
  bool ok = TOBJ_T(fix_index)(vi, numv, &out->vertex_index);
147
2.38M
  if (c.p < c.end && *c.p == '/') {
148
1.46M
    c.p++;
149
1.46M
    if (c.p < c.end && *c.p != '/') {
150
64.9k
      int ti;
151
64.9k
      if (tobj_scan_int(&c.p, c.end, &ti))
152
49.5k
        TOBJ_T(fix_index)(ti, numvt, &out->texcoord_index);
153
64.9k
    }
154
1.46M
    if (c.p < c.end && *c.p == '/') {
155
1.39M
      c.p++;
156
1.39M
      int ni;
157
1.39M
      if (tobj_scan_int(&c.p, c.end, &ni))
158
13.9k
        TOBJ_T(fix_index)(ni, numvn, &out->normal_index);
159
1.39M
    }
160
1.46M
  }
161
2.38M
  return ok;
162
36.2M
}
tiny_obj_c.c:parse_corner_n_f
Line
Count
Source
137
36.2M
                                   tobj_index *out) {
138
36.2M
  out->vertex_index = -1;
139
36.2M
  out->normal_index = -1;
140
36.2M
  out->texcoord_index = -1;
141
142
36.2M
  tobj_cursor c;
143
36.2M
  tobj_cur_init(&c, tok, len);
144
36.2M
  int vi;
145
36.2M
  if (!tobj_scan_int(&c.p, c.end, &vi)) return false;
146
36.2M
  bool ok = TOBJ_T(fix_index)(vi, numv, &out->vertex_index);
147
2.38M
  if (c.p < c.end && *c.p == '/') {
148
1.46M
    c.p++;
149
1.46M
    if (c.p < c.end && *c.p != '/') {
150
64.9k
      int ti;
151
64.9k
      if (tobj_scan_int(&c.p, c.end, &ti))
152
49.5k
        TOBJ_T(fix_index)(ti, numvt, &out->texcoord_index);
153
64.9k
    }
154
1.46M
    if (c.p < c.end && *c.p == '/') {
155
1.39M
      c.p++;
156
1.39M
      int ni;
157
1.39M
      if (tobj_scan_int(&c.p, c.end, &ni))
158
13.9k
        TOBJ_T(fix_index)(ni, numvn, &out->normal_index);
159
1.39M
    }
160
1.46M
  }
161
2.38M
  return ok;
162
36.2M
}
Unexecuted instantiation: tiny_obj_c.c:parse_corner_n_d
163
164
/* Parse one face/line/point corner using the builder's current counts. */
165
static void TOBJ_T(parse_corner)(TOBJ_T(tobj_builder) * b, const char *tok,
166
36.2M
                                 size_t len, tobj_index *out) {
167
36.2M
  bool ok = TOBJ_T(parse_corner_n)(tok, len, TOBJ_T(cnt_v)(b),
168
36.2M
                                   TOBJ_T(cnt_vt)(b), TOBJ_T(cnt_vn)(b), out);
169
36.2M
  if (!ok && out->vertex_index < 0)
170
33.8M
    TOBJ_T(b_warn)(b, "tinyobjc: invalid vertex index in face\n");
171
36.2M
}
tiny_obj_c.c:parse_corner_f
Line
Count
Source
166
36.2M
                                 size_t len, tobj_index *out) {
167
36.2M
  bool ok = TOBJ_T(parse_corner_n)(tok, len, TOBJ_T(cnt_v)(b),
168
36.2M
                                   TOBJ_T(cnt_vt)(b), TOBJ_T(cnt_vn)(b), out);
169
36.2M
  if (!ok && out->vertex_index < 0)
170
33.8M
    TOBJ_T(b_warn)(b, "tinyobjc: invalid vertex index in face\n");
171
36.2M
}
Unexecuted instantiation: tiny_obj_c.c:parse_corner_d
172
173
/* ----------------------------------------------------------------------- */
174
/* optional parallel-array helpers (lazy with backfill)                    */
175
/* ----------------------------------------------------------------------- */
176
177
static bool TOBJ_T(opt_push)(tobj_vec *v, size_t comps, size_t vidx,
178
                             const TOBJ_REAL *vals, const TOBJ_REAL *deflt,
179
20.0M
                             bool present, const tobj_allocator *a) {
180
20.0M
  if (!present && v->count == 0) return true; /* stay empty */
181
  /* backfill defaults up to vidx entries */
182
15.4M
  while (v->count / comps < vidx) {
183
8.27M
    if (!tobj_vec_append(v, deflt, comps, a)) return false;
184
8.27M
  }
185
7.13M
  const TOBJ_REAL *src = present ? vals : deflt;
186
7.13M
  return tobj_vec_append(v, src, comps, a);
187
7.13M
}
tiny_obj_c.c:opt_push_f
Line
Count
Source
179
20.0M
                             bool present, const tobj_allocator *a) {
180
20.0M
  if (!present && v->count == 0) return true; /* stay empty */
181
  /* backfill defaults up to vidx entries */
182
15.4M
  while (v->count / comps < vidx) {
183
8.27M
    if (!tobj_vec_append(v, deflt, comps, a)) return false;
184
8.27M
  }
185
7.13M
  const TOBJ_REAL *src = present ? vals : deflt;
186
7.13M
  return tobj_vec_append(v, src, comps, a);
187
7.13M
}
Unexecuted instantiation: tiny_obj_c.c:opt_push_d
188
189
/* ----------------------------------------------------------------------- */
190
/* directive handlers                                                      */
191
/* ----------------------------------------------------------------------- */
192
193
9.00M
static bool TOBJ_T(handle_v)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
194
9.00M
  double nums[8];
195
9.00M
  int n = 0;
196
9.10M
  while (n < 8) {
197
9.10M
    double d;
198
9.10M
    const char *save = c->p;
199
9.10M
    if (!tobj_scan_double(&c->p, c->end, &d)) {
200
9.00M
      c->p = save;
201
9.00M
      break;
202
9.00M
    }
203
103k
    nums[n++] = d;
204
103k
  }
205
9.00M
  if (n < 3) {
206
8.99M
    TOBJ_T(b_warn)(b, "tinyobjc: 'v' with fewer than 3 components\n");
207
35.9M
    while (n < 3) nums[n++] = 0.0;
208
8.99M
  }
209
9.00M
  if (b->av.count / 3u >= b->caps.vertices) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
210
9.00M
  size_t vidx = b->av.count / 3u;
211
9.00M
  TOBJ_REAL xyz[3] = {(TOBJ_REAL)nums[0], (TOBJ_REAL)nums[1], (TOBJ_REAL)nums[2]};
212
9.00M
  if (!tobj_vec_append(&b->av, xyz, 3, b->alloc)) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
213
214
  /* weight: only the 4-number form carries a vertex weight */
215
9.00M
  bool has_w = (n == 4);
216
9.00M
  TOBJ_REAL w = has_w ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1;
217
9.00M
  TOBJ_REAL wdef = (TOBJ_REAL)1;
218
9.00M
  if (!TOBJ_T(opt_push)(&b->avw, 1, vidx, &w, &wdef, has_w, b->alloc))
219
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
220
221
  /* color: the 6/7-number form carries r g b */
222
9.00M
  if (b->cfg->store_vertex_colors) {
223
9.00M
    bool has_c = (n >= 6);
224
9.00M
    TOBJ_REAL rgb[3];
225
9.00M
    rgb[0] = has_c ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1;
226
9.00M
    rgb[1] = has_c ? (TOBJ_REAL)nums[4] : (TOBJ_REAL)1;
227
9.00M
    rgb[2] = has_c ? (TOBJ_REAL)nums[5] : (TOBJ_REAL)1;
228
9.00M
    TOBJ_REAL white[3] = {(TOBJ_REAL)1, (TOBJ_REAL)1, (TOBJ_REAL)1};
229
9.00M
    if (has_c) b->color_seen = true;
230
9.00M
    if (!TOBJ_T(opt_push)(&b->acol, 3, vidx, rgb, white, has_c, b->alloc))
231
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
232
9.00M
  }
233
9.00M
  return true;
234
9.00M
}
tiny_obj_c.c:handle_v_f
Line
Count
Source
193
9.00M
static bool TOBJ_T(handle_v)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
194
9.00M
  double nums[8];
195
9.00M
  int n = 0;
196
9.10M
  while (n < 8) {
197
9.10M
    double d;
198
9.10M
    const char *save = c->p;
199
9.10M
    if (!tobj_scan_double(&c->p, c->end, &d)) {
200
9.00M
      c->p = save;
201
9.00M
      break;
202
9.00M
    }
203
103k
    nums[n++] = d;
204
103k
  }
205
9.00M
  if (n < 3) {
206
8.99M
    TOBJ_T(b_warn)(b, "tinyobjc: 'v' with fewer than 3 components\n");
207
35.9M
    while (n < 3) nums[n++] = 0.0;
208
8.99M
  }
209
9.00M
  if (b->av.count / 3u >= b->caps.vertices) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
210
9.00M
  size_t vidx = b->av.count / 3u;
211
9.00M
  TOBJ_REAL xyz[3] = {(TOBJ_REAL)nums[0], (TOBJ_REAL)nums[1], (TOBJ_REAL)nums[2]};
212
9.00M
  if (!tobj_vec_append(&b->av, xyz, 3, b->alloc)) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
213
214
  /* weight: only the 4-number form carries a vertex weight */
215
9.00M
  bool has_w = (n == 4);
216
9.00M
  TOBJ_REAL w = has_w ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1;
217
9.00M
  TOBJ_REAL wdef = (TOBJ_REAL)1;
218
9.00M
  if (!TOBJ_T(opt_push)(&b->avw, 1, vidx, &w, &wdef, has_w, b->alloc))
219
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
220
221
  /* color: the 6/7-number form carries r g b */
222
9.00M
  if (b->cfg->store_vertex_colors) {
223
9.00M
    bool has_c = (n >= 6);
224
9.00M
    TOBJ_REAL rgb[3];
225
9.00M
    rgb[0] = has_c ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1;
226
9.00M
    rgb[1] = has_c ? (TOBJ_REAL)nums[4] : (TOBJ_REAL)1;
227
9.00M
    rgb[2] = has_c ? (TOBJ_REAL)nums[5] : (TOBJ_REAL)1;
228
9.00M
    TOBJ_REAL white[3] = {(TOBJ_REAL)1, (TOBJ_REAL)1, (TOBJ_REAL)1};
229
9.00M
    if (has_c) b->color_seen = true;
230
9.00M
    if (!TOBJ_T(opt_push)(&b->acol, 3, vidx, rgb, white, has_c, b->alloc))
231
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
232
9.00M
  }
233
9.00M
  return true;
234
9.00M
}
Unexecuted instantiation: tiny_obj_c.c:handle_v_d
235
236
1.72M
static bool TOBJ_T(handle_vn)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
237
1.72M
  TOBJ_REAL xyz[3] = {0, 0, 0};
238
1.72M
  double d;
239
6.91M
  for (int i = 0; i < 3; i++)
240
5.18M
    if (tobj_scan_double(&c->p, c->end, &d)) xyz[i] = (TOBJ_REAL)d;
241
1.72M
  if (!tobj_vec_append(&b->avn, xyz, 3, b->alloc)) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
242
1.72M
  return true;
243
1.72M
}
tiny_obj_c.c:handle_vn_f
Line
Count
Source
236
1.72M
static bool TOBJ_T(handle_vn)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
237
1.72M
  TOBJ_REAL xyz[3] = {0, 0, 0};
238
1.72M
  double d;
239
6.91M
  for (int i = 0; i < 3; i++)
240
5.18M
    if (tobj_scan_double(&c->p, c->end, &d)) xyz[i] = (TOBJ_REAL)d;
241
1.72M
  if (!tobj_vec_append(&b->avn, xyz, 3, b->alloc)) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
242
1.72M
  return true;
243
1.72M
}
Unexecuted instantiation: tiny_obj_c.c:handle_vn_d
244
245
2.04M
static bool TOBJ_T(handle_vt)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
246
2.04M
  double d;
247
2.04M
  TOBJ_REAL uv[2] = {0, 0};
248
2.04M
  int n = 0;
249
2.23M
  for (; n < 2; n++) {
250
2.17M
    if (!tobj_scan_double(&c->p, c->end, &d)) break;
251
191k
    uv[n] = (TOBJ_REAL)d;
252
191k
  }
253
2.04M
  size_t tidx = b->avt.count / 2u;
254
2.04M
  if (!tobj_vec_append(&b->avt, uv, 2, b->alloc)) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
255
2.04M
  bool has_w = tobj_scan_double(&c->p, c->end, &d);
256
2.04M
  TOBJ_REAL w = has_w ? (TOBJ_REAL)d : (TOBJ_REAL)0;
257
2.04M
  TOBJ_REAL wdef = (TOBJ_REAL)0;
258
2.04M
  if (!TOBJ_T(opt_push)(&b->avtw, 1, tidx, &w, &wdef, has_w, b->alloc))
259
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
260
2.04M
  return true;
261
2.04M
}
tiny_obj_c.c:handle_vt_f
Line
Count
Source
245
2.04M
static bool TOBJ_T(handle_vt)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
246
2.04M
  double d;
247
2.04M
  TOBJ_REAL uv[2] = {0, 0};
248
2.04M
  int n = 0;
249
2.23M
  for (; n < 2; n++) {
250
2.17M
    if (!tobj_scan_double(&c->p, c->end, &d)) break;
251
191k
    uv[n] = (TOBJ_REAL)d;
252
191k
  }
253
2.04M
  size_t tidx = b->avt.count / 2u;
254
2.04M
  if (!tobj_vec_append(&b->avt, uv, 2, b->alloc)) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
255
2.04M
  bool has_w = tobj_scan_double(&c->p, c->end, &d);
256
2.04M
  TOBJ_REAL w = has_w ? (TOBJ_REAL)d : (TOBJ_REAL)0;
257
2.04M
  TOBJ_REAL wdef = (TOBJ_REAL)0;
258
2.04M
  if (!TOBJ_T(opt_push)(&b->avtw, 1, tidx, &w, &wdef, has_w, b->alloc))
259
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
260
2.04M
  return true;
261
2.04M
}
Unexecuted instantiation: tiny_obj_c.c:handle_vt_d
262
263
/* Append triangulated/raw face corners to the current shape mesh. */
264
54.0k
static bool TOBJ_T(emit_face)(TOBJ_T(tobj_builder) * b, size_t arity) {
265
54.0k
  const tobj_index *corners = (const tobj_index *)b->corners.data;
266
54.0k
  bool do_tri = b->triangulate && arity > 3u;
267
268
54.0k
  if (!do_tri) {
269
7.24k
    if (b->m_idx.count + arity > b->caps.indices)
270
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
271
7.24k
    if (!tobj_vec_append(&b->m_idx, corners, arity, b->alloc))
272
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
273
7.24k
    unsigned fv = (unsigned)tobj_size_to_int(arity);
274
7.24k
    if (!tobj_vec_append(&b->m_fv, &fv, 1, b->alloc) ||
275
7.24k
        !tobj_vec_append(&b->m_mid, &b->cur_material, 1, b->alloc) ||
276
7.24k
        !tobj_vec_append(&b->m_sg, &b->cur_smooth, 1, b->alloc))
277
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
278
7.24k
    b->cur_has_prims = true;
279
7.24k
    return true;
280
7.24k
  }
281
282
  /* triangulate */
283
46.8k
  size_t numv = TOBJ_T(cnt_v)(b);
284
46.8k
  bool pos_ok = true;
285
2.28M
  for (size_t k = 0; k < arity; k++) {
286
2.24M
    int vi = corners[k].vertex_index;
287
2.24M
    if (vi < 0 || (size_t)vi >= numv) {
288
10.1k
      pos_ok = false;
289
10.1k
      break;
290
10.1k
    }
291
2.24M
  }
292
293
46.8k
  uint32_t ntri = (uint32_t)(arity - 2u);
294
46.8k
  b->tris.count = 0;
295
46.8k
  if (!tobj_vec_reserve(&b->tris, (size_t)ntri * 3u, b->alloc))
296
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
297
46.8k
  uint32_t *tri = (uint32_t *)b->tris.data;
298
299
46.8k
  if (pos_ok) {
300
36.6k
    b->ringpos.count = 0;
301
36.6k
    if (!tobj_vec_reserve(&b->ringpos, arity * 3u, b->alloc))
302
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
303
36.6k
    TOBJ_REAL *rp = (TOBJ_REAL *)b->ringpos.data;
304
36.6k
    const TOBJ_REAL *vsrc = (const TOBJ_REAL *)b->av.data;
305
2.24M
    for (size_t k = 0; k < arity; k++) {
306
2.20M
      size_t vi = (size_t)corners[k].vertex_index;
307
2.20M
      rp[3 * k + 0] = vsrc[3 * vi + 0];
308
2.20M
      rp[3 * k + 1] = vsrc[3 * vi + 1];
309
2.20M
      rp[3 * k + 2] = vsrc[3 * vi + 2];
310
2.20M
    }
311
36.6k
    size_t need_scratch = tobj_tess_scratch_size((uint32_t)arity);
312
36.6k
    if (!tobj_vec_reserve(&b->tscratch, need_scratch, b->alloc))
313
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
314
315
36.6k
    TOBJ_T(tobj_tess_desc) desc;
316
36.6k
    tobj_memset(&desc, 0, sizeof desc);
317
36.6k
    desc.points = rp;
318
36.6k
    desc.n = (uint32_t)arity;
319
36.6k
    desc.out_indices = tri;
320
36.6k
    desc.out_cap = ntri * 3u;
321
36.6k
    desc.scratch = b->tscratch.data;
322
36.6k
    desc.scratch_size = need_scratch;
323
36.6k
    tobj_tess_result res;
324
36.6k
    TOBJ_T(tobj_tess_polygon)(&desc, &res);
325
36.6k
    ntri = res.num_triangles;
326
36.6k
    if (res.status == TOBJ_TESS_DEGENERATE_BESTEFFORT)
327
28.3k
      TOBJ_T(b_warn)(b, "tinyobjc: degenerate polygon triangulated best-effort\n");
328
36.6k
  } else {
329
10.1k
    TOBJ_T(b_warn)(b, "tinyobjc: face with invalid vertex index; fan fallback\n");
330
22.1M
    for (uint32_t t = 0; t < ntri; t++) {
331
22.1M
      tri[3 * t + 0] = 0;
332
22.1M
      tri[3 * t + 1] = t + 1;
333
22.1M
      tri[3 * t + 2] = t + 2;
334
22.1M
    }
335
10.1k
  }
336
337
46.8k
  if (b->m_idx.count + (size_t)ntri * 3u > b->caps.indices)
338
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
339
24.2M
  for (uint32_t t = 0; t < ntri; t++) {
340
24.1M
    tobj_index tidx[3];
341
24.1M
    tidx[0] = corners[tri[3 * t + 0]];
342
24.1M
    tidx[1] = corners[tri[3 * t + 1]];
343
24.1M
    tidx[2] = corners[tri[3 * t + 2]];
344
24.1M
    unsigned fv = 3;
345
24.1M
    if (!tobj_vec_append(&b->m_idx, tidx, 3, b->alloc) ||
346
24.1M
        !tobj_vec_append(&b->m_fv, &fv, 1, b->alloc) ||
347
24.1M
        !tobj_vec_append(&b->m_mid, &b->cur_material, 1, b->alloc) ||
348
24.1M
        !tobj_vec_append(&b->m_sg, &b->cur_smooth, 1, b->alloc))
349
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
350
24.1M
  }
351
46.8k
  b->cur_has_prims = true;
352
46.8k
  return true;
353
46.8k
}
tiny_obj_c.c:emit_face_f
Line
Count
Source
264
54.0k
static bool TOBJ_T(emit_face)(TOBJ_T(tobj_builder) * b, size_t arity) {
265
54.0k
  const tobj_index *corners = (const tobj_index *)b->corners.data;
266
54.0k
  bool do_tri = b->triangulate && arity > 3u;
267
268
54.0k
  if (!do_tri) {
269
7.24k
    if (b->m_idx.count + arity > b->caps.indices)
270
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
271
7.24k
    if (!tobj_vec_append(&b->m_idx, corners, arity, b->alloc))
272
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
273
7.24k
    unsigned fv = (unsigned)tobj_size_to_int(arity);
274
7.24k
    if (!tobj_vec_append(&b->m_fv, &fv, 1, b->alloc) ||
275
7.24k
        !tobj_vec_append(&b->m_mid, &b->cur_material, 1, b->alloc) ||
276
7.24k
        !tobj_vec_append(&b->m_sg, &b->cur_smooth, 1, b->alloc))
277
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
278
7.24k
    b->cur_has_prims = true;
279
7.24k
    return true;
280
7.24k
  }
281
282
  /* triangulate */
283
46.8k
  size_t numv = TOBJ_T(cnt_v)(b);
284
46.8k
  bool pos_ok = true;
285
2.28M
  for (size_t k = 0; k < arity; k++) {
286
2.24M
    int vi = corners[k].vertex_index;
287
2.24M
    if (vi < 0 || (size_t)vi >= numv) {
288
10.1k
      pos_ok = false;
289
10.1k
      break;
290
10.1k
    }
291
2.24M
  }
292
293
46.8k
  uint32_t ntri = (uint32_t)(arity - 2u);
294
46.8k
  b->tris.count = 0;
295
46.8k
  if (!tobj_vec_reserve(&b->tris, (size_t)ntri * 3u, b->alloc))
296
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
297
46.8k
  uint32_t *tri = (uint32_t *)b->tris.data;
298
299
46.8k
  if (pos_ok) {
300
36.6k
    b->ringpos.count = 0;
301
36.6k
    if (!tobj_vec_reserve(&b->ringpos, arity * 3u, b->alloc))
302
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
303
36.6k
    TOBJ_REAL *rp = (TOBJ_REAL *)b->ringpos.data;
304
36.6k
    const TOBJ_REAL *vsrc = (const TOBJ_REAL *)b->av.data;
305
2.24M
    for (size_t k = 0; k < arity; k++) {
306
2.20M
      size_t vi = (size_t)corners[k].vertex_index;
307
2.20M
      rp[3 * k + 0] = vsrc[3 * vi + 0];
308
2.20M
      rp[3 * k + 1] = vsrc[3 * vi + 1];
309
2.20M
      rp[3 * k + 2] = vsrc[3 * vi + 2];
310
2.20M
    }
311
36.6k
    size_t need_scratch = tobj_tess_scratch_size((uint32_t)arity);
312
36.6k
    if (!tobj_vec_reserve(&b->tscratch, need_scratch, b->alloc))
313
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
314
315
36.6k
    TOBJ_T(tobj_tess_desc) desc;
316
36.6k
    tobj_memset(&desc, 0, sizeof desc);
317
36.6k
    desc.points = rp;
318
36.6k
    desc.n = (uint32_t)arity;
319
36.6k
    desc.out_indices = tri;
320
36.6k
    desc.out_cap = ntri * 3u;
321
36.6k
    desc.scratch = b->tscratch.data;
322
36.6k
    desc.scratch_size = need_scratch;
323
36.6k
    tobj_tess_result res;
324
36.6k
    TOBJ_T(tobj_tess_polygon)(&desc, &res);
325
36.6k
    ntri = res.num_triangles;
326
36.6k
    if (res.status == TOBJ_TESS_DEGENERATE_BESTEFFORT)
327
28.3k
      TOBJ_T(b_warn)(b, "tinyobjc: degenerate polygon triangulated best-effort\n");
328
36.6k
  } else {
329
10.1k
    TOBJ_T(b_warn)(b, "tinyobjc: face with invalid vertex index; fan fallback\n");
330
22.1M
    for (uint32_t t = 0; t < ntri; t++) {
331
22.1M
      tri[3 * t + 0] = 0;
332
22.1M
      tri[3 * t + 1] = t + 1;
333
22.1M
      tri[3 * t + 2] = t + 2;
334
22.1M
    }
335
10.1k
  }
336
337
46.8k
  if (b->m_idx.count + (size_t)ntri * 3u > b->caps.indices)
338
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
339
24.2M
  for (uint32_t t = 0; t < ntri; t++) {
340
24.1M
    tobj_index tidx[3];
341
24.1M
    tidx[0] = corners[tri[3 * t + 0]];
342
24.1M
    tidx[1] = corners[tri[3 * t + 1]];
343
24.1M
    tidx[2] = corners[tri[3 * t + 2]];
344
24.1M
    unsigned fv = 3;
345
24.1M
    if (!tobj_vec_append(&b->m_idx, tidx, 3, b->alloc) ||
346
24.1M
        !tobj_vec_append(&b->m_fv, &fv, 1, b->alloc) ||
347
24.1M
        !tobj_vec_append(&b->m_mid, &b->cur_material, 1, b->alloc) ||
348
24.1M
        !tobj_vec_append(&b->m_sg, &b->cur_smooth, 1, b->alloc))
349
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
350
24.1M
  }
351
46.8k
  b->cur_has_prims = true;
352
  return true;
353
46.8k
}
Unexecuted instantiation: tiny_obj_c.c:emit_face_d
354
355
74.5k
static bool TOBJ_T(handle_f)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
356
74.5k
  b->corners.count = 0;
357
74.5k
  const char *tok;
358
74.5k
  size_t tlen;
359
74.5k
  size_t arity = 0;
360
24.5M
  while (tobj_next_token(c, &tok, &tlen)) {
361
24.4M
    if (arity >= b->caps.arity) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
362
24.4M
    tobj_index *slot = (tobj_index *)tobj_vec_push(&b->corners, b->alloc);
363
24.4M
    if (!slot) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
364
24.4M
    TOBJ_T(parse_corner)(b, tok, tlen, slot);
365
24.4M
    arity++;
366
24.4M
  }
367
74.5k
  if (arity < 3) {
368
20.5k
    TOBJ_T(b_warn)(b, "tinyobjc: degenerate face (<3 vertices)\n");
369
20.5k
    return true;
370
20.5k
  }
371
54.0k
  if (b->m_fv.count >= b->caps.faces) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
372
54.0k
  return TOBJ_T(emit_face)(b, arity);
373
54.0k
}
tiny_obj_c.c:handle_f_f
Line
Count
Source
355
74.5k
static bool TOBJ_T(handle_f)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
356
74.5k
  b->corners.count = 0;
357
74.5k
  const char *tok;
358
74.5k
  size_t tlen;
359
74.5k
  size_t arity = 0;
360
24.5M
  while (tobj_next_token(c, &tok, &tlen)) {
361
24.4M
    if (arity >= b->caps.arity) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
362
24.4M
    tobj_index *slot = (tobj_index *)tobj_vec_push(&b->corners, b->alloc);
363
24.4M
    if (!slot) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
364
24.4M
    TOBJ_T(parse_corner)(b, tok, tlen, slot);
365
24.4M
    arity++;
366
24.4M
  }
367
74.5k
  if (arity < 3) {
368
20.5k
    TOBJ_T(b_warn)(b, "tinyobjc: degenerate face (<3 vertices)\n");
369
20.5k
    return true;
370
20.5k
  }
371
54.0k
  if (b->m_fv.count >= b->caps.faces) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
372
54.0k
  return TOBJ_T(emit_face)(b, arity);
373
54.0k
}
Unexecuted instantiation: tiny_obj_c.c:handle_f_d
374
375
484k
static bool TOBJ_T(handle_l)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
376
484k
  const char *tok;
377
484k
  size_t tlen;
378
484k
  size_t cnt = 0;
379
484k
  size_t start = b->l_idx.count;
380
7.50M
  while (tobj_next_token(c, &tok, &tlen)) {
381
7.01M
    if (cnt >= b->caps.arity) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
382
7.01M
    tobj_index idx;
383
7.01M
    TOBJ_T(parse_corner)(b, tok, tlen, &idx);
384
7.01M
    if (!tobj_vec_append(&b->l_idx, &idx, 1, b->alloc))
385
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
386
7.01M
    cnt++;
387
7.01M
  }
388
484k
  if (cnt < 2) {
389
11.0k
    b->l_idx.count = start; /* roll back */
390
11.0k
    TOBJ_T(b_warn)(b, "tinyobjc: degenerate line (<2 vertices)\n");
391
11.0k
    return true;
392
11.0k
  }
393
473k
  int icnt = tobj_size_to_int(cnt);
394
473k
  if (!tobj_vec_append(&b->l_cnt, &icnt, 1, b->alloc))
395
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
396
473k
  b->cur_has_prims = true;
397
473k
  return true;
398
473k
}
tiny_obj_c.c:handle_l_f
Line
Count
Source
375
484k
static bool TOBJ_T(handle_l)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
376
484k
  const char *tok;
377
484k
  size_t tlen;
378
484k
  size_t cnt = 0;
379
484k
  size_t start = b->l_idx.count;
380
7.50M
  while (tobj_next_token(c, &tok, &tlen)) {
381
7.01M
    if (cnt >= b->caps.arity) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
382
7.01M
    tobj_index idx;
383
7.01M
    TOBJ_T(parse_corner)(b, tok, tlen, &idx);
384
7.01M
    if (!tobj_vec_append(&b->l_idx, &idx, 1, b->alloc))
385
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
386
7.01M
    cnt++;
387
7.01M
  }
388
484k
  if (cnt < 2) {
389
11.0k
    b->l_idx.count = start; /* roll back */
390
11.0k
    TOBJ_T(b_warn)(b, "tinyobjc: degenerate line (<2 vertices)\n");
391
11.0k
    return true;
392
11.0k
  }
393
473k
  int icnt = tobj_size_to_int(cnt);
394
473k
  if (!tobj_vec_append(&b->l_cnt, &icnt, 1, b->alloc))
395
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
396
473k
  b->cur_has_prims = true;
397
  return true;
398
473k
}
Unexecuted instantiation: tiny_obj_c.c:handle_l_d
399
400
338k
static bool TOBJ_T(handle_p)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
401
338k
  const char *tok;
402
338k
  size_t tlen;
403
5.11M
  while (tobj_next_token(c, &tok, &tlen)) {
404
4.77M
    tobj_index idx;
405
4.77M
    TOBJ_T(parse_corner)(b, tok, tlen, &idx);
406
4.77M
    if (!tobj_vec_append(&b->p_idx, &idx, 1, b->alloc))
407
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
408
4.77M
    b->cur_has_prims = true;
409
4.77M
  }
410
338k
  return true;
411
338k
}
tiny_obj_c.c:handle_p_f
Line
Count
Source
400
338k
static bool TOBJ_T(handle_p)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
401
338k
  const char *tok;
402
338k
  size_t tlen;
403
5.11M
  while (tobj_next_token(c, &tok, &tlen)) {
404
4.77M
    tobj_index idx;
405
4.77M
    TOBJ_T(parse_corner)(b, tok, tlen, &idx);
406
4.77M
    if (!tobj_vec_append(&b->p_idx, &idx, 1, b->alloc))
407
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
408
4.77M
    b->cur_has_prims = true;
409
4.77M
  }
410
338k
  return true;
411
338k
}
Unexecuted instantiation: tiny_obj_c.c:handle_p_d
412
413
/* ----------------------------------------------------------------------- */
414
/* shape finalize                                                          */
415
/* ----------------------------------------------------------------------- */
416
417
#define TOBJ_FIN(dst_ptr, dst_cnt, vec, T)                       \
418
2.32M
  do {                                                           \
419
2.32M
    (dst_cnt) = (vec).count;                                     \
420
2.32M
    (dst_ptr) = (T *)tobj_vec_finalize(&(vec), b->arena, 16);    \
421
2.32M
    if ((vec).count && !(dst_ptr)) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC); \
422
2.32M
    (vec).count = 0;                                             \
423
2.32M
  } while (0)
424
425
static bool TOBJ_T(ff_finalize_elem)(TOBJ_T(tobj_builder) * b); /* fwd */
426
427
383k
static bool TOBJ_T(finalize_shape)(TOBJ_T(tobj_builder) * b) {
428
383k
  if (b->ff_active && !TOBJ_T(ff_finalize_elem)(b)) return false;
429
383k
  bool has_ff = b->sh_curves.count || b->sh_curves2.count ||
430
383k
                b->sh_surfaces.count;
431
383k
  if (!b->cur_has_prims && b->m_fv.count == 0 && b->l_cnt.count == 0 &&
432
51.4k
      b->p_idx.count == 0 && !has_ff) {
433
    /* nothing to emit, but keep the name for the next group */
434
51.4k
    return true;
435
51.4k
  }
436
331k
  if (b->shapes.count >= b->caps.shapes) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
437
438
331k
  TOBJ_T(tobj_shape) sh;
439
331k
  tobj_memset(&sh, 0, sizeof sh);
440
331k
  sh.name = b->cur_name ? b->cur_name : tobj_intern(b->arena, "", 0);
441
331k
  if (!sh.name) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
442
443
331k
  size_t nf;
444
331k
  TOBJ_FIN(sh.mesh.indices, sh.mesh.num_indices, b->m_idx, tobj_index);
445
331k
  TOBJ_FIN(sh.mesh.num_face_vertices, nf, b->m_fv, unsigned);
446
331k
  sh.mesh.num_faces = nf;
447
331k
  TOBJ_FIN(sh.mesh.material_ids, nf, b->m_mid, int);
448
331k
  TOBJ_FIN(sh.mesh.smoothing_group_ids, nf, b->m_sg, unsigned);
449
450
331k
  TOBJ_FIN(sh.lines.indices, sh.lines.num_indices, b->l_idx, tobj_index);
451
331k
  TOBJ_FIN(sh.lines.num_line_vertices, sh.lines.num_lines, b->l_cnt, int);
452
453
331k
  TOBJ_FIN(sh.points.indices, sh.points.num_indices, b->p_idx, tobj_index);
454
455
  /* free-form geometry, if any accumulated for this shape */
456
331k
  if (b->sh_curves.count || b->sh_curves2.count || b->sh_surfaces.count) {
457
0
    TOBJ_T(tobj_freeform) *ff = (TOBJ_T(tobj_freeform) *)tobj_arena_alloc(
458
0
        b->arena, sizeof(*ff), 16);
459
0
    if (!ff) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
460
0
    tobj_memset(ff, 0, sizeof *ff);
461
0
    ff->vp.components = b->ff_vp_comps;
462
0
    ff->vp.uvw.count = b->ff_vp.count;
463
0
    ff->vp.uvw.ptr = (TOBJ_REAL *)tobj_arena_dup(
464
0
        b->arena, b->ff_vp.data, b->ff_vp.count * sizeof(TOBJ_REAL), 16);
465
0
    if (b->ff_vp.count && !ff->vp.uvw.ptr)
466
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
467
0
    TOBJ_FIN(ff->curves, ff->num_curves, b->sh_curves, TOBJ_T(tobj_curve));
468
0
    TOBJ_FIN(ff->curves2, ff->num_curves2, b->sh_curves2, TOBJ_T(tobj_curve2));
469
0
    TOBJ_FIN(ff->surfaces, ff->num_surfaces, b->sh_surfaces,
470
0
             TOBJ_T(tobj_surface));
471
0
    sh.freeform = ff;
472
0
  }
473
474
331k
  TOBJ_T(tobj_shape) *slot = (TOBJ_T(tobj_shape) *)tobj_vec_push(&b->shapes, b->alloc);
475
331k
  if (!slot) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
476
331k
  *slot = sh;
477
331k
  b->cur_has_prims = false;
478
331k
  return true;
479
331k
}
tiny_obj_c.c:finalize_shape_f
Line
Count
Source
427
383k
static bool TOBJ_T(finalize_shape)(TOBJ_T(tobj_builder) * b) {
428
383k
  if (b->ff_active && !TOBJ_T(ff_finalize_elem)(b)) return false;
429
383k
  bool has_ff = b->sh_curves.count || b->sh_curves2.count ||
430
383k
                b->sh_surfaces.count;
431
383k
  if (!b->cur_has_prims && b->m_fv.count == 0 && b->l_cnt.count == 0 &&
432
51.4k
      b->p_idx.count == 0 && !has_ff) {
433
    /* nothing to emit, but keep the name for the next group */
434
51.4k
    return true;
435
51.4k
  }
436
331k
  if (b->shapes.count >= b->caps.shapes) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
437
438
331k
  TOBJ_T(tobj_shape) sh;
439
331k
  tobj_memset(&sh, 0, sizeof sh);
440
331k
  sh.name = b->cur_name ? b->cur_name : tobj_intern(b->arena, "", 0);
441
331k
  if (!sh.name) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
442
443
331k
  size_t nf;
444
331k
  TOBJ_FIN(sh.mesh.indices, sh.mesh.num_indices, b->m_idx, tobj_index);
445
331k
  TOBJ_FIN(sh.mesh.num_face_vertices, nf, b->m_fv, unsigned);
446
331k
  sh.mesh.num_faces = nf;
447
331k
  TOBJ_FIN(sh.mesh.material_ids, nf, b->m_mid, int);
448
331k
  TOBJ_FIN(sh.mesh.smoothing_group_ids, nf, b->m_sg, unsigned);
449
450
331k
  TOBJ_FIN(sh.lines.indices, sh.lines.num_indices, b->l_idx, tobj_index);
451
331k
  TOBJ_FIN(sh.lines.num_line_vertices, sh.lines.num_lines, b->l_cnt, int);
452
453
331k
  TOBJ_FIN(sh.points.indices, sh.points.num_indices, b->p_idx, tobj_index);
454
455
  /* free-form geometry, if any accumulated for this shape */
456
331k
  if (b->sh_curves.count || b->sh_curves2.count || b->sh_surfaces.count) {
457
0
    TOBJ_T(tobj_freeform) *ff = (TOBJ_T(tobj_freeform) *)tobj_arena_alloc(
458
0
        b->arena, sizeof(*ff), 16);
459
0
    if (!ff) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
460
0
    tobj_memset(ff, 0, sizeof *ff);
461
0
    ff->vp.components = b->ff_vp_comps;
462
0
    ff->vp.uvw.count = b->ff_vp.count;
463
0
    ff->vp.uvw.ptr = (TOBJ_REAL *)tobj_arena_dup(
464
0
        b->arena, b->ff_vp.data, b->ff_vp.count * sizeof(TOBJ_REAL), 16);
465
0
    if (b->ff_vp.count && !ff->vp.uvw.ptr)
466
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
467
0
    TOBJ_FIN(ff->curves, ff->num_curves, b->sh_curves, TOBJ_T(tobj_curve));
468
0
    TOBJ_FIN(ff->curves2, ff->num_curves2, b->sh_curves2, TOBJ_T(tobj_curve2));
469
0
    TOBJ_FIN(ff->surfaces, ff->num_surfaces, b->sh_surfaces,
470
0
             TOBJ_T(tobj_surface));
471
0
    sh.freeform = ff;
472
0
  }
473
474
331k
  TOBJ_T(tobj_shape) *slot = (TOBJ_T(tobj_shape) *)tobj_vec_push(&b->shapes, b->alloc);
475
331k
  if (!slot) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
476
331k
  *slot = sh;
477
331k
  b->cur_has_prims = false;
478
  return true;
479
331k
}
Unexecuted instantiation: tiny_obj_c.c:finalize_shape_d
480
481
/* Start a new shape group with the given (interned) name. */
482
379k
static bool TOBJ_T(new_group)(TOBJ_T(tobj_builder) * b, const char *name) {
483
379k
  if (!TOBJ_T(finalize_shape)(b)) return false;
484
379k
  b->cur_name = name;
485
379k
  return true;
486
379k
}
tiny_obj_c.c:new_group_f
Line
Count
Source
482
379k
static bool TOBJ_T(new_group)(TOBJ_T(tobj_builder) * b, const char *name) {
483
379k
  if (!TOBJ_T(finalize_shape)(b)) return false;
484
379k
  b->cur_name = name;
485
  return true;
486
379k
}
Unexecuted instantiation: tiny_obj_c.c:new_group_d
487
488
/* ----------------------------------------------------------------------- */
489
/* material support                                                        */
490
/* ----------------------------------------------------------------------- */
491
492
0
static void TOBJ_T(texopt_init)(TOBJ_T(tobj_texture_option) * t, tobj_arena *ar) {
493
0
  tobj_memset(t, 0, sizeof *t);
494
0
  t->type = TOBJ_TEXTURE_TYPE_NONE;
495
0
  t->sharpness = (TOBJ_REAL)1;
496
0
  t->brightness = (TOBJ_REAL)0;
497
0
  t->contrast = (TOBJ_REAL)1;
498
0
  t->scale[0] = t->scale[1] = t->scale[2] = (TOBJ_REAL)1;
499
0
  t->texture_resolution = -1;
500
0
  t->clamp = false;
501
0
  t->blendu = true;
502
0
  t->blendv = true;
503
0
  t->imfchan = 'm';
504
0
  t->bump_multiplier = (TOBJ_REAL)1;
505
0
  t->colorspace = tobj_intern(ar, "", 0);
506
0
}
Unexecuted instantiation: tiny_obj_c.c:texopt_init_f
Unexecuted instantiation: tiny_obj_c.c:texopt_init_d
507
508
0
static void TOBJ_T(material_init)(TOBJ_T(tobj_material) * m, tobj_arena *ar) {
509
0
  tobj_memset(m, 0, sizeof *m);
510
0
  m->shininess = (TOBJ_REAL)1;
511
0
  m->ior = (TOBJ_REAL)1;
512
0
  m->dissolve = (TOBJ_REAL)1;
513
0
  m->illum = 0;
514
0
  const char *empty = tobj_intern(ar, "", 0);
515
0
  m->name = empty;
516
0
  m->ambient_texname = m->diffuse_texname = m->specular_texname = empty;
517
0
  m->specular_highlight_texname = m->bump_texname = m->displacement_texname = empty;
518
0
  m->alpha_texname = m->reflection_texname = empty;
519
0
  m->roughness_texname = m->metallic_texname = m->sheen_texname = empty;
520
0
  m->emissive_texname = m->normal_texname = empty;
521
0
  TOBJ_T(texopt_init)(&m->ambient_texopt, ar);
522
0
  TOBJ_T(texopt_init)(&m->diffuse_texopt, ar);
523
0
  TOBJ_T(texopt_init)(&m->specular_texopt, ar);
524
0
  TOBJ_T(texopt_init)(&m->specular_highlight_texopt, ar);
525
0
  TOBJ_T(texopt_init)(&m->bump_texopt, ar);
526
0
  TOBJ_T(texopt_init)(&m->displacement_texopt, ar);
527
0
  TOBJ_T(texopt_init)(&m->alpha_texopt, ar);
528
0
  TOBJ_T(texopt_init)(&m->reflection_texopt, ar);
529
0
  TOBJ_T(texopt_init)(&m->roughness_texopt, ar);
530
0
  TOBJ_T(texopt_init)(&m->metallic_texopt, ar);
531
0
  TOBJ_T(texopt_init)(&m->sheen_texopt, ar);
532
0
  TOBJ_T(texopt_init)(&m->emissive_texopt, ar);
533
0
  TOBJ_T(texopt_init)(&m->normal_texopt, ar);
534
0
}
Unexecuted instantiation: tiny_obj_c.c:material_init_f
Unexecuted instantiation: tiny_obj_c.c:material_init_d
535
536
static int TOBJ_T(mat_find)(TOBJ_T(tobj_builder) * b, const char *name,
537
581
                            size_t len) {
538
581
  const char **keys = (const char **)b->matmap_key.data;
539
581
  const int *ids = (const int *)b->matmap_id.data;
540
581
  for (size_t i = 0; i < b->matmap_key.count; i++) {
541
0
    if (tobj_slice_eq(name, len, keys[i])) return ids[i];
542
0
  }
543
581
  return -1;
544
581
}
tiny_obj_c.c:mat_find_f
Line
Count
Source
537
581
                            size_t len) {
538
581
  const char **keys = (const char **)b->matmap_key.data;
539
581
  const int *ids = (const int *)b->matmap_id.data;
540
581
  for (size_t i = 0; i < b->matmap_key.count; i++) {
541
0
    if (tobj_slice_eq(name, len, keys[i])) return ids[i];
542
0
  }
543
581
  return -1;
544
581
}
Unexecuted instantiation: tiny_obj_c.c:mat_find_d
545
546
/* Read up to `n` reals from the cursor into dst (missing left untouched). */
547
0
static void TOBJ_T(read_reals)(tobj_cursor *c, TOBJ_REAL *dst, int n) {
548
0
  double d;
549
0
  for (int i = 0; i < n; i++) {
550
0
    if (!tobj_scan_double(&c->p, c->end, &d)) break;
551
0
    dst[i] = (TOBJ_REAL)d;
552
0
  }
553
0
}
Unexecuted instantiation: tiny_obj_c.c:read_reals_f
Unexecuted instantiation: tiny_obj_c.c:read_reals_d
554
555
/* Trim leading/trailing spaces of the cursor remainder and intern it. */
556
379k
static const char *TOBJ_T(intern_rest)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
557
379k
  const char *p = c->p, *e = c->end;
558
393k
  while (p < e && tobj_is_space(*p)) p++;
559
380k
  while (e > p && tobj_is_space(e[-1])) e--;
560
379k
  return tobj_intern(b->arena, p, (size_t)(e - p));
561
379k
}
tiny_obj_c.c:intern_rest_f
Line
Count
Source
556
379k
static const char *TOBJ_T(intern_rest)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
557
379k
  const char *p = c->p, *e = c->end;
558
393k
  while (p < e && tobj_is_space(*p)) p++;
559
380k
  while (e > p && tobj_is_space(e[-1])) e--;
560
379k
  return tobj_intern(b->arena, p, (size_t)(e - p));
561
379k
}
Unexecuted instantiation: tiny_obj_c.c:intern_rest_d
562
563
0
static bool TOBJ_T(tok_onoff)(const char *t, size_t l, bool *out) {
564
0
  if (tobj_slice_eq(t, l, "on")) {
565
0
    *out = true;
566
0
    return true;
567
0
  }
568
0
  if (tobj_slice_eq(t, l, "off")) {
569
0
    *out = false;
570
0
    return true;
571
0
  }
572
0
  return false;
573
0
}
Unexecuted instantiation: tiny_obj_c.c:tok_onoff_f
Unexecuted instantiation: tiny_obj_c.c:tok_onoff_d
574
575
/* Parse "[-options ...] filename" into texopt (+ interned name). Mirrors the
576
 * C++ ParseTextureNameAndOption. The cursor is consumed up to the filename. */
577
static const char *TOBJ_T(parse_texopt)(TOBJ_T(tobj_builder) * b, tobj_cursor *c,
578
0
                                        TOBJ_T(tobj_texture_option) * opt) {
579
0
  const char *tok;
580
0
  size_t tl;
581
0
  for (;;) {
582
0
    const char *save = c->p;
583
0
    if (!tobj_next_token(c, &tok, &tl)) {
584
0
      c->p = save;
585
0
      break;
586
0
    }
587
0
    if (tl == 0 || tok[0] != '-') {
588
0
      c->p = save; /* not an option: filename begins here */
589
0
      break;
590
0
    }
591
0
    if (tobj_slice_eq(tok, tl, "-blendu")) {
592
0
      const char *v;
593
0
      size_t vl;
594
0
      if (tobj_next_token(c, &v, &vl)) TOBJ_T(tok_onoff)(v, vl, &opt->blendu);
595
0
    } else if (tobj_slice_eq(tok, tl, "-blendv")) {
596
0
      const char *v;
597
0
      size_t vl;
598
0
      if (tobj_next_token(c, &v, &vl)) TOBJ_T(tok_onoff)(v, vl, &opt->blendv);
599
0
    } else if (tobj_slice_eq(tok, tl, "-clamp")) {
600
0
      const char *v;
601
0
      size_t vl;
602
0
      if (tobj_next_token(c, &v, &vl)) TOBJ_T(tok_onoff)(v, vl, &opt->clamp);
603
0
    } else if (tobj_slice_eq(tok, tl, "-bm")) {
604
0
      TOBJ_T(read_reals)(c, &opt->bump_multiplier, 1);
605
0
    } else if (tobj_slice_eq(tok, tl, "-boost")) {
606
0
      TOBJ_T(read_reals)(c, &opt->sharpness, 1);
607
0
    } else if (tobj_slice_eq(tok, tl, "-o")) {
608
0
      TOBJ_T(read_reals)(c, opt->origin_offset, 3);
609
0
    } else if (tobj_slice_eq(tok, tl, "-s")) {
610
0
      TOBJ_T(read_reals)(c, opt->scale, 3);
611
0
    } else if (tobj_slice_eq(tok, tl, "-t")) {
612
0
      TOBJ_T(read_reals)(c, opt->turbulence, 3);
613
0
    } else if (tobj_slice_eq(tok, tl, "-mm")) {
614
0
      TOBJ_T(read_reals)(c, &opt->brightness, 1);
615
0
      TOBJ_T(read_reals)(c, &opt->contrast, 1);
616
0
    } else if (tobj_slice_eq(tok, tl, "-texres")) {
617
0
      int v = -1;
618
0
      tobj_scan_int(&c->p, c->end, &v);
619
0
      opt->texture_resolution = v;
620
0
    } else if (tobj_slice_eq(tok, tl, "-imfchan")) {
621
0
      const char *v;
622
0
      size_t vl;
623
0
      if (tobj_next_token(c, &v, &vl) && vl >= 1) opt->imfchan = v[0];
624
0
    } else if (tobj_slice_eq(tok, tl, "-type")) {
625
0
      const char *v;
626
0
      size_t vl;
627
0
      if (tobj_next_token(c, &v, &vl)) {
628
0
        if (tobj_slice_eq(v, vl, "sphere"))
629
0
          opt->type = TOBJ_TEXTURE_TYPE_SPHERE;
630
0
        else if (tobj_slice_eq(v, vl, "cube_top"))
631
0
          opt->type = TOBJ_TEXTURE_TYPE_CUBE_TOP;
632
0
        else if (tobj_slice_eq(v, vl, "cube_bottom"))
633
0
          opt->type = TOBJ_TEXTURE_TYPE_CUBE_BOTTOM;
634
0
        else if (tobj_slice_eq(v, vl, "cube_front"))
635
0
          opt->type = TOBJ_TEXTURE_TYPE_CUBE_FRONT;
636
0
        else if (tobj_slice_eq(v, vl, "cube_back"))
637
0
          opt->type = TOBJ_TEXTURE_TYPE_CUBE_BACK;
638
0
        else if (tobj_slice_eq(v, vl, "cube_left"))
639
0
          opt->type = TOBJ_TEXTURE_TYPE_CUBE_LEFT;
640
0
        else if (tobj_slice_eq(v, vl, "cube_right"))
641
0
          opt->type = TOBJ_TEXTURE_TYPE_CUBE_RIGHT;
642
0
      }
643
0
    } else if (tobj_slice_eq(tok, tl, "-colorspace")) {
644
0
      const char *v;
645
0
      size_t vl;
646
0
      if (tobj_next_token(c, &v, &vl))
647
0
        opt->colorspace = tobj_intern(b->arena, v, vl);
648
0
    } else {
649
      /* unknown option flag: skip just the flag token */
650
0
    }
651
0
  }
652
0
  return TOBJ_T(intern_rest)(b, c);
653
0
}
Unexecuted instantiation: tiny_obj_c.c:parse_texopt_f
Unexecuted instantiation: tiny_obj_c.c:parse_texopt_d
654
655
/* Record an unrecognized "key value" .mtl line for material `mi`. */
656
static bool TOBJ_T(push_unknown)(TOBJ_T(tobj_builder) * b, size_t mi,
657
                                 const char *key, size_t keylen,
658
0
                                 tobj_cursor *c) {
659
0
  tobj_pending_kv *kv = (tobj_pending_kv *)tobj_vec_push(&b->mat_unknown, b->alloc);
660
0
  if (!kv) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
661
0
  kv->mat_index = mi;
662
0
  kv->key = tobj_intern(b->arena, key, keylen);
663
0
  kv->value = TOBJ_T(intern_rest)(b, c);
664
0
  if (!kv->key || !kv->value) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
665
0
  return true;
666
0
}
Unexecuted instantiation: tiny_obj_c.c:push_unknown_f
Unexecuted instantiation: tiny_obj_c.c:push_unknown_d
667
668
/* If map_Kd was given but Kd was not, default diffuse to 0.6 (C++ parity). */
669
0
static void TOBJ_T(apply_kd_default)(TOBJ_T(tobj_material) * m, bool kd_seen) {
670
0
  if (!kd_seen && m->diffuse_texname && m->diffuse_texname[0] != '\0') {
671
0
    m->diffuse[0] = m->diffuse[1] = m->diffuse[2] = (TOBJ_REAL)0.6;
672
0
  }
673
0
}
Unexecuted instantiation: tiny_obj_c.c:apply_kd_default_f
Unexecuted instantiation: tiny_obj_c.c:apply_kd_default_d
674
675
/* Full .mtl parser. */
676
static bool TOBJ_T(parse_mtl_buffer)(TOBJ_T(tobj_builder) * b,
677
0
                                     const uint8_t *data, size_t len) {
678
0
  tobj_vec lines;
679
0
  tobj_vec_init(&lines, sizeof(tobj_line));
680
0
  if (!tobj_build_line_index(data, len, &lines, b->alloc)) {
681
0
    tobj_vec_free(&lines, b->alloc);
682
0
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
683
0
  }
684
0
  const char *base = (const char *)data;
685
0
  int cur = -1;
686
0
  bool kd_seen = false;
687
0
  for (size_t i = 0; i < lines.count && b->err == TOBJ_OK; i++) {
688
0
    tobj_line *ln = (tobj_line *)tobj_vec_at(&lines, i);
689
0
    if (ln->len > b->caps.line_bytes) continue;
690
0
    tobj_cursor c;
691
0
    tobj_cur_init(&c, base + ln->pos, ln->len);
692
0
    const char *tok;
693
0
    size_t tl;
694
0
    if (!tobj_next_token(&c, &tok, &tl)) continue;
695
0
    if (tl && tok[0] == '#') continue;
696
697
0
    if (tobj_slice_eq(tok, tl, "newmtl")) {
698
0
      if (cur >= 0)
699
0
        TOBJ_T(apply_kd_default)(
700
0
            (TOBJ_T(tobj_material) *)tobj_vec_at(&b->materials, (size_t)cur),
701
0
            kd_seen);
702
0
      if (b->materials.count >= b->caps.materials) {
703
0
        TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
704
0
        break;
705
0
      }
706
0
      TOBJ_T(tobj_material) *m =
707
0
          (TOBJ_T(tobj_material) *)tobj_vec_push(&b->materials, b->alloc);
708
0
      if (!m) {
709
0
        TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
710
0
        break;
711
0
      }
712
0
      TOBJ_T(material_init)(m, b->arena);
713
0
      m->name = TOBJ_T(intern_rest)(b, &c);
714
0
      cur = (int)(b->materials.count - 1);
715
0
      kd_seen = false;
716
0
      const char **k = (const char **)tobj_vec_push(&b->matmap_key, b->alloc);
717
0
      int *id = (int *)tobj_vec_push(&b->matmap_id, b->alloc);
718
0
      if (!k || !id) {
719
0
        TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
720
0
        break;
721
0
      }
722
0
      *k = m->name;
723
0
      *id = cur;
724
0
      continue;
725
0
    }
726
0
    if (cur < 0) continue;
727
0
    TOBJ_T(tobj_material) *m =
728
0
        (TOBJ_T(tobj_material) *)tobj_vec_at(&b->materials, (size_t)cur);
729
730
0
    if (tobj_slice_eq(tok, tl, "Ka")) {
731
0
      TOBJ_T(read_reals)(&c, m->ambient, 3);
732
0
    } else if (tobj_slice_eq(tok, tl, "Kd")) {
733
0
      TOBJ_T(read_reals)(&c, m->diffuse, 3);
734
0
      kd_seen = true;
735
0
    } else if (tobj_slice_eq(tok, tl, "Ks")) {
736
0
      TOBJ_T(read_reals)(&c, m->specular, 3);
737
0
    } else if (tobj_slice_eq(tok, tl, "Ke")) {
738
0
      TOBJ_T(read_reals)(&c, m->emission, 3);
739
0
    } else if (tobj_slice_eq(tok, tl, "Kt") || tobj_slice_eq(tok, tl, "Tf")) {
740
0
      TOBJ_T(read_reals)(&c, m->transmittance, 3);
741
0
    } else if (tobj_slice_eq(tok, tl, "Ns")) {
742
0
      TOBJ_T(read_reals)(&c, &m->shininess, 1);
743
0
    } else if (tobj_slice_eq(tok, tl, "Ni")) {
744
0
      TOBJ_T(read_reals)(&c, &m->ior, 1);
745
0
    } else if (tobj_slice_eq(tok, tl, "d")) {
746
0
      TOBJ_T(read_reals)(&c, &m->dissolve, 1);
747
0
    } else if (tobj_slice_eq(tok, tl, "Tr")) {
748
0
      TOBJ_REAL tr = 0;
749
0
      TOBJ_T(read_reals)(&c, &tr, 1);
750
0
      m->dissolve = (TOBJ_REAL)1 - tr;
751
0
    } else if (tobj_slice_eq(tok, tl, "illum")) {
752
0
      int v = 0;
753
0
      tobj_scan_int(&c.p, c.end, &v);
754
0
      m->illum = v;
755
0
    } else if (tobj_slice_eq(tok, tl, "Pr")) {
756
0
      TOBJ_T(read_reals)(&c, &m->roughness, 1);
757
0
    } else if (tobj_slice_eq(tok, tl, "Pm")) {
758
0
      TOBJ_T(read_reals)(&c, &m->metallic, 1);
759
0
    } else if (tobj_slice_eq(tok, tl, "Ps")) {
760
0
      TOBJ_T(read_reals)(&c, &m->sheen, 1);
761
0
    } else if (tobj_slice_eq(tok, tl, "Pc")) {
762
0
      TOBJ_T(read_reals)(&c, &m->clearcoat_thickness, 1);
763
0
    } else if (tobj_slice_eq(tok, tl, "Pcr")) {
764
0
      TOBJ_T(read_reals)(&c, &m->clearcoat_roughness, 1);
765
0
    } else if (tobj_slice_eq(tok, tl, "aniso")) {
766
0
      TOBJ_T(read_reals)(&c, &m->anisotropy, 1);
767
0
    } else if (tobj_slice_eq(tok, tl, "anisor")) {
768
0
      TOBJ_T(read_reals)(&c, &m->anisotropy_rotation, 1);
769
0
    } else if (tobj_slice_eq(tok, tl, "map_Ka")) {
770
0
      m->ambient_texname = TOBJ_T(parse_texopt)(b, &c, &m->ambient_texopt);
771
0
    } else if (tobj_slice_eq(tok, tl, "map_Kd")) {
772
0
      m->diffuse_texname = TOBJ_T(parse_texopt)(b, &c, &m->diffuse_texopt);
773
0
    } else if (tobj_slice_eq(tok, tl, "map_Ks")) {
774
0
      m->specular_texname = TOBJ_T(parse_texopt)(b, &c, &m->specular_texopt);
775
0
    } else if (tobj_slice_eq(tok, tl, "map_Ns")) {
776
0
      m->specular_highlight_texname =
777
0
          TOBJ_T(parse_texopt)(b, &c, &m->specular_highlight_texopt);
778
0
    } else if (tobj_slice_eq(tok, tl, "map_d")) {
779
0
      m->alpha_texname = TOBJ_T(parse_texopt)(b, &c, &m->alpha_texopt);
780
0
    } else if (tobj_slice_eq(tok, tl, "map_bump") ||
781
0
               tobj_slice_eq(tok, tl, "map_Bump") ||
782
0
               tobj_slice_eq(tok, tl, "bump")) {
783
0
      m->bump_texopt.imfchan = 'l';
784
0
      m->bump_texname = TOBJ_T(parse_texopt)(b, &c, &m->bump_texopt);
785
0
    } else if (tobj_slice_eq(tok, tl, "disp")) {
786
0
      m->displacement_texname =
787
0
          TOBJ_T(parse_texopt)(b, &c, &m->displacement_texopt);
788
0
    } else if (tobj_slice_eq(tok, tl, "refl")) {
789
0
      m->reflection_texname = TOBJ_T(parse_texopt)(b, &c, &m->reflection_texopt);
790
0
    } else if (tobj_slice_eq(tok, tl, "norm")) {
791
0
      m->normal_texname = TOBJ_T(parse_texopt)(b, &c, &m->normal_texopt);
792
0
    } else if (tobj_slice_eq(tok, tl, "map_Pr")) {
793
0
      m->roughness_texname = TOBJ_T(parse_texopt)(b, &c, &m->roughness_texopt);
794
0
    } else if (tobj_slice_eq(tok, tl, "map_Pm")) {
795
0
      m->metallic_texname = TOBJ_T(parse_texopt)(b, &c, &m->metallic_texopt);
796
0
    } else if (tobj_slice_eq(tok, tl, "map_Ps")) {
797
0
      m->sheen_texname = TOBJ_T(parse_texopt)(b, &c, &m->sheen_texopt);
798
0
    } else if (tobj_slice_eq(tok, tl, "map_Ke")) {
799
0
      m->emissive_texname = TOBJ_T(parse_texopt)(b, &c, &m->emissive_texopt);
800
0
    } else {
801
      /* unknown parameter: store key -> rest of line */
802
0
      if (!TOBJ_T(push_unknown)(b, (size_t)cur, tok, tl, &c)) break;
803
0
    }
804
0
  }
805
806
0
  if (cur >= 0)
807
0
    TOBJ_T(apply_kd_default)(
808
0
        (TOBJ_T(tobj_material) *)tobj_vec_at(&b->materials, (size_t)cur),
809
0
        kd_seen);
810
0
  tobj_vec_free(&lines, b->alloc);
811
0
  return b->err == TOBJ_OK;
812
0
}
Unexecuted instantiation: tiny_obj_c.c:parse_mtl_buffer_f
Unexecuted instantiation: tiny_obj_c.c:parse_mtl_buffer_d
813
814
259
static bool TOBJ_T(handle_mtllib)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
815
259
  if (!b->cfg->mtl_resolver) {
816
259
    TOBJ_T(b_warn)(b, "tinyobjc: mtllib present but no material resolver\n");
817
259
    return true;
818
259
  }
819
0
  const char *tok;
820
0
  size_t tl;
821
0
  while (tobj_next_token(c, &tok, &tl)) {
822
0
    char *name = (char *)tobj_arena_alloc(b->arena, tl + 1, 1);
823
0
    if (!name) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
824
0
    tobj_memcpy(name, tok, tl);
825
0
    name[tl] = '\0';
826
0
    tobj_mtl_source src;
827
0
    tobj_memset(&src, 0, sizeof src);
828
0
    tobj_result r =
829
0
        b->cfg->mtl_resolver(b->cfg->mtl_resolver_user_data, name, &src);
830
0
    if (r != TOBJ_OK) {
831
0
      TOBJ_T(b_warn)(b, "tinyobjc: failed to resolve mtllib\n");
832
0
      continue;
833
0
    }
834
0
    TOBJ_T(parse_mtl_buffer)(b, src.data, src.len);
835
0
    if (src.release) src.release(src.release_ud, src.data, src.len);
836
0
    if (b->err != TOBJ_OK) return false;
837
0
  }
838
0
  return true;
839
0
}
tiny_obj_c.c:handle_mtllib_f
Line
Count
Source
814
259
static bool TOBJ_T(handle_mtllib)(TOBJ_T(tobj_builder) * b, tobj_cursor *c) {
815
259
  if (!b->cfg->mtl_resolver) {
816
259
    TOBJ_T(b_warn)(b, "tinyobjc: mtllib present but no material resolver\n");
817
259
    return true;
818
259
  }
819
0
  const char *tok;
820
0
  size_t tl;
821
0
  while (tobj_next_token(c, &tok, &tl)) {
822
0
    char *name = (char *)tobj_arena_alloc(b->arena, tl + 1, 1);
823
0
    if (!name) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
824
0
    tobj_memcpy(name, tok, tl);
825
0
    name[tl] = '\0';
826
0
    tobj_mtl_source src;
827
0
    tobj_memset(&src, 0, sizeof src);
828
0
    tobj_result r =
829
0
        b->cfg->mtl_resolver(b->cfg->mtl_resolver_user_data, name, &src);
830
0
    if (r != TOBJ_OK) {
831
0
      TOBJ_T(b_warn)(b, "tinyobjc: failed to resolve mtllib\n");
832
0
      continue;
833
0
    }
834
0
    TOBJ_T(parse_mtl_buffer)(b, src.data, src.len);
835
0
    if (src.release) src.release(src.release_ud, src.data, src.len);
836
0
    if (b->err != TOBJ_OK) return false;
837
0
  }
838
0
  return true;
839
0
}
Unexecuted instantiation: tiny_obj_c.c:handle_mtllib_d
840
841
/* ----------------------------------------------------------------------- */
842
/* free-form geometry (parse & retain; no NURBS evaluation)                */
843
/* ----------------------------------------------------------------------- */
844
845
0
static void TOBJ_T(ff_reset_elem)(TOBJ_T(tobj_builder) * b) {
846
0
  b->ff_active = false;
847
0
  b->ff_kind = 0;
848
0
  b->ff_cp_int.count = 0;
849
0
  b->ff_cp_idx.count = 0;
850
0
  b->ff_knu.count = 0;
851
0
  b->ff_knv.count = 0;
852
0
  b->ff_trim.count = 0;
853
0
  b->ff_hole.count = 0;
854
0
  b->ff_scrv.count = 0;
855
0
}
Unexecuted instantiation: tiny_obj_c.c:ff_reset_elem_f
Unexecuted instantiation: tiny_obj_c.c:ff_reset_elem_d
856
857
0
static bool TOBJ_T(ff_finalize_elem)(TOBJ_T(tobj_builder) * b) {
858
0
  if (!b->ff_active) return true;
859
0
  if (b->ff_kind == 1) { /* curv */
860
0
    TOBJ_T(tobj_curve) *cv =
861
0
        (TOBJ_T(tobj_curve) *)tobj_vec_push(&b->sh_curves, b->alloc);
862
0
    if (!cv) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
863
0
    cv->type = b->ff_cstype;
864
0
    cv->rational = b->ff_rational;
865
0
    cv->degree = b->ff_degu;
866
0
    cv->u0 = b->ff_u0;
867
0
    cv->u1 = b->ff_u1;
868
0
    cv->num_control_points = b->ff_cp_int.count;
869
0
    cv->control_points = (int *)tobj_vec_finalize(&b->ff_cp_int, b->arena, 16);
870
0
    cv->num_knots = b->ff_knu.count;
871
0
    cv->knots = (TOBJ_REAL *)tobj_vec_finalize(&b->ff_knu, b->arena, 16);
872
0
    if ((cv->num_control_points && !cv->control_points) ||
873
0
        (cv->num_knots && !cv->knots))
874
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
875
0
  } else if (b->ff_kind == 2) { /* curv2 */
876
0
    TOBJ_T(tobj_curve2) *cv =
877
0
        (TOBJ_T(tobj_curve2) *)tobj_vec_push(&b->sh_curves2, b->alloc);
878
0
    if (!cv) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
879
0
    cv->type = b->ff_cstype;
880
0
    cv->rational = b->ff_rational;
881
0
    cv->degree = b->ff_degu;
882
0
    cv->num_control_points = b->ff_cp_int.count;
883
0
    cv->control_points = (int *)tobj_vec_finalize(&b->ff_cp_int, b->arena, 16);
884
0
    cv->num_knots = b->ff_knu.count;
885
0
    cv->knots = (TOBJ_REAL *)tobj_vec_finalize(&b->ff_knu, b->arena, 16);
886
0
    if ((cv->num_control_points && !cv->control_points) ||
887
0
        (cv->num_knots && !cv->knots))
888
0
      return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
889
0
  } else if (b->ff_kind == 3) { /* surf */
890
0
    TOBJ_T(tobj_surface) *sf =
891
0
        (TOBJ_T(tobj_surface) *)tobj_vec_push(&b->sh_surfaces, b->alloc);
892
0
    if (!sf) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
893
0
    sf->type = b->ff_cstype;
894
0
    sf->rational = b->ff_rational;
895
0
    sf->degu = b->ff_degu;
896
0
    sf->degv = b->ff_degv;
897
0
    sf->s0 = b->ff_s0;
898
0
    sf->s1 = b->ff_s1;
899
0
    sf->t0 = b->ff_t0;
900
0
    sf->t1 = b->ff_t1;
901
0
    sf->num_control_points = b->ff_cp_idx.count;
902
0
    sf->control_points =
903
0
        (tobj_index *)tobj_vec_finalize(&b->ff_cp_idx, b->arena, 16);
904
0
    sf->num_knots_u = b->ff_knu.count;
905
0
    sf->knots_u = (TOBJ_REAL *)tobj_vec_finalize(&b->ff_knu, b->arena, 16);
906
0
    sf->num_knots_v = b->ff_knv.count;
907
0
    sf->knots_v = (TOBJ_REAL *)tobj_vec_finalize(&b->ff_knv, b->arena, 16);
908
0
    sf->num_trims = b->ff_trim.count;
909
0
    sf->trims = (TOBJ_T(tobj_trim_loop) *)tobj_vec_finalize(&b->ff_trim,
910
0
                                                            b->arena, 16);
911
0
    sf->num_holes = b->ff_hole.count;
912
0
    sf->holes = (TOBJ_T(tobj_trim_loop) *)tobj_vec_finalize(&b->ff_hole,
913
0
                                                            b->arena, 16);
914
0
    sf->num_scrvs = b->ff_scrv.count;
915
0
    sf->scrvs = (TOBJ_T(tobj_trim_loop) *)tobj_vec_finalize(&b->ff_scrv,
916
0
                                                            b->arena, 16);
917
0
  }
918
0
  b->ff_present = true;
919
0
  TOBJ_T(ff_reset_elem)(b);
920
0
  return true;
921
0
}
Unexecuted instantiation: tiny_obj_c.c:ff_finalize_elem_f
Unexecuted instantiation: tiny_obj_c.c:ff_finalize_elem_d
922
923
static void TOBJ_T(ff_loops)(TOBJ_T(tobj_builder) * b, tobj_cursor *c,
924
0
                             tobj_vec *dst) {
925
0
  double u0, u1;
926
0
  int idx;
927
0
  while (tobj_scan_double(&c->p, c->end, &u0)) {
928
0
    if (!tobj_scan_double(&c->p, c->end, &u1)) break;
929
0
    if (!tobj_scan_int(&c->p, c->end, &idx)) break;
930
0
    TOBJ_T(tobj_trim_loop) *t =
931
0
        (TOBJ_T(tobj_trim_loop) *)tobj_vec_push(dst, b->alloc);
932
0
    if (!t) {
933
0
      TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
934
0
      return;
935
0
    }
936
0
    t->u0 = (TOBJ_REAL)u0;
937
0
    t->u1 = (TOBJ_REAL)u1;
938
0
    t->curve2_index = idx;
939
0
  }
940
0
}
Unexecuted instantiation: tiny_obj_c.c:ff_loops_f
Unexecuted instantiation: tiny_obj_c.c:ff_loops_d
941
942
static bool TOBJ_T(handle_freeform)(TOBJ_T(tobj_builder) * b, const char *tok,
943
0
                                    size_t tl, tobj_cursor *c) {
944
0
  if (tobj_slice_eq(tok, tl, "vp")) {
945
0
    double d;
946
0
    int n = 0;
947
0
    TOBJ_REAL uvw[3] = {0, 0, 0};
948
0
    for (; n < 3; n++) {
949
0
      if (!tobj_scan_double(&c->p, c->end, &d)) break;
950
0
      uvw[n] = (TOBJ_REAL)d;
951
0
    }
952
0
    if (n < 1) return true;
953
0
    if (n > b->ff_vp_comps) b->ff_vp_comps = n;
954
0
    return tobj_vec_append(&b->ff_vp, uvw, (size_t)n, b->alloc)
955
0
               ? true
956
0
               : TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
957
0
  }
958
0
  if (tobj_slice_eq(tok, tl, "cstype")) {
959
0
    const char *t2;
960
0
    size_t l2;
961
0
    b->ff_rational = false;
962
0
    b->ff_cstype = TOBJ_CSTYPE_NONE;
963
0
    while (tobj_next_token(c, &t2, &l2)) {
964
0
      if (tobj_slice_eq(t2, l2, "rat"))
965
0
        b->ff_rational = true;
966
0
      else if (tobj_slice_eq(t2, l2, "bmatrix"))
967
0
        b->ff_cstype = TOBJ_CSTYPE_BMATRIX;
968
0
      else if (tobj_slice_eq(t2, l2, "bezier"))
969
0
        b->ff_cstype = TOBJ_CSTYPE_BEZIER;
970
0
      else if (tobj_slice_eq(t2, l2, "bspline"))
971
0
        b->ff_cstype = TOBJ_CSTYPE_BSPLINE;
972
0
      else if (tobj_slice_eq(t2, l2, "cardinal"))
973
0
        b->ff_cstype = TOBJ_CSTYPE_CARDINAL;
974
0
      else if (tobj_slice_eq(t2, l2, "taylor"))
975
0
        b->ff_cstype = TOBJ_CSTYPE_TAYLOR;
976
0
    }
977
0
    return true;
978
0
  }
979
0
  if (tobj_slice_eq(tok, tl, "deg")) {
980
0
    int du = 0, dv = 0;
981
0
    if (tobj_scan_int(&c->p, c->end, &du)) b->ff_degu = du;
982
0
    if (tobj_scan_int(&c->p, c->end, &dv)) b->ff_degv = dv;
983
0
    return true;
984
0
  }
985
0
  if (tobj_slice_eq(tok, tl, "curv")) {
986
0
    TOBJ_T(ff_reset_elem)(b);
987
0
    b->ff_active = true;
988
0
    b->ff_kind = 1;
989
0
    double a, bb;
990
0
    b->ff_u0 = tobj_scan_double(&c->p, c->end, &a) ? (TOBJ_REAL)a : (TOBJ_REAL)0;
991
0
    b->ff_u1 = tobj_scan_double(&c->p, c->end, &bb) ? (TOBJ_REAL)bb : (TOBJ_REAL)0;
992
0
    int vi;
993
0
    size_t numv = TOBJ_T(cnt_v)(b);
994
0
    while (tobj_scan_int(&c->p, c->end, &vi)) {
995
0
      int r;
996
0
      TOBJ_T(fix_index)(vi, numv, &r);
997
0
      if (!tobj_vec_append(&b->ff_cp_int, &r, 1, b->alloc))
998
0
        return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
999
0
    }
1000
0
    return true;
1001
0
  }
1002
0
  if (tobj_slice_eq(tok, tl, "curv2")) {
1003
0
    TOBJ_T(ff_reset_elem)(b);
1004
0
    b->ff_active = true;
1005
0
    b->ff_kind = 2;
1006
0
    int vi;
1007
0
    size_t numvp = b->prefilled
1008
0
                       ? b->run_numvp
1009
0
                       : (b->ff_vp_comps ? b->ff_vp.count / (size_t)b->ff_vp_comps
1010
0
                                         : 0);
1011
0
    while (tobj_scan_int(&c->p, c->end, &vi)) {
1012
0
      int r;
1013
0
      TOBJ_T(fix_index)(vi, numvp, &r);
1014
0
      if (!tobj_vec_append(&b->ff_cp_int, &r, 1, b->alloc))
1015
0
        return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1016
0
    }
1017
0
    return true;
1018
0
  }
1019
0
  if (tobj_slice_eq(tok, tl, "surf")) {
1020
0
    TOBJ_T(ff_reset_elem)(b);
1021
0
    b->ff_active = true;
1022
0
    b->ff_kind = 3;
1023
0
    double s0, s1, t0, t1;
1024
0
    b->ff_s0 = tobj_scan_double(&c->p, c->end, &s0) ? (TOBJ_REAL)s0 : 0;
1025
0
    b->ff_s1 = tobj_scan_double(&c->p, c->end, &s1) ? (TOBJ_REAL)s1 : 0;
1026
0
    b->ff_t0 = tobj_scan_double(&c->p, c->end, &t0) ? (TOBJ_REAL)t0 : 0;
1027
0
    b->ff_t1 = tobj_scan_double(&c->p, c->end, &t1) ? (TOBJ_REAL)t1 : 0;
1028
0
    const char *t2;
1029
0
    size_t l2;
1030
0
    while (tobj_next_token(c, &t2, &l2)) {
1031
0
      tobj_index idx;
1032
0
      TOBJ_T(parse_corner_n)(t2, l2, TOBJ_T(cnt_v)(b), TOBJ_T(cnt_vt)(b),
1033
0
                             TOBJ_T(cnt_vn)(b), &idx);
1034
0
      if (!tobj_vec_append(&b->ff_cp_idx, &idx, 1, b->alloc))
1035
0
        return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1036
0
    }
1037
0
    return true;
1038
0
  }
1039
0
  if (tobj_slice_eq(tok, tl, "parm")) {
1040
0
    const char *t2;
1041
0
    size_t l2;
1042
0
    if (!tobj_next_token(c, &t2, &l2)) return true;
1043
0
    tobj_vec *dst = tobj_slice_eq(t2, l2, "v") ? &b->ff_knv : &b->ff_knu;
1044
0
    double d;
1045
0
    while (tobj_scan_double(&c->p, c->end, &d)) {
1046
0
      TOBJ_REAL r = (TOBJ_REAL)d;
1047
0
      if (!tobj_vec_append(dst, &r, 1, b->alloc))
1048
0
        return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1049
0
    }
1050
0
    return true;
1051
0
  }
1052
0
  if (tobj_slice_eq(tok, tl, "trim")) {
1053
0
    TOBJ_T(ff_loops)(b, c, &b->ff_trim);
1054
0
    return b->err == TOBJ_OK;
1055
0
  }
1056
0
  if (tobj_slice_eq(tok, tl, "hole")) {
1057
0
    TOBJ_T(ff_loops)(b, c, &b->ff_hole);
1058
0
    return b->err == TOBJ_OK;
1059
0
  }
1060
0
  if (tobj_slice_eq(tok, tl, "scrv")) {
1061
0
    TOBJ_T(ff_loops)(b, c, &b->ff_scrv);
1062
0
    return b->err == TOBJ_OK;
1063
0
  }
1064
0
  if (tobj_slice_eq(tok, tl, "end")) {
1065
0
    return TOBJ_T(ff_finalize_elem)(b);
1066
0
  }
1067
  /* sp / con / bmat / step etc.: recognized but not retained */
1068
0
  return true;
1069
0
}
Unexecuted instantiation: tiny_obj_c.c:handle_freeform_f
Unexecuted instantiation: tiny_obj_c.c:handle_freeform_d
1070
1071
/* ----------------------------------------------------------------------- */
1072
/* line dispatch                                                           */
1073
/* ----------------------------------------------------------------------- */
1074
1075
static bool TOBJ_T(dispatch_line)(TOBJ_T(tobj_builder) * b, const char *p,
1076
15.9M
                                  size_t len) {
1077
15.9M
  if (len > b->caps.line_bytes) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
1078
15.9M
  tobj_cursor c;
1079
15.9M
  tobj_cur_init(&c, p, len);
1080
15.9M
  const char *tok;
1081
15.9M
  size_t tl;
1082
15.9M
  if (!tobj_next_token(&c, &tok, &tl)) return true;
1083
14.7M
  if (tok[0] == '#') return true;
1084
1085
  /* In MT pass 2 the vertex data is already parsed; just advance the running
1086
   * counts used for relative-index resolution. */
1087
14.7M
  if (b->prefilled) {
1088
0
    if (tobj_slice_eq(tok, tl, "v")) {
1089
0
      b->run_numv++;
1090
0
      return true;
1091
0
    }
1092
0
    if (tobj_slice_eq(tok, tl, "vn")) {
1093
0
      b->run_numvn++;
1094
0
      return true;
1095
0
    }
1096
0
    if (tobj_slice_eq(tok, tl, "vt")) {
1097
0
      b->run_numvt++;
1098
0
      return true;
1099
0
    }
1100
0
    if (tobj_slice_eq(tok, tl, "vp")) {
1101
0
      b->run_numvp++;
1102
0
      return true;
1103
0
    }
1104
0
  }
1105
1106
14.7M
  if (tobj_slice_eq(tok, tl, "v")) return TOBJ_T(handle_v)(b, &c);
1107
5.78M
  if (tobj_slice_eq(tok, tl, "vn")) return TOBJ_T(handle_vn)(b, &c);
1108
4.05M
  if (tobj_slice_eq(tok, tl, "vt")) return TOBJ_T(handle_vt)(b, &c);
1109
2.00M
  if (tobj_slice_eq(tok, tl, "f")) return TOBJ_T(handle_f)(b, &c);
1110
1.93M
  if (tobj_slice_eq(tok, tl, "l")) return TOBJ_T(handle_l)(b, &c);
1111
1.44M
  if (tobj_slice_eq(tok, tl, "p")) return TOBJ_T(handle_p)(b, &c);
1112
1.10M
  if (tobj_slice_eq(tok, tl, "g") || tobj_slice_eq(tok, tl, "o")) {
1113
379k
    const char *name = TOBJ_T(intern_rest)(b, &c);
1114
379k
    if (!name) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1115
379k
    return TOBJ_T(new_group)(b, name);
1116
379k
  }
1117
729k
  if (tobj_slice_eq(tok, tl, "s")) {
1118
18.7k
    const char *st;
1119
18.7k
    size_t sl;
1120
18.7k
    if (tobj_next_token(&c, &st, &sl)) {
1121
16.2k
      if (tobj_slice_eq(st, sl, "off")) {
1122
4.33k
        b->cur_smooth = 0;
1123
11.8k
      } else {
1124
11.8k
        const char *q = st;
1125
11.8k
        int v = 0;
1126
11.8k
        tobj_scan_int(&q, st + sl, &v);
1127
11.8k
        b->cur_smooth = v > 0 ? (unsigned)v : 0u;
1128
11.8k
      }
1129
16.2k
    }
1130
18.7k
    return true;
1131
18.7k
  }
1132
711k
  if (tobj_slice_eq(tok, tl, "usemtl")) {
1133
581
    const char *name = c.p;
1134
1.81k
    while (name < c.end && tobj_is_space(*name)) name++;
1135
581
    const char *e = c.end;
1136
1.11k
    while (e > name && tobj_is_space(e[-1])) e--;
1137
581
    int id = TOBJ_T(mat_find)(b, name, (size_t)(e - name));
1138
581
    if (id < 0) TOBJ_T(b_warn)(b, "tinyobjc: usemtl references unknown material\n");
1139
581
    b->cur_material = id;
1140
581
    return true;
1141
581
  }
1142
710k
  if (tobj_slice_eq(tok, tl, "mtllib")) return TOBJ_T(handle_mtllib)(b, &c);
1143
710k
  if (b->cfg->parse_freeform) return TOBJ_T(handle_freeform)(b, tok, tl, &c);
1144
  /* unrecognized directive: silently skipped */
1145
710k
  return true;
1146
710k
}
tiny_obj_c.c:dispatch_line_f
Line
Count
Source
1076
15.9M
                                  size_t len) {
1077
15.9M
  if (len > b->caps.line_bytes) return TOBJ_T(b_fail)(b, TOBJ_ERR_LIMIT_EXCEEDED);
1078
15.9M
  tobj_cursor c;
1079
15.9M
  tobj_cur_init(&c, p, len);
1080
15.9M
  const char *tok;
1081
15.9M
  size_t tl;
1082
15.9M
  if (!tobj_next_token(&c, &tok, &tl)) return true;
1083
14.7M
  if (tok[0] == '#') return true;
1084
1085
  /* In MT pass 2 the vertex data is already parsed; just advance the running
1086
   * counts used for relative-index resolution. */
1087
14.7M
  if (b->prefilled) {
1088
0
    if (tobj_slice_eq(tok, tl, "v")) {
1089
0
      b->run_numv++;
1090
0
      return true;
1091
0
    }
1092
0
    if (tobj_slice_eq(tok, tl, "vn")) {
1093
0
      b->run_numvn++;
1094
0
      return true;
1095
0
    }
1096
0
    if (tobj_slice_eq(tok, tl, "vt")) {
1097
0
      b->run_numvt++;
1098
0
      return true;
1099
0
    }
1100
0
    if (tobj_slice_eq(tok, tl, "vp")) {
1101
0
      b->run_numvp++;
1102
0
      return true;
1103
0
    }
1104
0
  }
1105
1106
14.7M
  if (tobj_slice_eq(tok, tl, "v")) return TOBJ_T(handle_v)(b, &c);
1107
5.78M
  if (tobj_slice_eq(tok, tl, "vn")) return TOBJ_T(handle_vn)(b, &c);
1108
4.05M
  if (tobj_slice_eq(tok, tl, "vt")) return TOBJ_T(handle_vt)(b, &c);
1109
2.00M
  if (tobj_slice_eq(tok, tl, "f")) return TOBJ_T(handle_f)(b, &c);
1110
1.93M
  if (tobj_slice_eq(tok, tl, "l")) return TOBJ_T(handle_l)(b, &c);
1111
1.44M
  if (tobj_slice_eq(tok, tl, "p")) return TOBJ_T(handle_p)(b, &c);
1112
1.10M
  if (tobj_slice_eq(tok, tl, "g") || tobj_slice_eq(tok, tl, "o")) {
1113
379k
    const char *name = TOBJ_T(intern_rest)(b, &c);
1114
379k
    if (!name) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1115
379k
    return TOBJ_T(new_group)(b, name);
1116
379k
  }
1117
729k
  if (tobj_slice_eq(tok, tl, "s")) {
1118
18.7k
    const char *st;
1119
18.7k
    size_t sl;
1120
18.7k
    if (tobj_next_token(&c, &st, &sl)) {
1121
16.2k
      if (tobj_slice_eq(st, sl, "off")) {
1122
4.33k
        b->cur_smooth = 0;
1123
11.8k
      } else {
1124
11.8k
        const char *q = st;
1125
11.8k
        int v = 0;
1126
11.8k
        tobj_scan_int(&q, st + sl, &v);
1127
11.8k
        b->cur_smooth = v > 0 ? (unsigned)v : 0u;
1128
11.8k
      }
1129
16.2k
    }
1130
18.7k
    return true;
1131
18.7k
  }
1132
711k
  if (tobj_slice_eq(tok, tl, "usemtl")) {
1133
581
    const char *name = c.p;
1134
1.81k
    while (name < c.end && tobj_is_space(*name)) name++;
1135
581
    const char *e = c.end;
1136
1.11k
    while (e > name && tobj_is_space(e[-1])) e--;
1137
581
    int id = TOBJ_T(mat_find)(b, name, (size_t)(e - name));
1138
581
    if (id < 0) TOBJ_T(b_warn)(b, "tinyobjc: usemtl references unknown material\n");
1139
581
    b->cur_material = id;
1140
581
    return true;
1141
581
  }
1142
710k
  if (tobj_slice_eq(tok, tl, "mtllib")) return TOBJ_T(handle_mtllib)(b, &c);
1143
710k
  if (b->cfg->parse_freeform) return TOBJ_T(handle_freeform)(b, tok, tl, &c);
1144
  /* unrecognized directive: silently skipped */
1145
710k
  return true;
1146
710k
}
Unexecuted instantiation: tiny_obj_c.c:dispatch_line_d
1147
1148
/* ----------------------------------------------------------------------- */
1149
/* public entry points                                                     */
1150
/* ----------------------------------------------------------------------- */
1151
1152
/* Group pending unknown params by material, sort by key, attach to materials. */
1153
3.65k
static bool TOBJ_T(finalize_unknowns)(TOBJ_T(tobj_builder) * b) {
1154
3.65k
  if (b->mat_unknown.count == 0) return true;
1155
0
  const tobj_pending_kv *pend = (const tobj_pending_kv *)b->mat_unknown.data;
1156
0
  for (size_t mi = 0; mi < b->materials.count; mi++) {
1157
0
    size_t cnt = 0;
1158
0
    for (size_t j = 0; j < b->mat_unknown.count; j++)
1159
0
      if (pend[j].mat_index == mi) cnt++;
1160
0
    if (cnt == 0) continue;
1161
0
    tobj_kv *arr = (tobj_kv *)tobj_arena_alloc(b->arena, cnt * sizeof(tobj_kv),
1162
0
                                               16);
1163
0
    if (!arr) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1164
0
    size_t w = 0;
1165
0
    for (size_t j = 0; j < b->mat_unknown.count; j++) {
1166
0
      if (pend[j].mat_index != mi) continue;
1167
      /* stable insertion sort by key; equal keys keep insertion order */
1168
0
      size_t pos = w;
1169
0
      while (pos > 0 && tobj_strcmp(arr[pos - 1].key, pend[j].key) > 0) {
1170
0
        arr[pos] = arr[pos - 1];
1171
0
        pos--;
1172
0
      }
1173
0
      arr[pos].key = pend[j].key;
1174
0
      arr[pos].value = pend[j].value;
1175
0
      w++;
1176
0
    }
1177
0
    TOBJ_T(tobj_material) *m =
1178
0
        (TOBJ_T(tobj_material) *)tobj_vec_at(&b->materials, mi);
1179
0
    m->unknown_params = arr;
1180
0
    m->num_unknown_params = w;
1181
0
  }
1182
0
  return true;
1183
0
}
tiny_obj_c.c:finalize_unknowns_f
Line
Count
Source
1153
3.65k
static bool TOBJ_T(finalize_unknowns)(TOBJ_T(tobj_builder) * b) {
1154
3.65k
  if (b->mat_unknown.count == 0) return true;
1155
0
  const tobj_pending_kv *pend = (const tobj_pending_kv *)b->mat_unknown.data;
1156
0
  for (size_t mi = 0; mi < b->materials.count; mi++) {
1157
0
    size_t cnt = 0;
1158
0
    for (size_t j = 0; j < b->mat_unknown.count; j++)
1159
0
      if (pend[j].mat_index == mi) cnt++;
1160
0
    if (cnt == 0) continue;
1161
0
    tobj_kv *arr = (tobj_kv *)tobj_arena_alloc(b->arena, cnt * sizeof(tobj_kv),
1162
0
                                               16);
1163
0
    if (!arr) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1164
0
    size_t w = 0;
1165
0
    for (size_t j = 0; j < b->mat_unknown.count; j++) {
1166
0
      if (pend[j].mat_index != mi) continue;
1167
      /* stable insertion sort by key; equal keys keep insertion order */
1168
0
      size_t pos = w;
1169
0
      while (pos > 0 && tobj_strcmp(arr[pos - 1].key, pend[j].key) > 0) {
1170
0
        arr[pos] = arr[pos - 1];
1171
0
        pos--;
1172
0
      }
1173
0
      arr[pos].key = pend[j].key;
1174
0
      arr[pos].value = pend[j].value;
1175
0
      w++;
1176
0
    }
1177
0
    TOBJ_T(tobj_material) *m =
1178
0
        (TOBJ_T(tobj_material) *)tobj_vec_at(&b->materials, mi);
1179
0
    m->unknown_params = arr;
1180
0
    m->num_unknown_params = w;
1181
0
  }
1182
0
  return true;
1183
0
}
Unexecuted instantiation: tiny_obj_c.c:finalize_unknowns_d
1184
1185
3.65k
static void TOBJ_T(builder_init)(TOBJ_T(tobj_builder) * b) {
1186
3.65k
  tobj_vec_init(&b->av, sizeof(TOBJ_REAL));
1187
3.65k
  tobj_vec_init(&b->avw, sizeof(TOBJ_REAL));
1188
3.65k
  tobj_vec_init(&b->avn, sizeof(TOBJ_REAL));
1189
3.65k
  tobj_vec_init(&b->avt, sizeof(TOBJ_REAL));
1190
3.65k
  tobj_vec_init(&b->avtw, sizeof(TOBJ_REAL));
1191
3.65k
  tobj_vec_init(&b->acol, sizeof(TOBJ_REAL));
1192
3.65k
  tobj_vec_init(&b->materials, sizeof(TOBJ_T(tobj_material)));
1193
3.65k
  tobj_vec_init(&b->matmap_key, sizeof(const char *));
1194
3.65k
  tobj_vec_init(&b->matmap_id, sizeof(int));
1195
3.65k
  tobj_vec_init(&b->mat_unknown, sizeof(tobj_pending_kv));
1196
3.65k
  tobj_vec_init(&b->shapes, sizeof(TOBJ_T(tobj_shape)));
1197
3.65k
  tobj_vec_init(&b->m_idx, sizeof(tobj_index));
1198
3.65k
  tobj_vec_init(&b->m_fv, sizeof(unsigned));
1199
3.65k
  tobj_vec_init(&b->m_mid, sizeof(int));
1200
3.65k
  tobj_vec_init(&b->m_sg, sizeof(unsigned));
1201
3.65k
  tobj_vec_init(&b->l_idx, sizeof(tobj_index));
1202
3.65k
  tobj_vec_init(&b->l_cnt, sizeof(int));
1203
3.65k
  tobj_vec_init(&b->p_idx, sizeof(tobj_index));
1204
3.65k
  tobj_vec_init(&b->corners, sizeof(tobj_index));
1205
3.65k
  tobj_vec_init(&b->ringpos, sizeof(TOBJ_REAL));
1206
3.65k
  tobj_vec_init(&b->tris, sizeof(uint32_t));
1207
3.65k
  tobj_vec_init(&b->tscratch, 1);
1208
3.65k
  tobj_vec_init(&b->ff_vp, sizeof(TOBJ_REAL));
1209
3.65k
  tobj_vec_init(&b->ff_cp_int, sizeof(int));
1210
3.65k
  tobj_vec_init(&b->ff_cp_idx, sizeof(tobj_index));
1211
3.65k
  tobj_vec_init(&b->ff_knu, sizeof(TOBJ_REAL));
1212
3.65k
  tobj_vec_init(&b->ff_knv, sizeof(TOBJ_REAL));
1213
3.65k
  tobj_vec_init(&b->ff_trim, sizeof(TOBJ_T(tobj_trim_loop)));
1214
3.65k
  tobj_vec_init(&b->ff_hole, sizeof(TOBJ_T(tobj_trim_loop)));
1215
3.65k
  tobj_vec_init(&b->ff_scrv, sizeof(TOBJ_T(tobj_trim_loop)));
1216
3.65k
  tobj_vec_init(&b->sh_curves, sizeof(TOBJ_T(tobj_curve)));
1217
3.65k
  tobj_vec_init(&b->sh_curves2, sizeof(TOBJ_T(tobj_curve2)));
1218
3.65k
  tobj_vec_init(&b->sh_surfaces, sizeof(TOBJ_T(tobj_surface)));
1219
3.65k
  b->ff_degu = 0;
1220
3.65k
  b->ff_degv = 0;
1221
3.65k
}
tiny_obj_c.c:builder_init_f
Line
Count
Source
1185
3.65k
static void TOBJ_T(builder_init)(TOBJ_T(tobj_builder) * b) {
1186
3.65k
  tobj_vec_init(&b->av, sizeof(TOBJ_REAL));
1187
3.65k
  tobj_vec_init(&b->avw, sizeof(TOBJ_REAL));
1188
3.65k
  tobj_vec_init(&b->avn, sizeof(TOBJ_REAL));
1189
3.65k
  tobj_vec_init(&b->avt, sizeof(TOBJ_REAL));
1190
3.65k
  tobj_vec_init(&b->avtw, sizeof(TOBJ_REAL));
1191
3.65k
  tobj_vec_init(&b->acol, sizeof(TOBJ_REAL));
1192
3.65k
  tobj_vec_init(&b->materials, sizeof(TOBJ_T(tobj_material)));
1193
3.65k
  tobj_vec_init(&b->matmap_key, sizeof(const char *));
1194
3.65k
  tobj_vec_init(&b->matmap_id, sizeof(int));
1195
3.65k
  tobj_vec_init(&b->mat_unknown, sizeof(tobj_pending_kv));
1196
3.65k
  tobj_vec_init(&b->shapes, sizeof(TOBJ_T(tobj_shape)));
1197
3.65k
  tobj_vec_init(&b->m_idx, sizeof(tobj_index));
1198
3.65k
  tobj_vec_init(&b->m_fv, sizeof(unsigned));
1199
3.65k
  tobj_vec_init(&b->m_mid, sizeof(int));
1200
3.65k
  tobj_vec_init(&b->m_sg, sizeof(unsigned));
1201
3.65k
  tobj_vec_init(&b->l_idx, sizeof(tobj_index));
1202
3.65k
  tobj_vec_init(&b->l_cnt, sizeof(int));
1203
3.65k
  tobj_vec_init(&b->p_idx, sizeof(tobj_index));
1204
3.65k
  tobj_vec_init(&b->corners, sizeof(tobj_index));
1205
3.65k
  tobj_vec_init(&b->ringpos, sizeof(TOBJ_REAL));
1206
3.65k
  tobj_vec_init(&b->tris, sizeof(uint32_t));
1207
3.65k
  tobj_vec_init(&b->tscratch, 1);
1208
3.65k
  tobj_vec_init(&b->ff_vp, sizeof(TOBJ_REAL));
1209
3.65k
  tobj_vec_init(&b->ff_cp_int, sizeof(int));
1210
3.65k
  tobj_vec_init(&b->ff_cp_idx, sizeof(tobj_index));
1211
3.65k
  tobj_vec_init(&b->ff_knu, sizeof(TOBJ_REAL));
1212
3.65k
  tobj_vec_init(&b->ff_knv, sizeof(TOBJ_REAL));
1213
3.65k
  tobj_vec_init(&b->ff_trim, sizeof(TOBJ_T(tobj_trim_loop)));
1214
3.65k
  tobj_vec_init(&b->ff_hole, sizeof(TOBJ_T(tobj_trim_loop)));
1215
3.65k
  tobj_vec_init(&b->ff_scrv, sizeof(TOBJ_T(tobj_trim_loop)));
1216
3.65k
  tobj_vec_init(&b->sh_curves, sizeof(TOBJ_T(tobj_curve)));
1217
3.65k
  tobj_vec_init(&b->sh_curves2, sizeof(TOBJ_T(tobj_curve2)));
1218
3.65k
  tobj_vec_init(&b->sh_surfaces, sizeof(TOBJ_T(tobj_surface)));
1219
3.65k
  b->ff_degu = 0;
1220
3.65k
  b->ff_degv = 0;
1221
3.65k
}
Unexecuted instantiation: tiny_obj_c.c:builder_init_d
1222
1223
3.65k
static void TOBJ_T(builder_dispose)(TOBJ_T(tobj_builder) * b) {
1224
3.65k
  const tobj_allocator *a = b->alloc;
1225
3.65k
  tobj_vec_free(&b->av, a);
1226
3.65k
  tobj_vec_free(&b->avw, a);
1227
3.65k
  tobj_vec_free(&b->avn, a);
1228
3.65k
  tobj_vec_free(&b->avt, a);
1229
3.65k
  tobj_vec_free(&b->avtw, a);
1230
3.65k
  tobj_vec_free(&b->acol, a);
1231
3.65k
  tobj_vec_free(&b->materials, a);
1232
3.65k
  tobj_vec_free(&b->matmap_key, a);
1233
3.65k
  tobj_vec_free(&b->matmap_id, a);
1234
3.65k
  tobj_vec_free(&b->mat_unknown, a);
1235
3.65k
  tobj_vec_free(&b->shapes, a);
1236
3.65k
  tobj_vec_free(&b->m_idx, a);
1237
3.65k
  tobj_vec_free(&b->m_fv, a);
1238
3.65k
  tobj_vec_free(&b->m_mid, a);
1239
3.65k
  tobj_vec_free(&b->m_sg, a);
1240
3.65k
  tobj_vec_free(&b->l_idx, a);
1241
3.65k
  tobj_vec_free(&b->l_cnt, a);
1242
3.65k
  tobj_vec_free(&b->p_idx, a);
1243
3.65k
  tobj_vec_free(&b->corners, a);
1244
3.65k
  tobj_vec_free(&b->ringpos, a);
1245
3.65k
  tobj_vec_free(&b->tris, a);
1246
3.65k
  tobj_vec_free(&b->tscratch, a);
1247
3.65k
  tobj_vec_free(&b->ff_vp, a);
1248
3.65k
  tobj_vec_free(&b->ff_cp_int, a);
1249
3.65k
  tobj_vec_free(&b->ff_cp_idx, a);
1250
3.65k
  tobj_vec_free(&b->ff_knu, a);
1251
3.65k
  tobj_vec_free(&b->ff_knv, a);
1252
3.65k
  tobj_vec_free(&b->ff_trim, a);
1253
3.65k
  tobj_vec_free(&b->ff_hole, a);
1254
3.65k
  tobj_vec_free(&b->ff_scrv, a);
1255
3.65k
  tobj_vec_free(&b->sh_curves, a);
1256
3.65k
  tobj_vec_free(&b->sh_curves2, a);
1257
3.65k
  tobj_vec_free(&b->sh_surfaces, a);
1258
3.65k
}
tiny_obj_c.c:builder_dispose_f
Line
Count
Source
1223
3.65k
static void TOBJ_T(builder_dispose)(TOBJ_T(tobj_builder) * b) {
1224
3.65k
  const tobj_allocator *a = b->alloc;
1225
3.65k
  tobj_vec_free(&b->av, a);
1226
3.65k
  tobj_vec_free(&b->avw, a);
1227
3.65k
  tobj_vec_free(&b->avn, a);
1228
3.65k
  tobj_vec_free(&b->avt, a);
1229
3.65k
  tobj_vec_free(&b->avtw, a);
1230
3.65k
  tobj_vec_free(&b->acol, a);
1231
3.65k
  tobj_vec_free(&b->materials, a);
1232
3.65k
  tobj_vec_free(&b->matmap_key, a);
1233
3.65k
  tobj_vec_free(&b->matmap_id, a);
1234
3.65k
  tobj_vec_free(&b->mat_unknown, a);
1235
3.65k
  tobj_vec_free(&b->shapes, a);
1236
3.65k
  tobj_vec_free(&b->m_idx, a);
1237
3.65k
  tobj_vec_free(&b->m_fv, a);
1238
3.65k
  tobj_vec_free(&b->m_mid, a);
1239
3.65k
  tobj_vec_free(&b->m_sg, a);
1240
3.65k
  tobj_vec_free(&b->l_idx, a);
1241
3.65k
  tobj_vec_free(&b->l_cnt, a);
1242
3.65k
  tobj_vec_free(&b->p_idx, a);
1243
3.65k
  tobj_vec_free(&b->corners, a);
1244
3.65k
  tobj_vec_free(&b->ringpos, a);
1245
3.65k
  tobj_vec_free(&b->tris, a);
1246
3.65k
  tobj_vec_free(&b->tscratch, a);
1247
3.65k
  tobj_vec_free(&b->ff_vp, a);
1248
3.65k
  tobj_vec_free(&b->ff_cp_int, a);
1249
3.65k
  tobj_vec_free(&b->ff_cp_idx, a);
1250
3.65k
  tobj_vec_free(&b->ff_knu, a);
1251
3.65k
  tobj_vec_free(&b->ff_knv, a);
1252
3.65k
  tobj_vec_free(&b->ff_trim, a);
1253
3.65k
  tobj_vec_free(&b->ff_hole, a);
1254
3.65k
  tobj_vec_free(&b->ff_scrv, a);
1255
3.65k
  tobj_vec_free(&b->sh_curves, a);
1256
3.65k
  tobj_vec_free(&b->sh_curves2, a);
1257
3.65k
  tobj_vec_free(&b->sh_surfaces, a);
1258
3.65k
}
Unexecuted instantiation: tiny_obj_c.c:builder_dispose_d
1259
1260
3.65k
void TOBJ_T(tobj_scene_free)(TOBJ_T(tobj_scene) * s) {
1261
3.65k
  if (!s) return;
1262
3.65k
  if (s->arena) {
1263
3.65k
    tobj_arena *ar = (tobj_arena *)s->arena;
1264
3.65k
    tobj_allocator a = s->allocator;
1265
3.65k
    tobj_arena_destroy(ar);
1266
3.65k
    tobj_free(&a, ar, sizeof(tobj_arena));
1267
3.65k
  }
1268
3.65k
  tobj_memset(s, 0, sizeof *s);
1269
3.65k
}
tobj_scene_free_f
Line
Count
Source
1260
3.65k
void TOBJ_T(tobj_scene_free)(TOBJ_T(tobj_scene) * s) {
1261
3.65k
  if (!s) return;
1262
3.65k
  if (s->arena) {
1263
3.65k
    tobj_arena *ar = (tobj_arena *)s->arena;
1264
3.65k
    tobj_allocator a = s->allocator;
1265
3.65k
    tobj_arena_destroy(ar);
1266
3.65k
    tobj_free(&a, ar, sizeof(tobj_arena));
1267
3.65k
  }
1268
3.65k
  tobj_memset(s, 0, sizeof *s);
1269
3.65k
}
Unexecuted instantiation: tobj_scene_free_d
1270
1271
const char *TOBJ_T(tobj_material_get_param)(const TOBJ_T(tobj_material) * m,
1272
0
                                            const char *key) {
1273
0
  if (!m || !key) return NULL;
1274
0
  size_t lo = 0, hi = m->num_unknown_params;
1275
0
  while (lo < hi) {
1276
0
    size_t mid = lo + (hi - lo) / 2;
1277
0
    const char *k = m->unknown_params[mid].key;
1278
    /* strcmp without libc */
1279
0
    size_t i = 0;
1280
0
    int cmp = 0;
1281
0
    for (;; i++) {
1282
0
      unsigned char a = (unsigned char)k[i], bb = (unsigned char)key[i];
1283
0
      if (a != bb) {
1284
0
        cmp = (int)a - (int)bb;
1285
0
        break;
1286
0
      }
1287
0
      if (!a) break;
1288
0
    }
1289
0
    if (cmp == 0) return m->unknown_params[mid].value;
1290
0
    if (cmp < 0)
1291
0
      lo = mid + 1;
1292
0
    else
1293
0
      hi = mid;
1294
0
  }
1295
0
  return NULL;
1296
0
}
Unexecuted instantiation: tobj_material_get_param_f
Unexecuted instantiation: tobj_material_get_param_d
1297
1298
#ifdef TOBJ_ENABLE_MULTITHREADING
1299
/* Per-thread vertex-parse output (MT pass 1). */
1300
typedef struct TOBJ_T(tobj_tls) {
1301
  const char *base;
1302
  const tobj_line *lines;
1303
  size_t lo, hi;
1304
  const tobj_allocator *alloc;
1305
  bool store_colors;
1306
  bool parse_vp;
1307
  bool oom;
1308
  tobj_vec lv, lvw, lvw_has, lcol, lcol_has, lvn, lvt, lvtw, lvtw_has, lvp;
1309
  int vp_comps;
1310
} TOBJ_T(tobj_tls);
1311
1312
static void TOBJ_T(mt_worker)(void *arg) {
1313
  TOBJ_T(tobj_tls) *w = (TOBJ_T(tobj_tls) *)arg;
1314
  const tobj_allocator *a = w->alloc;
1315
  for (size_t i = w->lo; i < w->hi && !w->oom; i++) {
1316
    const tobj_line *ln = &w->lines[i];
1317
    tobj_cursor c;
1318
    tobj_cur_init(&c, w->base + ln->pos, ln->len);
1319
    const char *tok;
1320
    size_t tl;
1321
    if (!tobj_next_token(&c, &tok, &tl)) continue;
1322
    if (tobj_slice_eq(tok, tl, "v")) {
1323
      double nums[8];
1324
      int n = 0;
1325
      while (n < 8) {
1326
        double d;
1327
        const char *s = c.p;
1328
        if (!tobj_scan_double(&c.p, c.end, &d)) {
1329
          c.p = s;
1330
          break;
1331
        }
1332
        nums[n++] = d;
1333
      }
1334
      while (n < 3) nums[n++] = 0.0;
1335
      TOBJ_REAL xyz[3] = {(TOBJ_REAL)nums[0], (TOBJ_REAL)nums[1],
1336
                          (TOBJ_REAL)nums[2]};
1337
      uint8_t wh = (n == 4);
1338
      TOBJ_REAL wv = wh ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1;
1339
      if (!tobj_vec_append(&w->lv, xyz, 3, a) ||
1340
          !tobj_vec_append(&w->lvw, &wv, 1, a) ||
1341
          !tobj_vec_append(&w->lvw_has, &wh, 1, a)) {
1342
        w->oom = true;
1343
        return;
1344
      }
1345
      if (w->store_colors) {
1346
        uint8_t ch = (n >= 6);
1347
        TOBJ_REAL rgb[3] = {ch ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1,
1348
                            ch ? (TOBJ_REAL)nums[4] : (TOBJ_REAL)1,
1349
                            ch ? (TOBJ_REAL)nums[5] : (TOBJ_REAL)1};
1350
        if (!tobj_vec_append(&w->lcol, rgb, 3, a) ||
1351
            !tobj_vec_append(&w->lcol_has, &ch, 1, a)) {
1352
          w->oom = true;
1353
          return;
1354
        }
1355
      }
1356
    } else if (tobj_slice_eq(tok, tl, "vn")) {
1357
      TOBJ_REAL xyz[3] = {0, 0, 0};
1358
      double d;
1359
      for (int k = 0; k < 3; k++)
1360
        if (tobj_scan_double(&c.p, c.end, &d)) xyz[k] = (TOBJ_REAL)d;
1361
      if (!tobj_vec_append(&w->lvn, xyz, 3, a)) {
1362
        w->oom = true;
1363
        return;
1364
      }
1365
    } else if (tobj_slice_eq(tok, tl, "vt")) {
1366
      double d;
1367
      TOBJ_REAL uv[2] = {0, 0};
1368
      int n = 0;
1369
      for (; n < 2; n++) {
1370
        if (!tobj_scan_double(&c.p, c.end, &d)) break;
1371
        uv[n] = (TOBJ_REAL)d;
1372
      }
1373
      uint8_t wh = (uint8_t)tobj_scan_double(&c.p, c.end, &d);
1374
      TOBJ_REAL wv = wh ? (TOBJ_REAL)d : (TOBJ_REAL)0;
1375
      if (!tobj_vec_append(&w->lvt, uv, 2, a) ||
1376
          !tobj_vec_append(&w->lvtw, &wv, 1, a) ||
1377
          !tobj_vec_append(&w->lvtw_has, &wh, 1, a)) {
1378
        w->oom = true;
1379
        return;
1380
      }
1381
    } else if (w->parse_vp && tobj_slice_eq(tok, tl, "vp")) {
1382
      double d;
1383
      int n = 0;
1384
      TOBJ_REAL uvw[3] = {0, 0, 0};
1385
      for (; n < 3; n++) {
1386
        if (!tobj_scan_double(&c.p, c.end, &d)) break;
1387
        uvw[n] = (TOBJ_REAL)d;
1388
      }
1389
      if (n < 1) continue;
1390
      if (n > w->vp_comps) w->vp_comps = n;
1391
      if (!tobj_vec_append(&w->lvp, uvw, (size_t)n, a)) {
1392
        w->oom = true;
1393
        return;
1394
      }
1395
    }
1396
  }
1397
}
1398
1399
/* Reconstruct a lazy optional array (weights/colors/texcoord-w) from the
1400
 * per-thread (value,has) streams, reproducing the single-threaded semantics
1401
 * exactly: empty until the first present entry, then dense with backfill. */
1402
static bool TOBJ_T(mt_lazy)(tobj_vec *dst, size_t comps, const TOBJ_REAL *dflt,
1403
                            TOBJ_T(tobj_tls) * tls, int T, int kind,
1404
                            const tobj_allocator *a) {
1405
  bool started = false;
1406
  size_t gi = 0;
1407
  for (int t = 0; t < T; t++) {
1408
    tobj_vec *vval = (kind == 0) ? &tls[t].lvw
1409
                                 : (kind == 1) ? &tls[t].lcol : &tls[t].lvtw;
1410
    tobj_vec *vhas = (kind == 0) ? &tls[t].lvw_has
1411
                                 : (kind == 1) ? &tls[t].lcol_has
1412
                                               : &tls[t].lvtw_has;
1413
    size_t cnt = vhas->count;
1414
    const TOBJ_REAL *val = (const TOBJ_REAL *)vval->data;
1415
    const uint8_t *has = (const uint8_t *)vhas->data;
1416
    for (size_t j = 0; j < cnt; j++, gi++) {
1417
      if (has[j] || started) {
1418
        if (!started) {
1419
          for (size_t k = 0; k < gi; k++)
1420
            if (!tobj_vec_append(dst, dflt, comps, a)) return false;
1421
          started = true;
1422
        }
1423
        if (!tobj_vec_append(dst, &val[j * comps], comps, a)) return false;
1424
      }
1425
    }
1426
  }
1427
  return true;
1428
}
1429
1430
/* MT pass 1: parse v/vn/vt/vp in parallel and merge into the builder's attrib
1431
 * arrays, reproducing single-threaded output exactly. Sets b->prefilled. */
1432
static bool TOBJ_T(mt_prepass)(TOBJ_T(tobj_builder) * b, const tobj_vec *lines,
1433
                               const char *base, int T) {
1434
  const tobj_allocator *a = b->alloc;
1435
  TOBJ_T(tobj_tls) *tls =
1436
      (TOBJ_T(tobj_tls) *)tobj_alloc(a, (size_t)T * sizeof(*tls), 16);
1437
  tobj_thunk *thunks = (tobj_thunk *)tobj_alloc(a, (size_t)T * sizeof(*thunks),
1438
                                                16);
1439
  if (!tls || !thunks) {
1440
    if (tls) tobj_free(a, tls, (size_t)T * sizeof(*tls));
1441
    if (thunks) tobj_free(a, thunks, (size_t)T * sizeof(*thunks));
1442
    return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1443
  }
1444
  size_t nlines = lines->count;
1445
  const tobj_line *ld = (const tobj_line *)lines->data;
1446
  for (int t = 0; t < T; t++) {
1447
    tobj_memset(&tls[t], 0, sizeof tls[t]);
1448
    tls[t].base = base;
1449
    tls[t].lines = ld;
1450
    tls[t].lo = (size_t)t * nlines / (size_t)T;
1451
    tls[t].hi = (size_t)(t + 1) * nlines / (size_t)T;
1452
    tls[t].alloc = a;
1453
    tls[t].store_colors = b->cfg->store_vertex_colors;
1454
    tls[t].parse_vp = b->cfg->parse_freeform;
1455
    tobj_vec_init(&tls[t].lv, sizeof(TOBJ_REAL));
1456
    tobj_vec_init(&tls[t].lvw, sizeof(TOBJ_REAL));
1457
    tobj_vec_init(&tls[t].lvw_has, 1);
1458
    tobj_vec_init(&tls[t].lcol, sizeof(TOBJ_REAL));
1459
    tobj_vec_init(&tls[t].lcol_has, 1);
1460
    tobj_vec_init(&tls[t].lvn, sizeof(TOBJ_REAL));
1461
    tobj_vec_init(&tls[t].lvt, sizeof(TOBJ_REAL));
1462
    tobj_vec_init(&tls[t].lvtw, sizeof(TOBJ_REAL));
1463
    tobj_vec_init(&tls[t].lvtw_has, 1);
1464
    tobj_vec_init(&tls[t].lvp, sizeof(TOBJ_REAL));
1465
    thunks[t].fn = TOBJ_T(mt_worker);
1466
    thunks[t].arg = &tls[t];
1467
  }
1468
1469
  tobj_run_parallel(thunks, T);
1470
1471
  bool ok = true;
1472
  for (int t = 0; t < T; t++)
1473
    if (tls[t].oom) ok = false;
1474
1475
  /* merge plain arrays in thread (= file) order */
1476
  for (int t = 0; ok && t < T; t++) {
1477
    if (!tobj_vec_append(&b->av, tls[t].lv.data, tls[t].lv.count, a) ||
1478
        !tobj_vec_append(&b->avn, tls[t].lvn.data, tls[t].lvn.count, a) ||
1479
        !tobj_vec_append(&b->avt, tls[t].lvt.data, tls[t].lvt.count, a))
1480
      ok = false;
1481
    if (ok && b->cfg->parse_freeform && tls[t].lvp.count) {
1482
      if (!tobj_vec_append(&b->ff_vp, tls[t].lvp.data, tls[t].lvp.count, a))
1483
        ok = false;
1484
      if (tls[t].vp_comps > b->ff_vp_comps) b->ff_vp_comps = tls[t].vp_comps;
1485
    }
1486
  }
1487
  /* reconstruct lazy optional arrays */
1488
  if (ok) {
1489
    TOBJ_REAL d1 = (TOBJ_REAL)1, d0 = (TOBJ_REAL)0;
1490
    TOBJ_REAL white[3] = {(TOBJ_REAL)1, (TOBJ_REAL)1, (TOBJ_REAL)1};
1491
    if (!TOBJ_T(mt_lazy)(&b->avw, 1, &d1, tls, T, 0, a)) ok = false;
1492
    if (ok && b->cfg->store_vertex_colors &&
1493
        !TOBJ_T(mt_lazy)(&b->acol, 3, white, tls, T, 1, a))
1494
      ok = false;
1495
    if (ok && !TOBJ_T(mt_lazy)(&b->avtw, 1, &d0, tls, T, 2, a)) ok = false;
1496
  }
1497
1498
  for (int t = 0; t < T; t++) {
1499
    tobj_vec_free(&tls[t].lv, a);
1500
    tobj_vec_free(&tls[t].lvw, a);
1501
    tobj_vec_free(&tls[t].lvw_has, a);
1502
    tobj_vec_free(&tls[t].lcol, a);
1503
    tobj_vec_free(&tls[t].lcol_has, a);
1504
    tobj_vec_free(&tls[t].lvn, a);
1505
    tobj_vec_free(&tls[t].lvt, a);
1506
    tobj_vec_free(&tls[t].lvtw, a);
1507
    tobj_vec_free(&tls[t].lvtw_has, a);
1508
    tobj_vec_free(&tls[t].lvp, a);
1509
  }
1510
  tobj_free(a, tls, (size_t)T * sizeof(*tls));
1511
  tobj_free(a, thunks, (size_t)T * sizeof(*thunks));
1512
  if (!ok) return TOBJ_T(b_fail)(b, TOBJ_ERR_ALLOC);
1513
  b->prefilled = true;
1514
  return true;
1515
}
1516
#endif /* TOBJ_ENABLE_MULTITHREADING */
1517
1518
tobj_result TOBJ_T(tobj_load_obj_from_memory)(TOBJ_T(tobj_scene) * out,
1519
                                              const uint8_t *buf, size_t len,
1520
                                              const tobj_load_config *cfg,
1521
3.65k
                                              tobj_diag *diag) {
1522
3.65k
  if (!out) return TOBJ_ERR_INVALID_ARG;
1523
3.65k
  tobj_memset(out, 0, sizeof *out);
1524
3.65k
  if (!buf && len) return TOBJ_ERR_INVALID_ARG;
1525
1526
3.65k
  tobj_load_config defcfg;
1527
3.65k
  if (!cfg) {
1528
0
    defcfg = tobj_default_config();
1529
0
    cfg = &defcfg;
1530
0
  }
1531
3.65k
  if (len > tobj_resolve_caps(cfg).input_bytes) return TOBJ_ERR_LIMIT_EXCEEDED;
1532
1533
3.65k
  tobj_allocator alloc;
1534
3.65k
  if (!tobj_resolve_allocator(&cfg->allocator, &alloc))
1535
0
    return TOBJ_ERR_INVALID_ARG;
1536
1537
3.65k
  tobj_arena *arena = (tobj_arena *)tobj_alloc(&alloc, sizeof(tobj_arena), 16);
1538
3.65k
  if (!arena) return TOBJ_ERR_ALLOC;
1539
3.65k
  tobj_arena_init(arena, &alloc, cfg->use_arena ? (size_t)(1u << 20) : 0);
1540
1541
3.65k
  TOBJ_T(tobj_builder) b;
1542
3.65k
  tobj_memset(&b, 0, sizeof b);
1543
3.65k
  b.alloc = &alloc;
1544
3.65k
  b.arena = arena;
1545
3.65k
  b.diag = diag;
1546
3.65k
  b.cfg = cfg;
1547
3.65k
  b.caps = tobj_resolve_caps(cfg);
1548
3.65k
  b.err = TOBJ_OK;
1549
3.65k
  b.triangulate = cfg->triangulate;
1550
3.65k
  b.cur_material = -1;
1551
3.65k
  b.cur_smooth = 0;
1552
3.65k
  b.cur_name = NULL;
1553
3.65k
  TOBJ_T(builder_init)(&b);
1554
1555
3.65k
  tobj_vec lines;
1556
3.65k
  tobj_vec_init(&lines, sizeof(tobj_line));
1557
3.65k
  if (!tobj_build_line_index(buf, len, &lines, &alloc)) {
1558
0
    b.err = TOBJ_ERR_ALLOC;
1559
0
  }
1560
1561
3.65k
  const char *base = (const char *)buf;
1562
1563
#ifdef TOBJ_ENABLE_MULTITHREADING
1564
  if (b.err == TOBJ_OK) {
1565
    int T = cfg->num_threads < 0 ? (int)tobj_hw_threads() : cfg->num_threads;
1566
    if (T > 64) T = 64;
1567
    /* Only worth threading on sizeable inputs. */
1568
    if (T > 1 && lines.count >= (size_t)(256 * T)) {
1569
      TOBJ_T(mt_prepass)(&b, &lines, base, T);
1570
    }
1571
  }
1572
#endif
1573
1574
15.9M
  for (size_t i = 0; i < lines.count && b.err == TOBJ_OK; i++) {
1575
15.9M
    tobj_line *ln = (tobj_line *)tobj_vec_at(&lines, i);
1576
15.9M
    b.line = i + 1;
1577
15.9M
    TOBJ_T(dispatch_line)(&b, base + ln->pos, ln->len);
1578
15.9M
  }
1579
1580
3.65k
  if (b.err == TOBJ_OK) TOBJ_T(finalize_shape)(&b);
1581
1582
  /* vertex color fallback: fill white if requested and none present */
1583
3.65k
  if (b.err == TOBJ_OK && cfg->store_vertex_colors &&
1584
3.65k
      cfg->vertex_color_fallback && b.acol.count == 0) {
1585
3.43k
    size_t numv = b.av.count / 3u;
1586
3.43k
    TOBJ_REAL white[3] = {(TOBJ_REAL)1, (TOBJ_REAL)1, (TOBJ_REAL)1};
1587
1.97M
    for (size_t i = 0; i < numv && b.err == TOBJ_OK; i++) {
1588
1.97M
      if (!tobj_vec_append(&b.acol, white, 3, &alloc)) b.err = TOBJ_ERR_ALLOC;
1589
1.97M
    }
1590
3.43k
  }
1591
1592
3.65k
  if (b.err == TOBJ_OK) {
1593
3.65k
#define TOBJ_FIN_BUF(field, vec)                                        \
1594
21.9k
  do {                                                                  \
1595
21.9k
    out->attrib.field.count = (vec).count;                              \
1596
21.9k
    out->attrib.field.ptr =                                             \
1597
21.9k
        (TOBJ_REAL *)tobj_vec_finalize(&(vec), arena, 16);              \
1598
21.9k
    if ((vec).count && !out->attrib.field.ptr) b.err = TOBJ_ERR_ALLOC;  \
1599
21.9k
  } while (0)
1600
3.65k
    TOBJ_FIN_BUF(vertices, b.av);
1601
3.65k
    TOBJ_FIN_BUF(vertex_weights, b.avw);
1602
3.65k
    TOBJ_FIN_BUF(normals, b.avn);
1603
3.65k
    TOBJ_FIN_BUF(texcoords, b.avt);
1604
3.65k
    TOBJ_FIN_BUF(texcoord_ws, b.avtw);
1605
3.65k
    TOBJ_FIN_BUF(colors, b.acol);
1606
3.65k
#undef TOBJ_FIN_BUF
1607
3.65k
  }
1608
1609
3.65k
  if (b.err == TOBJ_OK) TOBJ_T(finalize_unknowns)(&b);
1610
1611
3.65k
  if (b.err == TOBJ_OK) {
1612
3.65k
    out->num_shapes = b.shapes.count;
1613
3.65k
    out->shapes = (TOBJ_T(tobj_shape) *)tobj_vec_finalize(
1614
3.65k
        &b.shapes, arena, 16);
1615
3.65k
    if (b.shapes.count && !out->shapes) b.err = TOBJ_ERR_ALLOC;
1616
3.65k
    out->num_materials = b.materials.count;
1617
3.65k
    out->materials = (TOBJ_T(tobj_material) *)tobj_vec_finalize(
1618
3.65k
        &b.materials, arena, 16);
1619
3.65k
    if (b.materials.count && !out->materials) b.err = TOBJ_ERR_ALLOC;
1620
3.65k
  }
1621
1622
3.65k
  TOBJ_T(builder_dispose)(&b);
1623
3.65k
  tobj_vec_free(&lines, &alloc);
1624
1625
3.65k
  if (b.err != TOBJ_OK) {
1626
0
    tobj_arena_destroy(arena);
1627
0
    tobj_free(&alloc, arena, sizeof(tobj_arena));
1628
0
    tobj_memset(out, 0, sizeof *out);
1629
0
    return b.err;
1630
0
  }
1631
1632
3.65k
  out->allocator = alloc;
1633
3.65k
  out->arena = arena;
1634
3.65k
  out->strpool = NULL;
1635
3.65k
  return TOBJ_OK;
1636
3.65k
}
tobj_load_obj_from_memory_f
Line
Count
Source
1521
3.65k
                                              tobj_diag *diag) {
1522
3.65k
  if (!out) return TOBJ_ERR_INVALID_ARG;
1523
3.65k
  tobj_memset(out, 0, sizeof *out);
1524
3.65k
  if (!buf && len) return TOBJ_ERR_INVALID_ARG;
1525
1526
3.65k
  tobj_load_config defcfg;
1527
3.65k
  if (!cfg) {
1528
0
    defcfg = tobj_default_config();
1529
0
    cfg = &defcfg;
1530
0
  }
1531
3.65k
  if (len > tobj_resolve_caps(cfg).input_bytes) return TOBJ_ERR_LIMIT_EXCEEDED;
1532
1533
3.65k
  tobj_allocator alloc;
1534
3.65k
  if (!tobj_resolve_allocator(&cfg->allocator, &alloc))
1535
0
    return TOBJ_ERR_INVALID_ARG;
1536
1537
3.65k
  tobj_arena *arena = (tobj_arena *)tobj_alloc(&alloc, sizeof(tobj_arena), 16);
1538
3.65k
  if (!arena) return TOBJ_ERR_ALLOC;
1539
3.65k
  tobj_arena_init(arena, &alloc, cfg->use_arena ? (size_t)(1u << 20) : 0);
1540
1541
3.65k
  TOBJ_T(tobj_builder) b;
1542
3.65k
  tobj_memset(&b, 0, sizeof b);
1543
3.65k
  b.alloc = &alloc;
1544
3.65k
  b.arena = arena;
1545
3.65k
  b.diag = diag;
1546
3.65k
  b.cfg = cfg;
1547
3.65k
  b.caps = tobj_resolve_caps(cfg);
1548
3.65k
  b.err = TOBJ_OK;
1549
3.65k
  b.triangulate = cfg->triangulate;
1550
3.65k
  b.cur_material = -1;
1551
3.65k
  b.cur_smooth = 0;
1552
3.65k
  b.cur_name = NULL;
1553
3.65k
  TOBJ_T(builder_init)(&b);
1554
1555
3.65k
  tobj_vec lines;
1556
3.65k
  tobj_vec_init(&lines, sizeof(tobj_line));
1557
3.65k
  if (!tobj_build_line_index(buf, len, &lines, &alloc)) {
1558
0
    b.err = TOBJ_ERR_ALLOC;
1559
0
  }
1560
1561
3.65k
  const char *base = (const char *)buf;
1562
1563
#ifdef TOBJ_ENABLE_MULTITHREADING
1564
  if (b.err == TOBJ_OK) {
1565
    int T = cfg->num_threads < 0 ? (int)tobj_hw_threads() : cfg->num_threads;
1566
    if (T > 64) T = 64;
1567
    /* Only worth threading on sizeable inputs. */
1568
    if (T > 1 && lines.count >= (size_t)(256 * T)) {
1569
      TOBJ_T(mt_prepass)(&b, &lines, base, T);
1570
    }
1571
  }
1572
#endif
1573
1574
15.9M
  for (size_t i = 0; i < lines.count && b.err == TOBJ_OK; i++) {
1575
15.9M
    tobj_line *ln = (tobj_line *)tobj_vec_at(&lines, i);
1576
15.9M
    b.line = i + 1;
1577
15.9M
    TOBJ_T(dispatch_line)(&b, base + ln->pos, ln->len);
1578
15.9M
  }
1579
1580
3.65k
  if (b.err == TOBJ_OK) TOBJ_T(finalize_shape)(&b);
1581
1582
  /* vertex color fallback: fill white if requested and none present */
1583
3.65k
  if (b.err == TOBJ_OK && cfg->store_vertex_colors &&
1584
3.65k
      cfg->vertex_color_fallback && b.acol.count == 0) {
1585
3.43k
    size_t numv = b.av.count / 3u;
1586
3.43k
    TOBJ_REAL white[3] = {(TOBJ_REAL)1, (TOBJ_REAL)1, (TOBJ_REAL)1};
1587
1.97M
    for (size_t i = 0; i < numv && b.err == TOBJ_OK; i++) {
1588
1.97M
      if (!tobj_vec_append(&b.acol, white, 3, &alloc)) b.err = TOBJ_ERR_ALLOC;
1589
1.97M
    }
1590
3.43k
  }
1591
1592
3.65k
  if (b.err == TOBJ_OK) {
1593
3.65k
#define TOBJ_FIN_BUF(field, vec)                                        \
1594
3.65k
  do {                                                                  \
1595
3.65k
    out->attrib.field.count = (vec).count;                              \
1596
3.65k
    out->attrib.field.ptr =                                             \
1597
3.65k
        (TOBJ_REAL *)tobj_vec_finalize(&(vec), arena, 16);              \
1598
3.65k
    if ((vec).count && !out->attrib.field.ptr) b.err = TOBJ_ERR_ALLOC;  \
1599
3.65k
  } while (0)
1600
3.65k
    TOBJ_FIN_BUF(vertices, b.av);
1601
3.65k
    TOBJ_FIN_BUF(vertex_weights, b.avw);
1602
3.65k
    TOBJ_FIN_BUF(normals, b.avn);
1603
3.65k
    TOBJ_FIN_BUF(texcoords, b.avt);
1604
3.65k
    TOBJ_FIN_BUF(texcoord_ws, b.avtw);
1605
3.65k
    TOBJ_FIN_BUF(colors, b.acol);
1606
3.65k
#undef TOBJ_FIN_BUF
1607
3.65k
  }
1608
1609
3.65k
  if (b.err == TOBJ_OK) TOBJ_T(finalize_unknowns)(&b);
1610
1611
3.65k
  if (b.err == TOBJ_OK) {
1612
3.65k
    out->num_shapes = b.shapes.count;
1613
3.65k
    out->shapes = (TOBJ_T(tobj_shape) *)tobj_vec_finalize(
1614
3.65k
        &b.shapes, arena, 16);
1615
3.65k
    if (b.shapes.count && !out->shapes) b.err = TOBJ_ERR_ALLOC;
1616
3.65k
    out->num_materials = b.materials.count;
1617
3.65k
    out->materials = (TOBJ_T(tobj_material) *)tobj_vec_finalize(
1618
3.65k
        &b.materials, arena, 16);
1619
3.65k
    if (b.materials.count && !out->materials) b.err = TOBJ_ERR_ALLOC;
1620
3.65k
  }
1621
1622
3.65k
  TOBJ_T(builder_dispose)(&b);
1623
3.65k
  tobj_vec_free(&lines, &alloc);
1624
1625
3.65k
  if (b.err != TOBJ_OK) {
1626
0
    tobj_arena_destroy(arena);
1627
0
    tobj_free(&alloc, arena, sizeof(tobj_arena));
1628
0
    tobj_memset(out, 0, sizeof *out);
1629
0
    return b.err;
1630
0
  }
1631
1632
3.65k
  out->allocator = alloc;
1633
3.65k
  out->arena = arena;
1634
  out->strpool = NULL;
1635
3.65k
  return TOBJ_OK;
1636
3.65k
}
Unexecuted instantiation: tobj_load_obj_from_memory_d
1637
1638
tobj_result TOBJ_T(tobj_load_obj_from_io)(TOBJ_T(tobj_scene) * out,
1639
                                          const tobj_io_callbacks *io,
1640
                                          const tobj_load_config *cfg,
1641
0
                                          tobj_diag *diag) {
1642
0
  if (!out) return TOBJ_ERR_INVALID_ARG;
1643
0
  tobj_memset(out, 0, sizeof *out);
1644
0
  if (!io || !io->read) return TOBJ_ERR_INVALID_ARG;
1645
1646
0
  tobj_load_config cc = cfg ? *cfg : tobj_default_config();
1647
0
  tobj_allocator alloc;
1648
0
  if (!tobj_resolve_allocator(&cc.allocator, &alloc))
1649
0
    return TOBJ_ERR_INVALID_ARG;
1650
0
  cc.allocator = alloc;
1651
1652
0
  tobj_caps caps = tobj_resolve_caps(&cc);
1653
0
  tobj_vec buf;
1654
0
  tobj_vec_init(&buf, 1);
1655
0
  tobj_result r = TOBJ_OK;
1656
1657
0
  for (;;) {
1658
0
    size_t remain = caps.input_bytes - buf.count;
1659
0
    if (remain == 0) {
1660
0
      uint8_t extra;
1661
0
      size_t got = 0;
1662
0
      r = io->read(io->user_data, &extra, 1, &got);
1663
0
      if (r == TOBJ_OK && got != 0) r = TOBJ_ERR_LIMIT_EXCEEDED;
1664
0
      break;
1665
0
    }
1666
0
    size_t want = remain < (size_t)(64u * 1024u) ? remain
1667
0
                                                  : (size_t)(64u * 1024u);
1668
0
    size_t old = buf.count;
1669
0
    if (!tobj_vec_reserve(&buf, old + want, &alloc)) {
1670
0
      r = TOBJ_ERR_ALLOC;
1671
0
      break;
1672
0
    }
1673
0
    size_t got = 0;
1674
0
    r = io->read(io->user_data, (uint8_t *)buf.data + old, want, &got);
1675
0
    if (r != TOBJ_OK) break;
1676
0
    if (got > want) {
1677
0
      r = TOBJ_ERR_IO;
1678
0
      break;
1679
0
    }
1680
0
    if (got == 0) break;
1681
0
    buf.count = old + got;
1682
0
  }
1683
1684
0
  if (io->close) io->close(io->user_data);
1685
0
  if (r == TOBJ_OK)
1686
0
    r = TOBJ_T(tobj_load_obj_from_memory)(out, (const uint8_t *)buf.data,
1687
0
                                          buf.count, &cc, diag);
1688
0
  tobj_vec_free(&buf, &alloc);
1689
0
  return r;
1690
0
}
Unexecuted instantiation: tobj_load_obj_from_io_f
Unexecuted instantiation: tobj_load_obj_from_io_d
1691
1692
#ifdef TOBJ_ENABLE_FILE_IO
1693
tobj_result TOBJ_T(tobj_load_obj_from_file)(TOBJ_T(tobj_scene) * out,
1694
                                            const char *path,
1695
                                            const tobj_load_config *cfg,
1696
0
                                            tobj_diag *diag) {
1697
0
  if (!out) return TOBJ_ERR_INVALID_ARG;
1698
0
  tobj_memset(out, 0, sizeof *out);
1699
0
  if (!path) return TOBJ_ERR_INVALID_ARG;
1700
1701
0
  tobj_load_config cc = cfg ? *cfg : tobj_default_config();
1702
0
  tobj_allocator alloc;
1703
0
  if (!tobj_resolve_allocator(&cc.allocator, &alloc))
1704
0
    return TOBJ_ERR_INVALID_ARG;
1705
0
  cc.allocator = alloc;
1706
1707
0
  tobj_file_buf fb;
1708
0
  tobj_result r = tobj_file_open(&fb, path, &alloc,
1709
0
                                 tobj_resolve_caps(&cc).input_bytes);
1710
0
  if (r != TOBJ_OK) return r;
1711
1712
0
  size_t dlen;
1713
0
  char *basedir = tobj_dirname_dup(path, &alloc, &dlen);
1714
0
  if (!basedir) {
1715
0
    tobj_file_close(&fb);
1716
0
    return TOBJ_ERR_ALLOC;
1717
0
  }
1718
0
  tobj_file_res_ctx ctx;
1719
0
  ctx.basedir = basedir;
1720
0
  ctx.alloc = alloc;
1721
0
  ctx.max_bytes = tobj_resolve_caps(&cc).input_bytes;
1722
0
  if (!cc.mtl_resolver) {
1723
0
    cc.mtl_resolver = tobj_file_material_resolver;
1724
0
    cc.mtl_resolver_user_data = &ctx;
1725
0
  }
1726
1727
0
  r = TOBJ_T(tobj_load_obj_from_memory)(out, fb.data, fb.len, &cc, diag);
1728
1729
0
  tobj_free(&alloc, basedir, dlen + 1);
1730
0
  tobj_file_close(&fb);
1731
0
  return r;
1732
0
}
Unexecuted instantiation: tobj_load_obj_from_file_f
Unexecuted instantiation: tobj_load_obj_from_file_d
1733
#endif /* TOBJ_ENABLE_FILE_IO */
1734
1735
/* ----------------------------------------------------------------------- */
1736
/* standalone .mtl parsing                                                 */
1737
/* ----------------------------------------------------------------------- */
1738
1739
tobj_result TOBJ_T(tobj_parse_mtl_from_memory)(TOBJ_T(tobj_material_list) * out,
1740
                                               const uint8_t *buf, size_t len,
1741
                                               const tobj_load_config *cfg,
1742
0
                                               tobj_diag *diag) {
1743
0
  if (!out) return TOBJ_ERR_INVALID_ARG;
1744
0
  tobj_memset(out, 0, sizeof *out);
1745
0
  if (!buf && len) return TOBJ_ERR_INVALID_ARG;
1746
1747
0
  tobj_load_config defcfg;
1748
0
  if (!cfg) {
1749
0
    defcfg = tobj_default_config();
1750
0
    cfg = &defcfg;
1751
0
  }
1752
0
  if (len > tobj_resolve_caps(cfg).input_bytes) return TOBJ_ERR_LIMIT_EXCEEDED;
1753
0
  tobj_allocator alloc;
1754
0
  if (!tobj_resolve_allocator(&cfg->allocator, &alloc))
1755
0
    return TOBJ_ERR_INVALID_ARG;
1756
0
  tobj_arena *arena = (tobj_arena *)tobj_alloc(&alloc, sizeof(tobj_arena), 16);
1757
0
  if (!arena) return TOBJ_ERR_ALLOC;
1758
0
  tobj_arena_init(arena, &alloc, 0);
1759
1760
0
  TOBJ_T(tobj_builder) b;
1761
0
  tobj_memset(&b, 0, sizeof b);
1762
0
  b.alloc = &alloc;
1763
0
  b.arena = arena;
1764
0
  b.diag = diag;
1765
0
  b.cfg = cfg;
1766
0
  b.caps = tobj_resolve_caps(cfg);
1767
0
  b.err = TOBJ_OK;
1768
0
  TOBJ_T(builder_init)(&b);
1769
1770
0
  TOBJ_T(parse_mtl_buffer)(&b, buf, len);
1771
0
  if (b.err == TOBJ_OK) TOBJ_T(finalize_unknowns)(&b);
1772
1773
0
  if (b.err == TOBJ_OK) {
1774
0
    out->count = b.materials.count;
1775
0
    out->items = (TOBJ_T(tobj_material) *)tobj_vec_finalize(&b.materials,
1776
0
                                                            arena, 16);
1777
0
    if (b.materials.count && !out->items) b.err = TOBJ_ERR_ALLOC;
1778
0
  }
1779
1780
0
  TOBJ_T(builder_dispose)(&b);
1781
1782
0
  if (b.err != TOBJ_OK) {
1783
0
    tobj_arena_destroy(arena);
1784
0
    tobj_free(&alloc, arena, sizeof(tobj_arena));
1785
0
    tobj_memset(out, 0, sizeof *out);
1786
0
    return b.err;
1787
0
  }
1788
0
  out->allocator = alloc;
1789
0
  out->strpool = arena;
1790
0
  return TOBJ_OK;
1791
0
}
Unexecuted instantiation: tobj_parse_mtl_from_memory_f
Unexecuted instantiation: tobj_parse_mtl_from_memory_d
1792
1793
0
void TOBJ_T(tobj_material_list_free)(TOBJ_T(tobj_material_list) * list) {
1794
0
  if (!list) return;
1795
0
  if (list->strpool) {
1796
0
    tobj_arena *ar = (tobj_arena *)list->strpool;
1797
0
    tobj_allocator a = list->allocator;
1798
0
    tobj_arena_destroy(ar);
1799
0
    tobj_free(&a, ar, sizeof(tobj_arena));
1800
0
  }
1801
0
  tobj_memset(list, 0, sizeof *list);
1802
0
}
Unexecuted instantiation: tobj_material_list_free_f
Unexecuted instantiation: tobj_material_list_free_d
1803
1804
/* ----------------------------------------------------------------------- */
1805
/* streaming callback API                                                  */
1806
/* ----------------------------------------------------------------------- */
1807
1808
tobj_result TOBJ_T(tobj_load_obj_with_callbacks)(
1809
    const uint8_t *buf, size_t len, const TOBJ_T(tobj_callbacks) * cb,
1810
0
    const tobj_load_config *cfg, tobj_diag *diag) {
1811
0
  if (!cb) return TOBJ_ERR_INVALID_ARG;
1812
0
  if (!buf && len) return TOBJ_ERR_INVALID_ARG;
1813
1814
0
  tobj_load_config defcfg;
1815
0
  if (!cfg) {
1816
0
    defcfg = tobj_default_config();
1817
0
    cfg = &defcfg;
1818
0
  }
1819
0
  if (len > tobj_resolve_caps(cfg).input_bytes) return TOBJ_ERR_LIMIT_EXCEEDED;
1820
0
  tobj_allocator alloc;
1821
0
  if (!tobj_resolve_allocator(&cfg->allocator, &alloc))
1822
0
    return TOBJ_ERR_INVALID_ARG;
1823
0
  tobj_arena *arena = (tobj_arena *)tobj_alloc(&alloc, sizeof(tobj_arena), 16);
1824
0
  if (!arena) return TOBJ_ERR_ALLOC;
1825
0
  tobj_arena_init(arena, &alloc, 0);
1826
1827
0
  TOBJ_T(tobj_builder) b;
1828
0
  tobj_memset(&b, 0, sizeof b);
1829
0
  b.alloc = &alloc;
1830
0
  b.arena = arena;
1831
0
  b.diag = diag;
1832
0
  b.cfg = cfg;
1833
0
  b.caps = tobj_resolve_caps(cfg);
1834
0
  b.err = TOBJ_OK;
1835
0
  TOBJ_T(builder_init)(&b);
1836
0
  void *ud = cb->user_data;
1837
1838
0
  tobj_vec lines;
1839
0
  tobj_vec_init(&lines, sizeof(tobj_line));
1840
0
  if (!tobj_build_line_index(buf, len, &lines, &alloc)) b.err = TOBJ_ERR_ALLOC;
1841
1842
0
  size_t numv = 0, numvt = 0, numvn = 0;
1843
0
  const char *base = (const char *)buf;
1844
1845
0
  for (size_t i = 0; i < lines.count && b.err == TOBJ_OK; i++) {
1846
0
    tobj_line *ln = (tobj_line *)tobj_vec_at(&lines, i);
1847
0
    b.line = i + 1;
1848
0
    if (ln->len > b.caps.line_bytes) {
1849
0
      b.err = TOBJ_ERR_LIMIT_EXCEEDED;
1850
0
      break;
1851
0
    }
1852
0
    tobj_cursor c;
1853
0
    tobj_cur_init(&c, base + ln->pos, ln->len);
1854
0
    const char *tok;
1855
0
    size_t tl;
1856
0
    if (!tobj_next_token(&c, &tok, &tl)) continue;
1857
0
    if (tok[0] == '#') continue;
1858
1859
0
    if (tobj_slice_eq(tok, tl, "v")) {
1860
0
      double nums[8];
1861
0
      int n = 0;
1862
0
      while (n < 8) {
1863
0
        double d;
1864
0
        const char *save = c.p;
1865
0
        if (!tobj_scan_double(&c.p, c.end, &d)) {
1866
0
          c.p = save;
1867
0
          break;
1868
0
        }
1869
0
        nums[n++] = d;
1870
0
      }
1871
0
      while (n < 3) nums[n++] = 0.0;
1872
0
      TOBJ_REAL w = (n == 4) ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1;
1873
0
      if (cb->vertex_cb)
1874
0
        cb->vertex_cb(ud, (TOBJ_REAL)nums[0], (TOBJ_REAL)nums[1],
1875
0
                      (TOBJ_REAL)nums[2], w);
1876
0
      if (cb->vertex_color_cb) {
1877
0
        bool hc = n >= 6;
1878
0
        cb->vertex_color_cb(ud, (TOBJ_REAL)nums[0], (TOBJ_REAL)nums[1],
1879
0
                            (TOBJ_REAL)nums[2],
1880
0
                            hc ? (TOBJ_REAL)nums[3] : (TOBJ_REAL)1,
1881
0
                            hc ? (TOBJ_REAL)nums[4] : (TOBJ_REAL)1,
1882
0
                            hc ? (TOBJ_REAL)nums[5] : (TOBJ_REAL)1, hc);
1883
0
      }
1884
0
      numv++;
1885
0
    } else if (tobj_slice_eq(tok, tl, "vn")) {
1886
0
      double d;
1887
0
      TOBJ_REAL xyz[3] = {0, 0, 0};
1888
0
      for (int k = 0; k < 3; k++)
1889
0
        if (tobj_scan_double(&c.p, c.end, &d)) xyz[k] = (TOBJ_REAL)d;
1890
0
      if (cb->normal_cb) cb->normal_cb(ud, xyz[0], xyz[1], xyz[2]);
1891
0
      numvn++;
1892
0
    } else if (tobj_slice_eq(tok, tl, "vt")) {
1893
0
      double d;
1894
0
      TOBJ_REAL uvw[3] = {0, 0, 0};
1895
0
      for (int k = 0; k < 3; k++)
1896
0
        if (tobj_scan_double(&c.p, c.end, &d)) uvw[k] = (TOBJ_REAL)d;
1897
0
      if (cb->texcoord_cb) cb->texcoord_cb(ud, uvw[0], uvw[1], uvw[2]);
1898
0
      numvt++;
1899
0
    } else if (tobj_slice_eq(tok, tl, "f") || tobj_slice_eq(tok, tl, "l") ||
1900
0
               tobj_slice_eq(tok, tl, "p")) {
1901
0
      char kind = tok[0];
1902
0
      b.corners.count = 0;
1903
0
      const char *t2;
1904
0
      size_t l2;
1905
0
      while (tobj_next_token(&c, &t2, &l2)) {
1906
0
        tobj_index *slot = (tobj_index *)tobj_vec_push(&b.corners, &alloc);
1907
0
        if (!slot) {
1908
0
          b.err = TOBJ_ERR_ALLOC;
1909
0
          break;
1910
0
        }
1911
0
        TOBJ_T(parse_corner_n)(t2, l2, numv, numvt, numvn, slot);
1912
0
      }
1913
0
      if (b.err != TOBJ_OK) break;
1914
0
      int nidx = tobj_size_to_int(b.corners.count);
1915
0
      const tobj_index *ci = (const tobj_index *)b.corners.data;
1916
0
      if (kind == 'f' && cb->index_cb && nidx >= 3)
1917
0
        cb->index_cb(ud, ci, nidx);
1918
0
      else if (kind == 'l' && cb->line_cb && nidx >= 2)
1919
0
        cb->line_cb(ud, ci, nidx);
1920
0
      else if (kind == 'p' && cb->point_cb && nidx >= 1)
1921
0
        cb->point_cb(ud, ci, nidx);
1922
0
    } else if (tobj_slice_eq(tok, tl, "g") || tobj_slice_eq(tok, tl, "o")) {
1923
0
      const char *name = TOBJ_T(intern_rest)(&b, &c);
1924
0
      if (!name) {
1925
0
        b.err = TOBJ_ERR_ALLOC;
1926
0
        break;
1927
0
      }
1928
0
      if (tok[0] == 'o') {
1929
0
        if (cb->object_cb) cb->object_cb(ud, name);
1930
0
      } else {
1931
0
        if (cb->group_cb) {
1932
0
          const char *names[1] = {name};
1933
0
          cb->group_cb(ud, names, 1);
1934
0
        }
1935
0
      }
1936
0
    } else if (tobj_slice_eq(tok, tl, "usemtl")) {
1937
0
      const char *p = c.p, *e = c.end;
1938
0
      while (p < e && tobj_is_space(*p)) p++;
1939
0
      while (e > p && tobj_is_space(e[-1])) e--;
1940
0
      int id = TOBJ_T(mat_find)(&b, p, (size_t)(e - p));
1941
0
      if (cb->usemtl_cb) {
1942
0
        const char *name = tobj_intern(arena, p, (size_t)(e - p));
1943
0
        cb->usemtl_cb(ud, name, id);
1944
0
      }
1945
0
    } else if (tobj_slice_eq(tok, tl, "mtllib")) {
1946
0
      if (cfg->mtl_resolver) {
1947
0
        const char *t2;
1948
0
        size_t l2;
1949
0
        size_t before = b.materials.count;
1950
0
        while (tobj_next_token(&c, &t2, &l2)) {
1951
0
          char *name = (char *)tobj_arena_alloc(arena, l2 + 1, 1);
1952
0
          if (!name) {
1953
0
            b.err = TOBJ_ERR_ALLOC;
1954
0
            break;
1955
0
          }
1956
0
          tobj_memcpy(name, t2, l2);
1957
0
          name[l2] = '\0';
1958
0
          tobj_mtl_source src;
1959
0
          tobj_memset(&src, 0, sizeof src);
1960
0
          if (cfg->mtl_resolver(cfg->mtl_resolver_user_data, name, &src) ==
1961
0
              TOBJ_OK) {
1962
0
            TOBJ_T(parse_mtl_buffer)(&b, src.data, src.len);
1963
0
            if (src.release) src.release(src.release_ud, src.data, src.len);
1964
0
          }
1965
0
        }
1966
0
        if (b.err == TOBJ_OK && cb->mtllib_cb && b.materials.count > before) {
1967
0
          cb->mtllib_cb(ud, (const TOBJ_T(tobj_material) *)b.materials.data,
1968
0
                        tobj_size_to_int(b.materials.count));
1969
0
        }
1970
0
      }
1971
0
    }
1972
0
  }
1973
1974
0
  TOBJ_T(builder_dispose)(&b);
1975
0
  tobj_vec_free(&lines, &alloc);
1976
0
  tobj_arena_destroy(arena);
1977
0
  tobj_free(&alloc, arena, sizeof(tobj_arena));
1978
0
  return b.err;
1979
0
}
Unexecuted instantiation: tobj_load_obj_with_callbacks_f
Unexecuted instantiation: tobj_load_obj_with_callbacks_d