| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS
- #endif
- #include "objparser.h"
- #include <cassert>
- #include <cmath>
- #include <cstdio>
- #include <cstdlib>
- #include <cstring>
- template <typename T>
- static void growArray(T*& data, size_t& capacity)
- {
- size_t newcapacity = capacity == 0 ? 32 : capacity + capacity / 2;
- T* newdata = new T[newcapacity];
- if (data)
- {
- memcpy(newdata, data, capacity * sizeof(T));
- delete[] data;
- }
- data = newdata;
- capacity = newcapacity;
- }
- static int fixupIndex(int index, size_t size)
- {
- return (index >= 0) ? index - 1 : int(size) + index;
- }
- static int parseInt(const char* s, const char** end)
- {
- // skip whitespace
- while (*s == ' ' || *s == '\t')
- s++;
- // read sign bit
- int sign = (*s == '-');
- s += (*s == '-' || *s == '+');
- unsigned int result = 0;
- for (;;)
- {
- if (unsigned(*s - '0') < 10)
- result = result * 10 + (*s - '0');
- else
- break;
- s++;
- }
- // return end-of-string
- *end = s;
- return sign ? -int(result) : int(result);
- }
- static float parseFloat(const char* s, const char** end)
- {
- static const double digits[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- static const double powers[] = {1e0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+21, 1e+22};
- // skip whitespace
- while (*s == ' ' || *s == '\t')
- s++;
- // read sign
- double sign = (*s == '-') ? -1 : 1;
- s += (*s == '-' || *s == '+');
- // read integer part
- double result = 0;
- int power = 0;
- while (unsigned(*s - '0') < 10)
- {
- result = result * 10 + digits[*s - '0'];
- s++;
- }
- // read fractional part
- if (*s == '.')
- {
- s++;
- while (unsigned(*s - '0') < 10)
- {
- result = result * 10 + digits[*s - '0'];
- s++;
- power--;
- }
- }
- // read exponent part
- if ((*s | ' ') == 'e')
- {
- s++;
- // read exponent sign
- int expsign = (*s == '-') ? -1 : 1;
- s += (*s == '-' || *s == '+');
- // read exponent
- int exppower = 0;
- while (unsigned(*s - '0') < 10)
- {
- exppower = exppower * 10 + (*s - '0');
- s++;
- }
- // done!
- power += expsign * exppower;
- }
- // return end-of-string
- *end = s;
- // note: this is precise if result < 9e15
- // for longer inputs we lose a bit of precision here
- if (unsigned(-power) < sizeof(powers) / sizeof(powers[0]))
- return float(sign * result / powers[-power]);
- else if (unsigned(power) < sizeof(powers) / sizeof(powers[0]))
- return float(sign * result * powers[power]);
- else
- return float(sign * result * pow(10.0, power));
- }
- static const char* parseFace(const char* s, int& vi, int& vti, int& vni)
- {
- while (*s == ' ' || *s == '\t')
- s++;
- vi = parseInt(s, &s);
- if (*s != '/')
- return s;
- s++;
- // handle vi//vni indices
- if (*s != '/')
- vti = parseInt(s, &s);
- if (*s != '/')
- return s;
- s++;
- vni = parseInt(s, &s);
- return s;
- }
- ObjFile::ObjFile()
- : v(0)
- , v_size(0)
- , v_cap(0)
- , vt(0)
- , vt_size(0)
- , vt_cap(0)
- , vn(0)
- , vn_size(0)
- , vn_cap(0)
- , f(0)
- , f_size(0)
- , f_cap(0)
- , g(0)
- , g_size(0)
- , g_cap(0)
- {
- }
- ObjFile::~ObjFile()
- {
- delete[] v;
- delete[] vt;
- delete[] vn;
- delete[] f;
- delete[] g;
- }
- void objParseLine(ObjFile& result, const char* line)
- {
- if (line[0] == 'v' && line[1] == ' ')
- {
- const char* s = line + 2;
- float x = parseFloat(s, &s);
- float y = parseFloat(s, &s);
- float z = parseFloat(s, &s);
- if (result.v_size + 3 > result.v_cap)
- growArray(result.v, result.v_cap);
- result.v[result.v_size++] = x;
- result.v[result.v_size++] = y;
- result.v[result.v_size++] = z;
- }
- else if (line[0] == 'v' && line[1] == 't' && line[2] == ' ')
- {
- const char* s = line + 3;
- float u = parseFloat(s, &s);
- float v = parseFloat(s, &s);
- float w = parseFloat(s, &s);
- if (result.vt_size + 3 > result.vt_cap)
- growArray(result.vt, result.vt_cap);
- result.vt[result.vt_size++] = u;
- result.vt[result.vt_size++] = v;
- result.vt[result.vt_size++] = w;
- }
- else if (line[0] == 'v' && line[1] == 'n' && line[2] == ' ')
- {
- const char* s = line + 3;
- float x = parseFloat(s, &s);
- float y = parseFloat(s, &s);
- float z = parseFloat(s, &s);
- if (result.vn_size + 3 > result.vn_cap)
- growArray(result.vn, result.vn_cap);
- result.vn[result.vn_size++] = x;
- result.vn[result.vn_size++] = y;
- result.vn[result.vn_size++] = z;
- }
- else if (line[0] == 'f' && line[1] == ' ')
- {
- const char* s = line + 2;
- if (!result.g)
- {
- growArray(result.g, result.g_cap);
- ObjGroup g = {};
- result.g[result.g_size++] = g;
- }
- size_t v = result.v_size / 3;
- size_t vt = result.vt_size / 3;
- size_t vn = result.vn_size / 3;
- int fv = 0;
- int f[3][3] = {};
- while (*s)
- {
- int vi = 0, vti = 0, vni = 0;
- s = parseFace(s, vi, vti, vni);
- if (vi == 0)
- break;
- f[fv][0] = fixupIndex(vi, v);
- f[fv][1] = fixupIndex(vti, vt);
- f[fv][2] = fixupIndex(vni, vn);
- if (fv == 2)
- {
- if (result.f_size + 9 > result.f_cap)
- growArray(result.f, result.f_cap);
- memcpy(&result.f[result.f_size], f, 9 * sizeof(int));
- result.f_size += 9;
- result.g[result.g_size - 1].index_count += 3;
- f[1][0] = f[2][0];
- f[1][1] = f[2][1];
- f[1][2] = f[2][2];
- }
- else
- {
- fv++;
- }
- }
- }
- else if (strncmp(line, "usemtl", 6) == 0)
- {
- const char* s = line + 6;
- // skip whitespace
- while (*s == ' ' || *s == '\t')
- s++;
- if (result.g_size + 1 > result.g_cap)
- growArray(result.g, result.g_cap);
- ObjGroup g = {};
- g.index_offset = result.f_size / 3;
- strncpy(g.material, s, sizeof(g.material));
- g.material[sizeof(g.material) - 1] = 0;
- result.g[result.g_size++] = g;
- }
- }
- bool objParseFile(ObjFile& result, const char* path)
- {
- FILE* file = fopen(path, "rb");
- if (!file)
- return false;
- char buffer[65536];
- size_t size = 0;
- while (!feof(file))
- {
- size += fread(buffer + size, 1, sizeof(buffer) - size, file);
- size_t line = 0;
- while (line < size)
- {
- // find the end of current line
- void* eol = memchr(buffer + line, '\n', size - line);
- if (!eol)
- break;
- // zero-terminate for objParseLine
- size_t next = static_cast<char*>(eol) - buffer;
- buffer[next] = 0;
- // process next line
- objParseLine(result, buffer + line);
- line = next + 1;
- }
- // move prefix of the last line in the buffer to the beginning of the buffer for next iteration
- assert(line <= size);
- memmove(buffer, buffer + line, size - line);
- size -= line;
- }
- if (size)
- {
- // process last line
- assert(size < sizeof(buffer));
- buffer[size] = 0;
- objParseLine(result, buffer);
- }
- fclose(file);
- return true;
- }
- bool objValidate(const ObjFile& result)
- {
- size_t v = result.v_size / 3;
- size_t vt = result.vt_size / 3;
- size_t vn = result.vn_size / 3;
- for (size_t i = 0; i < result.f_size; i += 3)
- {
- int vi = result.f[i + 0];
- int vti = result.f[i + 1];
- int vni = result.f[i + 2];
- if (vi < 0)
- return false;
- if (vi >= 0 && size_t(vi) >= v)
- return false;
- if (vti >= 0 && size_t(vti) >= vt)
- return false;
- if (vni >= 0 && size_t(vni) >= vn)
- return false;
- }
- return true;
- }
|