Import OBJ.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. namespace OBJ{ // so local structures are unique
  5. /******************************************************************************/
  6. struct FVtx
  7. {
  8. Int pos, nrm, tex;
  9. FVtx() {pos=nrm=tex=-1;}
  10. };
  11. struct FTri
  12. {
  13. VecI pos, nrm, tex;
  14. };
  15. struct FQuad
  16. {
  17. VecI4 pos, nrm, tex;
  18. };
  19. struct Group
  20. {
  21. Memc<FTri > tri ;
  22. Memc<FQuad> quad ;
  23. Int material;
  24. Str name ;
  25. Int faces()C {return tri.elms() + quad.elms();}
  26. Group& set(Int material) {T.material=material; return T;}
  27. };
  28. /******************************************************************************/
  29. static void ImportMtl(C Str &name, MemPtr<XMaterial> materials)
  30. {
  31. FileTextEx f; if(f.read(name))
  32. {
  33. for(Str line; !f.end(); )
  34. {
  35. f.getLine(line);
  36. if(Starts(line, "newmtl " )){ materials.New ().name = _SkipChar(TextPos(line, ' ')) ;}else
  37. if(Starts(line, "Ke " )){if(materials.elms())materials.last().ambient =TextVec(_SkipChar(TextPos(line, ' ')));}else
  38. if(Starts(line, "Kd " )){if(materials.elms())materials.last().color.xyz =TextVec(_SkipChar(TextPos(line, ' ')));}else
  39. if(Starts(line, "Ks " )){if(materials.elms())materials.last().specular =TextVec(_SkipChar(TextPos(line, ' ')));}else
  40. if(Starts(line, "map_Ke " )){if(materials.elms())materials.last(). light_map= _SkipChar(TextPos(line, ' ')) ;}else
  41. if(Starts(line, "map_Kd " )){if(materials.elms())materials.last(). color_map= _SkipChar(TextPos(line, ' ')) ;}else
  42. if(Starts(line, "map_Ks " )){if(materials.elms())materials.last().specular_map= _SkipChar(TextPos(line, ' ')) ;}else
  43. if(Starts(line, "map_bump "))
  44. {
  45. if(materials.elms())
  46. {
  47. Str name=_SkipChar(TextPos(line, ' ')); if(Starts(name, "-bm"))name=_SkipChar(TextPos(_SkipChar(TextPos(name, ' ')), ' '));
  48. materials.last().normal_map=name;
  49. }
  50. }
  51. }
  52. REPAO(materials).fixPath(GetPath(name));
  53. }
  54. }
  55. /******************************************************************************/
  56. } // namespace OBJ
  57. /******************************************************************************/
  58. Bool ImportOBJ(C Str &name, Mesh *mesh, MemPtr<XMaterial> materials, MemPtr<Int> part_material_index)
  59. {
  60. if(mesh)mesh->del();
  61. materials .clear();
  62. part_material_index.clear();
  63. using namespace OBJ;
  64. FileTextEx f; if(f.read(name))
  65. {
  66. Int cur_material=-1;
  67. Bool flip_normal_y=false;
  68. Memc<Vec > vpos;
  69. Memc<Vec > vnrm;
  70. Memc<Vec2 > vtex;
  71. Memc<Group> groups;
  72. Memc<FVtx > fvtx;
  73. for(Str line; !f.end(); )
  74. {
  75. Char c=f.getChar();
  76. if(c=='m' && f.getChar()=='t' && f.getChar()=='l' && f.getChar()=='l' && f.getChar()=='i' && f.getChar()=='b' && f.getChar()==' ')
  77. {
  78. if(materials)ImportMtl(S+_GetPath(name)+'/'+f.getLine(), materials);else f.skipLine();
  79. }else
  80. if(c=='v')
  81. {
  82. c=f.getChar();
  83. if(c==' ' )f.get(vpos.New());else
  84. if(c=='n' && f.getChar()==' ')vnrm.add(!f.getVec());else
  85. if(c=='t' && f.getChar()==' '){Vec2 &t=vtex.New(); f.get(t); t.y=1-t.y;}
  86. }else
  87. if(c=='g' && f.getChar()==' ')
  88. {
  89. if(groups.elms() && !groups.last().faces())groups.removeLast();
  90. f.getLine(groups.New().set(cur_material).name);
  91. }else
  92. if(c=='u' && f.getChar()=='s' && f.getChar()=='e' && f.getChar()=='m' && f.getChar()=='t' && f.getChar()=='l' && f.getChar()==' ')
  93. {
  94. if(materials)
  95. {
  96. f.getLine(line);
  97. cur_material=-1; REPA(materials)if(Equal(materials[i].name, line)){cur_material=i; break;}
  98. }else f.skipLine();
  99. if(groups.elms() && groups.last().faces())groups.New ().set(cur_material);else
  100. if(groups.elms() )groups.last().set(cur_material);
  101. }else
  102. if(c=='f' && f.getChar()==' ')
  103. {
  104. if(!groups.elms())groups.New().set(cur_material);
  105. Group &group=groups.last();
  106. f.getLine(line);
  107. for(CChar *face=line; face; )
  108. {
  109. CalcValue cval; face=TextValue(face, cval); if(cval.type)
  110. {
  111. FVtx &vtx=fvtx.New();
  112. vtx.pos=cval.asInt()-1; if(!InRange(vtx.pos, vpos))goto invalid; // can't be invalid
  113. if(Is(face) && *face++=='/')
  114. if(*face!=' ') // space means proceed to next index
  115. {
  116. face=TextValue(face, cval); if(cval.type)vtx.tex=cval.asInt()-1; // can be invalid, because it's checked later
  117. if(Is(face) && *face++=='/')
  118. if(*face!=' ') // space means proceed to next index
  119. {
  120. face=TextValue(face, cval); if(cval.type)vtx.nrm=cval.asInt()-1; // can be invalid, because it's checked later
  121. }
  122. }
  123. }else break;
  124. }
  125. if(fvtx.elms()==3)
  126. {
  127. FTri &face=group.tri.New();
  128. face.pos.set(fvtx[0].pos, fvtx[1].pos, fvtx[2].pos);
  129. face.nrm.set(fvtx[0].nrm, fvtx[1].nrm, fvtx[2].nrm);
  130. face.tex.set(fvtx[0].tex, fvtx[1].tex, fvtx[2].tex);
  131. }else
  132. if(fvtx.elms()==4)
  133. {
  134. VecI4 ind (fvtx[0].pos, fvtx[1].pos, fvtx[2].pos, fvtx[3].pos);
  135. Quad quad(vpos[ind.x], vpos[ind.y], vpos[ind.z], vpos[ind.w]);
  136. if(!quad.convex())goto as_poly; // this can happen
  137. FQuad &face=group.quad.New();
  138. face.pos=ind;
  139. face.nrm.set(fvtx[0].nrm, fvtx[1].nrm, fvtx[2].nrm, fvtx[3].nrm);
  140. face.tex.set(fvtx[0].tex, fvtx[1].tex, fvtx[2].tex, fvtx[3].tex);
  141. }else
  142. {
  143. as_poly:
  144. Vec nrm=0; REPA(fvtx) // get average face normal
  145. {
  146. VecI2 ind(fvtx[i].pos, fvtx[(i+1)%fvtx.elms()].pos);
  147. nrm+=GetNormalEdge(vpos[ind.x], vpos[ind.y]);
  148. }
  149. for(; fvtx.elms()>=3; )
  150. {
  151. Bool added=false; // if added a face in this step
  152. REP(fvtx.elms()-2)
  153. {
  154. VecI ind(fvtx[i].pos, fvtx[i+1].pos, fvtx[i+2].pos);
  155. Tri tri(vpos[ind.x], vpos[ind.y], vpos[ind.z]);
  156. if(Dot(nrm, tri.n)>0) // if normal of this triangle is correct
  157. {
  158. Vec cross[3]={Cross(tri.n, tri.p[0]-tri.p[1]), Cross(tri.n, tri.p[1]-tri.p[2]), Cross(tri.n, tri.p[2]-tri.p[0])};
  159. REPAD(t, fvtx)if(t<i || t>i+2)if(Cuts(vpos[fvtx[t].pos], tri, cross))goto cuts; // if any other vertex intersects with this triangle, then continue
  160. {
  161. FTri &face=group.tri.New();
  162. face.pos=ind;
  163. face.nrm.set(fvtx[i].nrm, fvtx[i+1].nrm, fvtx[i+2].nrm);
  164. face.tex.set(fvtx[i].tex, fvtx[i+1].tex, fvtx[i+2].tex);
  165. fvtx.remove(i+1, true);
  166. added=true;
  167. }
  168. cuts:;
  169. }
  170. }
  171. if(!added) // add all remaining
  172. {
  173. REP(fvtx.elms()-2)
  174. {
  175. FTri &face=group.tri.New();
  176. face.pos.set(fvtx[i].pos, fvtx[i+1].pos, fvtx[i+2].pos);
  177. face.nrm.set(fvtx[i].nrm, fvtx[i+1].nrm, fvtx[i+2].nrm);
  178. face.tex.set(fvtx[i].tex, fvtx[i+1].tex, fvtx[i+2].tex);
  179. fvtx.remove(i+1, true);
  180. }
  181. break;
  182. }
  183. }
  184. }
  185. invalid:
  186. fvtx.clear();
  187. }else
  188. if(c=='#') // comment
  189. {
  190. f.getLine(line);
  191. if(Contains(line, "Luxology modo"))flip_normal_y=true;
  192. }else
  193. if(c!=0xA)f.skipLine();
  194. }
  195. if(flip_normal_y)REPAO(materials).flip_normal_y=true;
  196. REPA(groups)if(!groups[i].faces())groups.remove(i, true); // remove empty groups
  197. // create mesh
  198. if(mesh)
  199. {
  200. mesh->create(groups.elms());
  201. FREPA(groups)
  202. {
  203. Group &cur =groups[i];
  204. MeshPart &part=mesh->parts[i]; Set(part.name, cur.name);
  205. MeshBase &mshb=part.base;
  206. mshb.create(cur.tri.elms()*3+cur.quad.elms()*4, 0, cur.tri.elms(), cur.quad.elms(), (vnrm.elms() ? VTX_NRM : 0)|(vtex.elms() ? VTX_TEX0 : 0));
  207. Vec *pos =mshb.vtx .pos ();
  208. Vec *nrm =mshb.vtx .nrm ();
  209. Vec2 *tex =mshb.vtx .tex0();
  210. VecI *tri =mshb.tri .ind ();
  211. VecI4 *quad=mshb.quad.ind ();
  212. Int vtxs=0;
  213. FREPA(cur.tri)
  214. {
  215. FTri &ftri=cur.tri[i];
  216. if(pos)
  217. {
  218. pos[0]=vpos[ftri.pos.c[0]];
  219. pos[1]=vpos[ftri.pos.c[1]];
  220. pos[2]=vpos[ftri.pos.c[2]]; pos+=3;
  221. }
  222. if(nrm)
  223. {
  224. nrm[0]=(InRange(ftri.nrm.c[0], vnrm) ? vnrm[ftri.nrm.c[0]] : VecZero);
  225. nrm[1]=(InRange(ftri.nrm.c[1], vnrm) ? vnrm[ftri.nrm.c[1]] : VecZero);
  226. nrm[2]=(InRange(ftri.nrm.c[2], vnrm) ? vnrm[ftri.nrm.c[2]] : VecZero); nrm+=3;
  227. }
  228. if(tex)
  229. {
  230. tex[0]=(InRange(ftri.tex.c[0], vtex) ? vtex[ftri.tex.c[0]] : 0);
  231. tex[1]=(InRange(ftri.tex.c[1], vtex) ? vtex[ftri.tex.c[1]] : 0);
  232. tex[2]=(InRange(ftri.tex.c[2], vtex) ? vtex[ftri.tex.c[2]] : 0); tex+=3;
  233. }
  234. (tri++)->set(vtxs, vtxs+1, vtxs+2); vtxs+=3;
  235. }
  236. FREPA(cur.quad)
  237. {
  238. FQuad &fquad=cur.quad[i];
  239. if(pos)
  240. {
  241. pos[0]=vpos[fquad.pos.c[0]];
  242. pos[1]=vpos[fquad.pos.c[1]];
  243. pos[2]=vpos[fquad.pos.c[2]];
  244. pos[3]=vpos[fquad.pos.c[3]]; pos+=4;
  245. }
  246. if(nrm)
  247. {
  248. nrm[0]=(InRange(fquad.nrm.c[0], vnrm) ? vnrm[fquad.nrm.c[0]] : VecZero);
  249. nrm[1]=(InRange(fquad.nrm.c[1], vnrm) ? vnrm[fquad.nrm.c[1]] : VecZero);
  250. nrm[2]=(InRange(fquad.nrm.c[2], vnrm) ? vnrm[fquad.nrm.c[2]] : VecZero);
  251. nrm[3]=(InRange(fquad.nrm.c[3], vnrm) ? vnrm[fquad.nrm.c[3]] : VecZero); nrm+=4;
  252. }
  253. if(tex)
  254. {
  255. tex[0]=(InRange(fquad.tex.c[0], vtex) ? vtex[fquad.tex.c[0]] : 0);
  256. tex[1]=(InRange(fquad.tex.c[1], vtex) ? vtex[fquad.tex.c[1]] : 0);
  257. tex[2]=(InRange(fquad.tex.c[2], vtex) ? vtex[fquad.tex.c[2]] : 0);
  258. tex[3]=(InRange(fquad.tex.c[3], vtex) ? vtex[fquad.tex.c[3]] : 0); tex+=4;
  259. }
  260. (quad++)->set(vtxs, vtxs+1, vtxs+2, vtxs+3); vtxs+=4;
  261. }
  262. mshb.weldVtx(VTX_ALL, EPSD, EPS_COL_COS, -1); // use small epsilon in case mesh is scaled down
  263. }
  264. mesh->mirrorX().setBox();
  265. REPA(*mesh)if(!mesh->parts[i].base.vtx.nrm())mesh->parts[i].base.setNormals();
  266. CleanMesh(*mesh);
  267. }
  268. // material indexes
  269. if(part_material_index)FREPA(groups)part_material_index.add(groups[i].material);
  270. return true;
  271. }
  272. return false;
  273. }
  274. /******************************************************************************/
  275. }
  276. /******************************************************************************/