objfile.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /*
  2. * DoRayMe - a quick and dirty Raytracer
  3. * OBJ File implementation
  4. *
  5. * Created by Manoël Trapier
  6. * Copyright (c) 2020 986-Studio.
  7. *
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <tuple.h>
  13. #include <ray.h>
  14. #include <shape.h>
  15. #include <objfile.h>
  16. #include <math_helper.h>
  17. #include <group.h>
  18. #include <triangle.h>
  19. #include <sphere.h>
  20. #include <smoothtriangle.h>
  21. #include <transformation.h>
  22. #include <cone.h>
  23. #include <cylinder.h>
  24. #define MIN_ALLOC (2)
  25. //#define DEBUG_NORMAL
  26. OBJFile::OBJFile() : Shape(Shape::OBJFILE), ignoredLines(0)
  27. {
  28. stats.addOBJFile();
  29. this->allocatedVertexCount = MIN_ALLOC;
  30. this->vertexList = (Point **)calloc(sizeof(Point **), MIN_ALLOC);
  31. this->vertexCount = 0;
  32. this->allocatedVertexNormalCount = MIN_ALLOC;
  33. this->vertexNormalList = (Vector **)calloc(sizeof(Vector **), MIN_ALLOC);
  34. this->vertexNormalCount = 0;
  35. /* The base group */
  36. this->baseGroup = new Group(OBJ_DEFAULT_GROUP);
  37. this->currentGroup = this->baseGroup;
  38. this->baseGroup->parent = this;
  39. };
  40. OBJFile::~OBJFile()
  41. {
  42. int i;
  43. if (vertexCount > 0)
  44. {
  45. for(i = 0; i < vertexCount; i++)
  46. {
  47. delete this->vertexList[i];
  48. this->vertexList[i] = nullptr;
  49. }
  50. }
  51. free(this->vertexList);
  52. this->vertexList = nullptr;
  53. if (vertexNormalCount > 0)
  54. {
  55. for(i = 0; i < vertexNormalCount; i++)
  56. {
  57. delete this->vertexNormalList[i];
  58. this->vertexNormalList[i] = nullptr;
  59. }
  60. }
  61. free(this->vertexNormalList);
  62. this->vertexNormalList = nullptr;
  63. /* It is not our responsibility to clear the group object as this object may be destroyed before the
  64. * render is done
  65. */
  66. }
  67. OBJFile::OBJFile(const char *filepath) : OBJFile()
  68. {
  69. FILE *fp;
  70. size_t fileSize;
  71. char *fileBuff;
  72. fp = fopen(filepath, "rt");
  73. if (fp)
  74. {
  75. fseek(fp, 0, SEEK_END);
  76. fileSize = ftell(fp);
  77. /* Add one byte to the size to make sure it is null terminated */
  78. fileBuff = (char *)calloc(fileSize + 1, 1);
  79. fseek(fp, 0, SEEK_SET);
  80. fileSize = fread(fileBuff, 1, fileSize, fp);
  81. fclose(fp);
  82. this->parseOBJFile(fileBuff);
  83. free(fileBuff);
  84. }
  85. else
  86. {
  87. printf("ERROR: Can't open/find the file '%s'.\n", filepath);
  88. }
  89. }
  90. void OBJFile::addGroup(Group *group)
  91. {
  92. this->baseGroup->addObject(group);
  93. group->setParent(this);
  94. group->updateTransform();
  95. this->bounds | group->getBounds();
  96. this->currentGroup = group;
  97. }
  98. void OBJFile::addVertex(Point *vertex)
  99. {
  100. if ((this->vertexCount + 1) > this->allocatedVertexCount)
  101. {
  102. this->allocatedVertexCount *= 2;
  103. this->vertexList = (Point **)realloc(this->vertexList, sizeof(Point **) * this->allocatedVertexCount);
  104. }
  105. this->vertexList[this->vertexCount++] = vertex;
  106. }
  107. void OBJFile::addVertexNormal(Vector *vertexNormal)
  108. {
  109. if ((this->vertexNormalCount + 1) > this->allocatedVertexNormalCount)
  110. {
  111. this->allocatedVertexNormalCount *= 2;
  112. this->vertexNormalList = (Vector **)realloc(this->vertexNormalList, sizeof(Vector **) * this->allocatedVertexNormalCount);
  113. }
  114. this->vertexNormalList[this->vertexNormalCount++] = vertexNormal;
  115. }
  116. Group *OBJFile::groups(const char *groupName)
  117. {
  118. if (strncmp(groupName, this->baseGroup->getName(), strlen(groupName)) == 0)
  119. {
  120. return this->baseGroup;
  121. }
  122. int i;
  123. for(i = 0; i < this->baseGroup->getObjectCount(); i++)
  124. {
  125. Shape *cur = (*this->baseGroup)[i];
  126. if (cur->getType() == Shape::GROUP)
  127. {
  128. Group *curGrp = (Group *)cur;
  129. if (strncmp(groupName, curGrp->getName(), strlen(groupName)) == 0)
  130. {
  131. return curGrp;
  132. }
  133. }
  134. }
  135. /* Not found */
  136. return nullptr;
  137. }
  138. Intersect OBJFile::intersect(Ray r)
  139. {
  140. return this->baseGroup->intersect(r);
  141. }
  142. bool OBJFile::includes(Shape *b)
  143. {
  144. return this->baseGroup->includes(b);
  145. }
  146. Intersect OBJFile::localIntersect(Ray r)
  147. {
  148. return Intersect();
  149. }
  150. Tuple OBJFile::localNormalAt(Tuple point, Intersection *hit)
  151. {
  152. return Vector(0, 1, 0);
  153. }
  154. BoundingBox OBJFile::getLocalBounds()
  155. {
  156. return this->bounds;
  157. }
  158. BoundingBox OBJFile::getBounds()
  159. {
  160. if (this->bounds.isEmpty()) { this->updateBoundingBox(); }
  161. return this->bounds;
  162. }
  163. void OBJFile::updateBoundingBox()
  164. {
  165. int i;
  166. this->bounds.reset();
  167. this->bounds | this->baseGroup->getBounds();
  168. }
  169. void OBJFile::updateTransform()
  170. {
  171. int i;
  172. Shape::updateTransform();
  173. this->baseGroup->updateTransform();
  174. /* Once the full stack being notified of the changes, let's update the
  175. * bounding box
  176. */
  177. this->updateBoundingBox();
  178. }
  179. void OBJFile::dumpMe(FILE * fp)
  180. {
  181. int i;
  182. fprintf(fp, "\"Type\": \"OBJFile\",\n");
  183. fprintf(fp, "\"Objects\": {\n");
  184. this->baseGroup->dumpMe(fp);
  185. fprintf(fp, "},\n");
  186. fprintf(fp, "\"Vertices\": {\n");
  187. for(i = 1; i < this->vertexCount + 1; i++)
  188. {
  189. fprintf(fp, "\"v[%d]\": { \"x\": %f, \"y\": %f, \"z\": %f },\n", i,
  190. this->vertices(i).x, this->vertices(i).y, this->vertices(i).z);
  191. }
  192. fprintf(fp, "},\n");
  193. fprintf(fp, "\"NormalVertices\": {\n");
  194. for(i = 1; i < this->vertexNormalCount + 1; i++)
  195. {
  196. fprintf(fp, "\"vn[%d]\": { \"x\": %f, \"y\": %f, \"z\": %f },\n", i,
  197. this->verticesNormal(i).x, this->verticesNormal(i).y, this->verticesNormal(i).z);
  198. }
  199. fprintf(fp, "},\n");
  200. Shape::dumpMe(fp);
  201. }
  202. #define MAX_LINE_LENGTH (512)
  203. /* Here start the fun! */
  204. int OBJFile::parseOBJFile(const char *content)
  205. {
  206. /* I don't think we will handle lines of more than 512 characters... */
  207. char lineBuff[MAX_LINE_LENGTH];
  208. uint32_t currentLineNum = 1;
  209. uint32_t totalLength = strlen(content);
  210. /* Need to process line by line */
  211. const char *bufferPos = content;
  212. const char *lineNewline;
  213. while(*bufferPos != '\0')
  214. {
  215. uint32_t lineLength;
  216. lineNewline = strchr(bufferPos, '\n');
  217. if (lineNewline == nullptr)
  218. {
  219. /* We are on the last line */
  220. lineLength = strlen(bufferPos);
  221. }
  222. else
  223. {
  224. lineLength = (lineNewline - bufferPos);
  225. }
  226. if (lineLength >= MAX_LINE_LENGTH)
  227. {
  228. printf("ERROR: Line %d is too long! (%d)\n", currentLineNum, lineLength);
  229. return -1;
  230. }
  231. memset(lineBuff, 0, MAX_LINE_LENGTH);
  232. strncpy(lineBuff, bufferPos, lineLength);
  233. this->parseLine(lineBuff, currentLineNum);
  234. bufferPos += lineLength + 1;
  235. if ((bufferPos - content) >= totalLength)
  236. {
  237. /* We are past the length of the buffer, don't need to continue */
  238. break;
  239. }
  240. currentLineNum++;
  241. }
  242. return 0;
  243. }
  244. #define MAX_ARGS (15)
  245. /* Parse the line into a couple ofr argc/argv using space as argument separator */
  246. void OBJFile::parseLine(char *line, uint32_t currentLine)
  247. {
  248. char *argv[MAX_ARGS];
  249. uint32_t argc = 0;
  250. char *buffer = line;
  251. uint32_t lineLength = strlen(line);
  252. uint32_t linePos = 0;
  253. /* First argument */
  254. argv[argc++] = line;
  255. while(linePos < lineLength)
  256. {
  257. char *next = strchr(buffer, ' ');
  258. if (next != nullptr)
  259. {
  260. *next = '\0';
  261. linePos = next - line;
  262. buffer = next + 1;
  263. /* Skip empty strings as it mean multiple spaces */
  264. if (strlen(buffer) > 0)
  265. {
  266. argv[argc++] = buffer;
  267. }
  268. }
  269. else
  270. {
  271. linePos = lineLength;
  272. }
  273. }
  274. if (this->execLine(argc, argv, currentLine))
  275. {
  276. this->ignoredLines++;
  277. }
  278. }
  279. static int parseFaceVertex(char *buf, uint32_t &v, uint32_t &vt, uint32_t &vn)
  280. {
  281. uint32_t bufPos = 0;
  282. uint32_t lineLength = strlen(buf);
  283. vt = INT32_MAX;
  284. vn = INT32_MAX;
  285. int ret = 0;
  286. int token = 0;
  287. while(bufPos < lineLength)
  288. {
  289. char *next = strchr(buf, '/');
  290. if (next != nullptr)
  291. {
  292. *next = '\0';
  293. bufPos = next - buf;
  294. }
  295. else
  296. {
  297. bufPos = lineLength;
  298. }
  299. if (strlen(buf) > 0)
  300. {
  301. switch(token)
  302. {
  303. case 0: v = atol(buf); break;
  304. case 1: vt = atol(buf); break;
  305. case 2: vn = atol(buf); break;
  306. default: printf("ERROR: Too many entry for a face vertice!"); ret = 1;
  307. }
  308. }
  309. buf = next + 1;
  310. token++;
  311. }
  312. return ret;
  313. }
  314. #ifdef DEBUG_NORMAL
  315. Shape *makeVector(Point pos, Vector verNorm, Colour c, double scale = 1)
  316. {
  317. Group *ret = new Group("Vector");
  318. Sphere *sp = new Sphere();
  319. Colour c2 = c;
  320. sp->material.colour = c2;
  321. sp->material.ambient = 1;
  322. sp->material.refractiveIndex = 0;
  323. sp->material.reflective = 0;
  324. sp->material.specular = 0;
  325. sp->materialSet = true;
  326. sp->setTransform(translation(pos.x, pos.y, pos.z) * scaling(0.1, 0.1, 0.1));
  327. ret->addObject(sp);
  328. double theta = atan2(verNorm.x, verNorm.z);
  329. double radius = verNorm.magnitude();
  330. double phi = acos(verNorm.y / radius);
  331. sp = new Sphere();
  332. c2 = c;
  333. c2.x /=3; c2.y /=3; c2.y /=3;
  334. sp->material.colour = c2;
  335. sp->material.ambient = 1;
  336. sp->material.refractiveIndex = 0;
  337. sp->material.transparency = 0;
  338. sp->material.specular = 0;
  339. sp->materialSet = true;
  340. sp->setTransform(translation(pos.x, pos.y, pos.z) * translation(verNorm.x, verNorm.y, verNorm.z) * scaling(0.1, 0.1, 0.1));
  341. ret->addObject(sp);
  342. c2 = c;
  343. c2.x /=2; c2.y /=3; c2.y /=2;
  344. Cone *cn = new Cone();
  345. cn->minCap = 0;
  346. cn->maxCap = 1;
  347. cn->material.colour = c2;
  348. cn->material.ambient = 1;
  349. cn->material.refractiveIndex = 0;
  350. cn->material.reflective = 0;
  351. cn->material.specular = 0;
  352. cn->materialSet = true;
  353. cn->setTransform(translation(pos.x, pos.y, pos.z) * rotationY(theta) * rotationX(phi) * scaling(0.1, radius, 0.1));
  354. ret->addObject(cn);
  355. return ret;
  356. }
  357. #endif
  358. /* Actually execute the line */
  359. int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
  360. {
  361. int ret = 1;
  362. if (strncmp(argv[0], "v", 2) == 0)
  363. {
  364. /* Vertice entry */
  365. if (argc != 4)
  366. {
  367. printf("ERROR: Malformed file at line %d: Vertices expect 3 parameters!\n", currentLine);
  368. }
  369. else
  370. {
  371. this->addVertex(new Point(atof(argv[1]), atof(argv[2]), atof(argv[3])));
  372. ret = 0;
  373. }
  374. }
  375. else if (strncmp(argv[0], "vn", 3) == 0)
  376. {
  377. /* Vertice Normal entry */
  378. if (argc != 4)
  379. {
  380. printf("ERROR: Malformed file at line %d: Vertices normal expect 3 parameters!\n", currentLine);
  381. }
  382. else
  383. {
  384. this->addVertexNormal(new Vector(atof(argv[1]), atof(argv[2]), atof(argv[3])));
  385. ret = 0;
  386. }
  387. }
  388. else if (strncmp(argv[0], "f", 2) == 0)
  389. {
  390. /* Faces entry */
  391. int i;
  392. uint32_t v[MAX_ARGS], vt[MAX_ARGS], vn[MAX_ARGS];
  393. for(i = 1; i < argc; i++)
  394. {
  395. parseFaceVertex(argv[i], v[i], vt[i], vn[i]);
  396. }
  397. if (argc == 4)
  398. {
  399. Shape *t;
  400. if (vn[1] == INT32_MAX)
  401. {
  402. t = new Triangle(this->vertices(v[1]),
  403. this->vertices(v[2]),
  404. this->vertices(v[3]));
  405. }
  406. else
  407. {
  408. #ifdef DEBUG_NORMAL
  409. this->currentGroup->addObject(makeVector(this->vertices(v[1]),
  410. this->verticesNormal(vn[1]),
  411. Colour(1, 0, 1)));
  412. this->currentGroup->addObject(makeVector(this->vertices(v[2]),
  413. this->verticesNormal(vn[2]),
  414. Colour(0.5, 0, 0.5)));
  415. this->currentGroup->addObject(makeVector(this->vertices(v[3]),
  416. this->verticesNormal(vn[3]),
  417. Colour(0.5, 0, 1)));
  418. t = new SmoothTriangle(this->vertices(v[1]),
  419. this->vertices(v[2]),
  420. this->vertices(v[3]),
  421. this->verticesNormal(vn[1]),
  422. this->verticesNormal(vn[2]),
  423. this->verticesNormal(vn[3]));
  424. #endif
  425. }
  426. this->currentGroup->addObject(t);
  427. ret = 0;
  428. }
  429. else if (argc > 4)
  430. {
  431. #ifdef DEBUG_NORMAL
  432. if (vn[1] != INT32_MAX)
  433. {
  434. for(i = 2; i < (argc); i++)
  435. {
  436. this->currentGroup->addObject(makeVector(this->vertices(v[i]),
  437. this->verticesNormal(vn[i]),
  438. Colour(1, 1, 0)));
  439. }
  440. this->currentGroup->addObject(makeVector(this->vertices(v[1]),
  441. this->verticesNormal(vn[1]),
  442. Colour(0, 1, 0)));
  443. }
  444. #endif
  445. for(i = 2; i < (argc - 1); i++)
  446. {
  447. Shape *t;
  448. if (vn[1] == INT32_MAX)
  449. {
  450. t = new Triangle(this->vertices(v[1]),
  451. this->vertices(v[i]),
  452. this->vertices(v[i + 1]));
  453. }
  454. else
  455. {
  456. t = new SmoothTriangle(this->vertices(v[1]),
  457. this->vertices(v[i]),
  458. this->vertices(v[i + 1]),
  459. this->verticesNormal(vn[1]),
  460. this->verticesNormal(vn[i]),
  461. this->verticesNormal(vn[i + 1]));
  462. }
  463. this->currentGroup->addObject(t);
  464. }
  465. ret = 0;
  466. }
  467. else
  468. {
  469. printf("ERROR: Malformed file at line %d: Too few/many parameters!\n", currentLine);
  470. }
  471. }
  472. else if (strncmp(argv[0], "g", 2) == 0)
  473. {
  474. if (argc == 2)
  475. {
  476. this->addGroup(new Group(argv[1]));
  477. }
  478. else
  479. {
  480. printf("ERROR: Malformed file at line %d: Too few/many parameters!\n", currentLine);
  481. }
  482. }
  483. return ret;
  484. }
  485. void OBJFile::lock()
  486. {
  487. Shape::lock();
  488. this->baseGroup->lock();
  489. }