Mesh Simplify.cpp 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. #define ALLOW_VTX_MOVE 2 // 0=off, 1=based on edge center, 2=edge center and custom quadric calculation
  4. #define ALLOW_PLANES 1 // 0=off, 1=store multiple planes per each vertex
  5. #define ADJUST_REFS 1 // if adjust references for all vertexes when deleting a triangle (if not then 'tri.exists' checks are performed)
  6. #define TEST_ORIGINAL_POS 1 // if test original position against the new triangle (or if false, test the new position against the old triangle)
  7. #define DEBUG_CHECK_REFS 0 // if perform debug checks that the references have been properly adjusted
  8. #define PROFILE 0 // if perform profiling, measuring which functions take how much time to complete
  9. #define VTX_NORMAL_QM 2.0f // value of 2 was chosen based on test results (it gave better quality)
  10. #define VTX_BORDER_QM 1.0f
  11. #if PROFILE
  12. #pragma message("!! Warning: This will slow down mesh simplification, use only for testing !!")
  13. enum PROFILE_TYPE
  14. {
  15. ADD,
  16. SIMPLIFY,
  17. INIT,
  18. FLIPPED,
  19. CALC_ERROR,
  20. RESET_ERROR,
  21. SET_VTX_DATA,
  22. ADD_VTX_DATA,
  23. TEST_TRIS,
  24. SET_NRM_TAN_BIN,
  25. STORE,
  26. PROFILE_NUM,
  27. };
  28. static Dbl Times[PROFILE_NUM];
  29. static CChar8 *Names[]=
  30. {
  31. "Add",
  32. "Simplify",
  33. " Init",
  34. " Flipped",
  35. " Calc Error",
  36. " Reset Error",
  37. " Set Vtx Data",
  38. " Add Vtx Data",
  39. " Test Tris",
  40. " Set Nrm Tan Bin",
  41. "Store",
  42. };
  43. struct Profile
  44. {
  45. Int type;
  46. Dbl time;
  47. void end() {Times[type]+=Time.curTime()-time;}
  48. void start() {time=Time.curTime();}
  49. Profile(Int type) {T.type=type; start();}
  50. ~Profile() {end();}
  51. };
  52. void ProfileEnd()
  53. {
  54. Str s;
  55. FREPA(Times)s.line()+=S+Names[i]+": "+Times[i]+'s';
  56. Exit(s);
  57. }
  58. #define PROF(x) Profile p(x);
  59. #define PROFILE_END ProfileEnd();
  60. #else
  61. #define PROF(x)
  62. #define PROFILE_END
  63. #endif
  64. /******************************************************************************
  65. Part of the algorithm is based on "Quadric Mesh Simplification" by Sven Forstmann:
  66. http://voxels.blogspot.com.au/2014/05/quadric-mesh-simplification-with-source.html
  67. /******************************************************************************/
  68. namespace EE{
  69. /******************************************************************************/
  70. typedef Dbl Real; // 'QuadricMatrix' requires high precision
  71. /******************************************************************************/
  72. struct QuadricMatrix
  73. {
  74. // http://www.bmsc.washington.edu/people/merritt/graphics/quadrics.html
  75. // F(x, y, z) = Ax2 + By2 + Cz2 + Dxy + Exz + Fyz + Gx + Hy + Iz + J = 0
  76. union
  77. {
  78. struct{Real m[10];};
  79. struct{Real a, d, e, g, b, f, h, c, i, j;}; // list in this order to match the above formula and 'error2' method
  80. };
  81. QuadricMatrix() {}
  82. QuadricMatrix(Real m11, Real m12, Real m13, Real m14,
  83. Real m22, Real m23, Real m24,
  84. Real m33, Real m34,
  85. Real m44)
  86. {
  87. m[0]=m11; m[1]=m12; m[2]=m13; m[3]=m14;
  88. m[4]=m22; m[5]=m23; m[6]=m24;
  89. m[7]=m33; m[8]=m34;
  90. m[9]=m44;
  91. }
  92. QuadricMatrix(C Vec &normal, Real plane_dist) // QuadricMatrix(n, d) == QuadricMatrix(-n, -d), because each component is multiplied by 1 other, and because a*b == (-a)*(-b), which means that plane normal sign is not important for 'QuadricMatrix'
  93. {
  94. Real x=normal.x, y=normal.y, z=normal.z, w=plane_dist;
  95. m[0]=x*x; m[1]=x*y; m[2]=x*z; m[3]=x*w;
  96. m[4]=y*y; m[5]=y*z; m[6]=y*w;
  97. m[7]=z*z; m[8]=z*w;
  98. m[9]=w*w;
  99. }
  100. QuadricMatrix(C Vec &normal, C Vec &pos) : QuadricMatrix(normal, -Dot(normal, pos)) {} // QuadricMatrix(n, p) == QuadricMatrix(-n, p)
  101. // get
  102. Real operator[](Int i)C {return m[i];}
  103. Real det(Int a11, Int a12, Int a13,
  104. Int a21, Int a22, Int a23,
  105. Int a31, Int a32, Int a33)C // Determinant
  106. {
  107. Real det=m[a11]*m[a22]*m[a33] + m[a13]*m[a21]*m[a32] + m[a12]*m[a23]*m[a31]
  108. -m[a13]*m[a22]*m[a31] - m[a11]*m[a23]*m[a32] - m[a12]*m[a21]*m[a33];
  109. return det;
  110. }
  111. Real error (C Vec &pos)C {return SqrtS(error2(pos));} // get error between position and Quadric (can be negative)
  112. Real error2(C Vec &pos)C // get squared error between position and Quadric (can be negative)
  113. {
  114. Real x=pos.x, y=pos.y, z=pos.z;
  115. return m[0]*x*x + 2*m[1]*x*y + 2*m[2]*x*z + 2*m[3]*x
  116. + m[4]*y*y + 2*m[5]*y*z + 2*m[6]*y
  117. + m[7]*z*z + 2*m[8]*z
  118. + m[9] ;
  119. }
  120. Vec normal (C Vec &pos)C {return !normalU(pos);} // calculate quadric surface normal at 'pos' position
  121. Vec normalU(C Vec &pos)C // calculate quadric surface unnormalized normal at 'pos' position
  122. {
  123. /* equal to partial derivatives of F with respect to x, y, z, which is (dF/dx, dF/dy, dF/dz)
  124. something like:
  125. Real d=0.000001, f=error(pos);
  126. return Vec((error(pos+Vec(d, 0, 0))-f)/d,
  127. (error(pos+Vec(0, d, 0))-f)/d,
  128. (error(pos+Vec(0, 0, d))-f)/d); */
  129. Real x=pos.x, y=pos.y, z=pos.z;
  130. #if 0 // formula assuming we don't use 2* in 'error2'
  131. return Vec(2*a*x + d*y + e*z + g,
  132. 2*b*y + d*x + f*z + h,
  133. 2*c*z + e*x + f*y + i);
  134. #elif 0 // formula assuming we use 2* in 'error2'
  135. return Vec(2*(a*x + d*y + e*z + g),
  136. 2*(b*y + d*x + f*z + h),
  137. 2*(c*z + e*x + f*y + i));
  138. #else // formula assuming we use 2* in 'error2' and with "2*" removed here, because this normal will be normalized
  139. return Vec(a*x + d*y + e*z + g,
  140. b*y + d*x + f*z + h,
  141. c*z + e*x + f*y + i);
  142. #endif
  143. }
  144. // operations
  145. void reset() {REPAO(m)=0;}
  146. QuadricMatrix operator+(C QuadricMatrix &n)C
  147. {
  148. return QuadricMatrix(m[0]+n[0], m[1]+n[1], m[2]+n[2], m[3]+n[3],
  149. m[4]+n[4], m[5]+n[5], m[6]+n[6],
  150. m[7]+n[7], m[8]+n[8],
  151. m[9]+n[9]);
  152. }
  153. QuadricMatrix operator*(Real r)C
  154. {
  155. return QuadricMatrix(m[0]*r, m[1]*r, m[2]*r, m[3]*r,
  156. m[4]*r, m[5]*r, m[6]*r,
  157. m[7]*r, m[8]*r,
  158. m[9]*r);
  159. }
  160. QuadricMatrix& operator+=(C QuadricMatrix &n)
  161. {
  162. m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3];
  163. m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6];
  164. m[7]+=n[7]; m[8]+=n[8];
  165. m[9]+=n[9];
  166. return T;
  167. }
  168. QuadricMatrix& operator*=(Real r) {REPAO(m)*=r; return T;}
  169. QuadricMatrix& operator/=(Real r) {REPAO(m)/=r; return T;}
  170. };
  171. /******************************************************************************/
  172. struct Simplify // must be used for a single 'simplify', after that it cannot be used (because the members aren't cleared)
  173. {
  174. struct Weights
  175. {
  176. Flt hlp, tex0, tex1, tex2, color, material, size; // no need to keep weight for pos (managed separately), nrm (will be normalized), skin (weights stored per bone/matrix)
  177. Weights() {}
  178. Weights(Flt weight)
  179. {
  180. hlp =weight;
  181. tex0 =weight;
  182. tex1 =weight;
  183. tex2 =weight;
  184. color =weight;
  185. material=weight;
  186. size =weight;
  187. }
  188. };
  189. struct VtxData
  190. {
  191. Vec hlp, nrm;
  192. Vec2 tex0, tex1, tex2;
  193. Vec4 color, material;
  194. VecB4 matrix, blend;
  195. Flt size;
  196. void from(C MeshBase &mshb, Int i)
  197. {
  198. VtxFull vtx; vtx.from(mshb, i);
  199. hlp=vtx.hlp;
  200. nrm=vtx.nrm;
  201. tex0=vtx.tex0;
  202. tex1=vtx.tex1;
  203. tex2=vtx.tex2;
  204. color=vtx.color.asVec4();
  205. material=vtx.material; material/=255;
  206. matrix=vtx.matrix;
  207. blend=vtx.blend;
  208. size=vtx.size;
  209. }
  210. void to(MeshBase &mshb, Int i)
  211. {
  212. VtxFull vtx;
  213. vtx.hlp=hlp;
  214. vtx.nrm=nrm;
  215. vtx.tex0=tex0;
  216. vtx.tex1=tex1;
  217. vtx.tex2=tex2;
  218. vtx.color=color;
  219. if(Flt sum=material.sum())material/=sum; Color mc=material; vtx.material.set(mc.r, mc.g, mc.b, mc.a);
  220. vtx.matrix=matrix;
  221. vtx.blend=blend;
  222. vtx.size=size;
  223. vtx.to(mshb, i);
  224. }
  225. void lerp(C VtxData &a, C VtxData &b, Flt step, UInt flag)
  226. {
  227. Flt step1=1-step;
  228. if(flag&VTX_TEX0 )tex0 = a.tex0 *step1 + b.tex0 *step;
  229. if(flag&VTX_COLOR )color = a.color *step1 + b.color *step;
  230. if(flag&VTX_MATERIAL)material= a.material*step1 + b.material*step;
  231. if(flag&VTX_NRM )nrm =!(a.nrm *step1 + b.nrm *step);
  232. if(flag&VTX_SKIN )
  233. {
  234. MemtN<IndexWeight, 256> skin;
  235. FREPA(a.matrix)skin.New().set(a.matrix.c[i], a.blend.c[i]*step1);
  236. FREPA(b.matrix)skin.New().set(b.matrix.c[i], b.blend.c[i]*step );
  237. SetSkin(skin, matrix, blend, null);
  238. }
  239. if(flag&(VTX_HLP|VTX_TEX1|VTX_TEX2|VTX_SIZE)) // uncommon
  240. {
  241. if(flag&VTX_HLP )hlp =a.hlp *step1 + b.hlp *step;
  242. if(flag&VTX_SIZE)size=a.size*step1 + b.size*step;
  243. if(flag&VTX_TEX1)tex1=a.tex1*step1 + b.tex1*step;
  244. if(flag&VTX_TEX2)tex2=a.tex2*step1 + b.tex2*step;
  245. }
  246. }
  247. void lerp(C VtxData &a, C VtxData &b, C VtxData &c, C Vec &blend, UInt flag)
  248. {
  249. if(flag&VTX_TEX0 )tex0 = a.tex0 *blend.x + b.tex0 *blend.y + c.tex0 *blend.z;
  250. if(flag&VTX_COLOR )color = a.color *blend.x + b.color *blend.y + c.color *blend.z;
  251. if(flag&VTX_MATERIAL)material= a.material*blend.x + b.material*blend.y + c.material*blend.z;
  252. if(flag&VTX_NRM )nrm =!(a.nrm *blend.x + b.nrm *blend.y + c.nrm *blend.z);
  253. if(flag&VTX_SKIN )
  254. {
  255. MemtN<IndexWeight, 256> skin;
  256. FREPA(a.matrix)skin.New().set(a.matrix.c[i], a.blend.c[i]*blend.x);
  257. FREPA(b.matrix)skin.New().set(b.matrix.c[i], b.blend.c[i]*blend.y);
  258. FREPA(c.matrix)skin.New().set(c.matrix.c[i], c.blend.c[i]*blend.z);
  259. SetSkin(skin, T.matrix, T.blend, null);
  260. }
  261. if(flag&(VTX_HLP|VTX_TEX1|VTX_TEX2|VTX_SIZE)) // uncommon
  262. {
  263. if(flag&VTX_HLP )hlp =a.hlp *blend.x + b.hlp *blend.y + c.hlp *blend.z;
  264. if(flag&VTX_SIZE)size=a.size*blend.x + b.size*blend.y + c.size*blend.z;
  265. if(flag&VTX_TEX1)tex1=a.tex1*blend.x + b.tex1*blend.y + c.tex1*blend.z;
  266. if(flag&VTX_TEX2)tex2=a.tex2*blend.x + b.tex2*blend.y + c.tex2*blend.z;
  267. }
  268. }
  269. void normalize(C Weights &weight, UInt flag)
  270. {
  271. // skin not handled here
  272. if(flag&VTX_NRM )nrm.normalize();
  273. if(flag&VTX_TEX0 )tex0 /=weight.tex0;
  274. if(flag&VTX_COLOR )color /=weight.color;
  275. if(flag&VTX_MATERIAL)material/=weight.material;
  276. if(flag&(VTX_HLP|VTX_TEX1|VTX_TEX2|VTX_SIZE)) // uncommon
  277. {
  278. if(flag&VTX_HLP )hlp /=weight.hlp;
  279. if(flag&VTX_TEX1)tex1/=weight.tex1;
  280. if(flag&VTX_TEX2)tex2/=weight.tex2;
  281. if(flag&VTX_SIZE)size/=weight.size;
  282. }
  283. }
  284. };
  285. struct Triangle
  286. {
  287. Real edge_error[3], error_min;
  288. VecI ind; // vertex index
  289. Vec nrm, tan, bin; // for simplification tangent/binormal are computed per triangle (not per vertex), tangent/binormal are not normalized
  290. Flt weight;
  291. VtxData vtxs[3], // data for each vertex
  292. vtx_new; // weighted average 'vtx_mid' for all tris, this is done per-component only if in 2 tris the shared vertexes data is the same
  293. Int part, // MeshPart index
  294. mtrl_group;
  295. UInt flag; // MSHB_FLAG, this is obtained from the MeshPart.base of that triangle
  296. Bool middle;
  297. #if !ADJUST_REFS
  298. Bool exists; // if references are not adjusted, then we need to keep information about which triangle exists and which got removed
  299. Triangle() {exists=true ;}
  300. ~Triangle() {exists=false;}
  301. #endif
  302. };
  303. struct TrianglePtr
  304. {
  305. Triangle *tri;
  306. VtxData vtx_mid, // data interpolated for the middle position and then multiplied by 'weight'
  307. // following 'vtx_*' members specify source triangle to copy from
  308. *vtx_hlp,
  309. *vtx_nrm,
  310. *vtx_tex0,
  311. *vtx_tex1,
  312. *vtx_tex2,
  313. *vtx_skin,
  314. *vtx_color,
  315. *vtx_material,
  316. *vtx_size;
  317. TrianglePtr& set(Triangle &tri) {T.tri=&tri; return T;}
  318. void clear() {vtx_hlp=vtx_nrm=vtx_tex0=vtx_tex1=vtx_tex2=vtx_skin=vtx_color=vtx_material=vtx_size=null;}
  319. };
  320. enum BORDER
  321. {
  322. BORDER_POS=1<<0,
  323. BORDER_TEX=1<<1,
  324. };
  325. struct Vertex
  326. {
  327. Vec pos;
  328. Int ref_start, tri_num;
  329. Byte border;
  330. QuadricMatrix qm;
  331. Real weight;
  332. #if ALLOW_PLANES
  333. struct Plane
  334. {
  335. Vec normal;
  336. Flt plane ;
  337. void scale(Flt scale) {normal*=scale; plane*=scale;}
  338. Flt error(C Vec &pos)C {return Abs(Dot(pos, normal)+plane);}
  339. #define EPS_SIMPLIFY_PLANE 0.001f
  340. Bool operator==(C Plane &src)C {return Abs(plane-src.plane)<=EPS_SIMPLIFY_PLANE && Dot(normal, src.normal)>=EPS_COL_COS;} // we could also check for T==-src (if they are co-planar), because normal sign of these planes is not important, however most likely planes will have the same sign, if they don't, then in worst case they will be listed twice, but it's better to don't check to avoid extra CPU overhead, because most likely they won't be
  341. };
  342. Memc<Plane> planes;
  343. void includePlane(C Plane &plane)
  344. {
  345. REPA(planes)if(planes[i]==plane)return; // if plane is similar, then don't add it
  346. planes.add(plane);
  347. }
  348. #endif
  349. void eat(Vertex &vtx)
  350. {
  351. border|=vtx.border;
  352. qm +=vtx.qm;
  353. weight+=vtx.weight;
  354. #if ALLOW_PLANES
  355. if(vtx.planes.elms()>planes.elms())Swap(vtx.planes, planes); // if 'vtx' uses more planes, then reuse its memory container, to avoid memory allocations
  356. REPA(vtx.planes)includePlane(vtx.planes[i]);
  357. vtx.planes.del(); // will no longer be needed
  358. #endif
  359. }
  360. };
  361. struct Ref
  362. {
  363. Int tri_abs , // absolute index of the triangle
  364. tri_vtx_index; // 0..2 (1 of 3 vertexes in triangle)
  365. };
  366. struct VtxDupMap : VtxDup
  367. {
  368. Int vtxs_index;
  369. Vec2 tex0;
  370. Int mtrl_group;
  371. };
  372. struct MtrlGroup
  373. {
  374. UInt flag;
  375. Ptr multi_mtrls[4];
  376. MtrlGroup(UInt flag=0, C MeshPart *part=null)
  377. {
  378. T.flag=flag;
  379. REPAO(multi_mtrls)=(part ? part->multiMaterial(i)() : null);
  380. }
  381. Bool operator==(C MtrlGroup &mg)C
  382. {
  383. const UInt mask=(VTX_POS|VTX_HLP|VTX_NRM|VTX_TEX_ALL|VTX_SIZE); // tan/bin not needed because they are recalculated, color not needed because defaulted to WHITE, material blend weight not needed because defaulted to (1,0,0,0), skin not needed because defaulted to bone=0 weight=1
  384. if((flag&mask)!=(mg.flag&mask))return false;
  385. REPA(multi_mtrls)if(multi_mtrls[i]!=mg.multi_mtrls[i])return false;
  386. return true;
  387. }
  388. Bool testAndMerge(C MtrlGroup &mg)
  389. {
  390. if(T==mg)
  391. {
  392. flag|=mg.flag;
  393. return true;
  394. }
  395. return false;
  396. }
  397. };
  398. Bool keep_border;
  399. MESH_SIMPLIFY mode;
  400. Int processed_tris, max_skin;
  401. UInt test_flag; // this is set based on the max tolerance parameters to the simplify function
  402. Flt max_uv2, max_color, max_material, max_normal;
  403. Box box;
  404. Memx<Triangle > tris;
  405. Memc<Vertex > vtxs;
  406. Memc<Ref > refs;
  407. Memc<VtxDupMap > vtx_dups;
  408. Memb<TrianglePtr > all_tris; // need 'Memb' because we're adding new elements and storing pointers
  409. Memc<TrianglePtr*> middle_tris; // usually this will be just 2 triangles, however it may be more
  410. Memc<MtrlGroup > mtrl_groups;
  411. Bool *_stop;
  412. Simplify(Bool *stop) : _stop(stop) {}
  413. Bool stop()C {return _stop && *_stop;}
  414. static Int CompareError(C Triangle &a, C Triangle &b) {return Compare(b.error_min, a.error_min);} // swap order to sort from highest to lowest
  415. // error for one edge
  416. #if ALLOW_PLANES
  417. static Flt PlanesError(C Vertex &vtx0, C Vertex &vtx1, C Vec &pos) // this is linear
  418. {
  419. Flt error=0;
  420. REPA(vtx0.planes)MAX(error, vtx0.planes[i].error(pos));
  421. REPA(vtx1.planes)MAX(error, vtx1.planes[i].error(pos));
  422. return error;
  423. }
  424. #endif
  425. Real calculateError(Int v0, Int v1, Vec &mid_pos) // TODO: make error calculation optional?
  426. {
  427. PROF(CALC_ERROR);
  428. // compute interpolated vertex
  429. C Vertex &vtx0=vtxs[v0],
  430. &vtx1=vtxs[v1];
  431. QuadricMatrix qm=vtx0.qm +vtx1.qm ;
  432. Real weight=vtx0.weight+vtx1.weight, error, det;
  433. if(keep_border) // if we want to keep border, and one of the vertexes is at the border, then we can merge only to that vtx (this method won't be called if both vertexes are at the border)
  434. {
  435. if(vtx0.border&BORDER_POS){mid_pos=vtx0.pos; goto calc_error;}
  436. if(vtx1.border&BORDER_POS){mid_pos=vtx1.pos; goto calc_error;}
  437. }
  438. det=qm.det(0, 1, 2, 1, 4, 5, 2, 5, 7);
  439. if(det && ALLOW_VTX_MOVE>=2) // q_delta is invertible, calculate a new mid position
  440. {
  441. mid_pos.x=-1/det*qm.det(1, 2, 3, 4, 5, 6, 5, 7, 8); // x=A41/det(q_delta)
  442. mid_pos.y= 1/det*qm.det(0, 2, 3, 1, 5, 6, 2, 7, 8); // y=A42/det(q_delta)
  443. mid_pos.z=-1/det*qm.det(0, 1, 3, 1, 4, 6, 2, 5, 8); // z=A43/det(q_delta)
  444. #if 0 // currently this is disabled, because we already adjust 'qm' and 'planes' for border vtx's, however if restored, then it needs to be analyzed, if there's no better way to do this (for example, case when vtx0 is BORDER_TEX, but vtx1 not, meaning that some other triangle with vtx0 but not with this edge will have tex surface stretched), and should it check for BORDER_POS/BORDER_TEX only/both?
  445. if(vtx0.border&vtx1.border) // if this is a border (tex or pos) then set the mid_pos on the line between the vertexes, to avoid moving them too much, which could introduce UV artifacts, do this also for BORDER_POS because edge middle pos based on quadrics could go completely outside of polygon range - having a /\ triangle, it could turn into /-----/
  446. mid_pos=NearestPointOnEdge(mid_pos, vtx0.pos, vtx1.pos); // this gave better results than "NearestPointOnStr(mid_pos, vtx0.pos, !(vtx1.pos-vtx0.pos))"
  447. #endif
  448. calc_error:
  449. #if ALLOW_PLANES
  450. if(mode==SIMPLIFY_PLANES)error=PlanesError(vtx0, vtx1, mid_pos);else
  451. #endif
  452. error=qm.error2(mid_pos)/weight; // since the 'qm' is scaled by triangle surface area, we need to restore it back to get normal scale
  453. }else // det=0, reuse existing position, find best result (one with smallest error)
  454. {
  455. Int error_min;
  456. mid_pos=Avg(vtx0.pos, vtx1.pos);
  457. #if ALLOW_PLANES
  458. if(mode==SIMPLIFY_PLANES)
  459. {
  460. Flt errors[]={PlanesError(vtx0, vtx1, vtx0.pos),
  461. PlanesError(vtx0, vtx1, vtx1.pos),
  462. PlanesError(vtx0, vtx1, mid_pos)};
  463. error_min=(ALLOW_VTX_MOVE ? MinI(errors[0], errors[1], errors[2]) : MinI(errors[0], errors[1]));
  464. error =errors[error_min];
  465. }else
  466. #endif
  467. {
  468. Real errors[]={qm.error2(vtx0.pos),
  469. qm.error2(vtx1.pos),
  470. qm.error2( mid_pos)};
  471. error_min=(ALLOW_VTX_MOVE ? MinI(errors[0], errors[1], errors[2]) : MinI(errors[0], errors[1]));
  472. error =errors[error_min]/weight; // since the 'qm' is scaled by triangle surface area, we need to restore it back to get normal scale
  473. }
  474. switch(error_min)
  475. {
  476. case 0: mid_pos=vtx0.pos; break;
  477. case 1: mid_pos=vtx1.pos; break;
  478. //case 2: mid_pos= mid_pos; break; this is NOP
  479. }
  480. }
  481. return error;
  482. }
  483. // check if triangle flips when this edge is removed
  484. Bool flipped(C Vec &mid_pos, Int edge_vtx1i, C Vertex &edge_vtx0, Bool add)
  485. {
  486. PROF(FLIPPED);
  487. REP(edge_vtx0.tri_num)
  488. {
  489. C Ref &ref=refs[edge_vtx0.ref_start+i];
  490. Triangle &tri=tris.absElm(ref.tri_abs);
  491. #if !ADJUST_REFS
  492. if(tri.exists)
  493. #endif
  494. {
  495. Int tvi =ref.tri_vtx_index,
  496. //tri_v0=tri.ind.c[ tvi ], this is always 'edge_vtx0i'
  497. tri_v1=tri.ind.c[(tvi+1)%3],
  498. tri_v2=tri.ind.c[(tvi+2)%3];
  499. if(tri.middle=(tri_v1==edge_vtx1i || tri_v2==edge_vtx1i)) // this is a middle tri
  500. {
  501. if(add)middle_tris.add(&all_tris.New().set(tri)); // we're adding middle tris only for the first vertex, to avoid adding middle triangles twice, from both vertexes
  502. }else
  503. {
  504. C Vec //&v0= edge_vtx0.pos,
  505. &v1=vtxs[tri_v1].pos,
  506. &v2=vtxs[tri_v2].pos;
  507. if(Dot(tri.nrm, GetNormalU(mid_pos, v1, v2))<=0)return true; // if the new triangle normal is in the opposite direction of the original normal
  508. all_tris.New().set(tri);
  509. }
  510. }
  511. }
  512. return false;
  513. }
  514. static inline void AddSkin(MemtN<IndexWeight, 256> &skin, C VecB4 &matrix, C VecB4 &blend, Flt weight)
  515. {
  516. REPA(matrix){Flt w=blend.c[i]*weight; if(w>0)skin.New().set(matrix.c[i], w);}
  517. }
  518. static Bool EqualSkin(C VecB4 &m0, C VecB4 &w0, C VecB4 &m1, C VecB4 &w1)
  519. {
  520. return m0==m1 && w0==w1; // Warning: normally we should check every matrix index separately, in case for example m0.x==m1.y (different order, and that would be much slower), however 'SetSkin' was designed to sort matrix/weight so they can be compared in this fast way
  521. }
  522. static inline Int GetBlend(Byte index, VecB4 matrix, VecB4 blend) // find blend value for 'index' in 'matrix' 'blend' skinning
  523. {
  524. Int b=0; REPA(matrix)if(matrix.c[i]==index)b+=blend.c[i];
  525. return b;
  526. }
  527. static Int SkinDifference(VecB4 matrix_a, VecB4 blend_a, VecB4 matrix_b, VecB4 blend_b)
  528. {
  529. Int dif=0;
  530. REP(4)
  531. {
  532. if(Byte b=blend_a.c[i])MAX(dif, Abs(b-GetBlend(matrix_a.c[i], matrix_b, blend_b))); // get difference between i-th A bone
  533. if(Byte b=blend_b.c[i])MAX(dif, Abs(b-GetBlend(matrix_b.c[i], matrix_a, blend_a))); // get difference between i-th B bone
  534. }
  535. return dif;
  536. }
  537. void addVtxData(TrianglePtr &trip, Int v0, Int v1)
  538. {
  539. PROF(ADD_VTX_DATA);
  540. Triangle &tri=*trip.tri;
  541. // start with self
  542. Weights weight(tri.weight); MemtN<IndexWeight, 256> skin;
  543. tri.vtx_new=trip.vtx_mid; // 'vtx_mid' is already multiplied by 'tri.weight'
  544. if(tri.flag&VTX_SKIN)AddSkin(skin, trip.vtx_mid.matrix, trip.vtx_mid.blend, tri.weight);
  545. // check all tris
  546. REPA(all_tris) // iterate all tris
  547. {
  548. TrianglePtr &trip2=all_tris[i]; if(&trip!=&trip2) // skip self, remember that 'tri2' can be middle too, just like 'tri'
  549. {
  550. Triangle &tri2=*trip2.tri;
  551. UInt add_flag=(tri.flag&tri2.flag);
  552. if( add_flag&VTX_TEX_ALL) // if we want to merge tex coordinates
  553. {
  554. if(tri.mtrl_group!=tri2.mtrl_group // we can merge tex only if materials match
  555. || Dot(tri.tan, tri2.tan)<=0 // and triangles don't have mirrored tex
  556. || Dot(tri.bin, tri2.bin)<=0) // and triangles don't have mirrored tex
  557. FlagDisable(add_flag, VTX_TEX_ALL); // can't merge tex
  558. }
  559. if(tri.mtrl_group!=tri2.mtrl_group)FlagDisable(add_flag, VTX_MATERIAL); // we can merge material only if materials match
  560. // check if shared vertexes match, remember that both 'tri' and 'tri2' can be middle, in which case, they will share 2 vertexes and not 1, so check all shared
  561. REPAD(tvi, tri.ind)
  562. {
  563. const Int tv=tri.ind.c[tvi];
  564. if(tv==v0 || tv==v1) // test only edge vertexes, this is to ignore other shared vertexes between the tris which are not on the edge to be removed, this will allow to simplify the mesh more
  565. REPAD(tvi2, tri2.ind)
  566. {
  567. const Int tv2=tri2.ind.c[tvi2];
  568. if(tv==tv2) // if the same vtx
  569. {
  570. C VtxData &v=tri.vtxs[tvi], &v2=tri2.vtxs[tvi2];
  571. // if any of the vtx components isn't equal in both triangles, then we can't merge
  572. if((add_flag&VTX_NRM ) && !Equal (v.nrm , v2.nrm ))FlagDisable(add_flag, VTX_NRM );
  573. if((add_flag&VTX_TEX0 ) && !Equal (v.tex0 , v2.tex0 ))FlagDisable(add_flag, VTX_TEX0 );
  574. if((add_flag&VTX_COLOR ) && !Equal (v.color , v2.color ))FlagDisable(add_flag, VTX_COLOR );
  575. if((add_flag&VTX_MATERIAL) && !Equal (v.material, v2.material ))FlagDisable(add_flag, VTX_MATERIAL);
  576. if((add_flag&VTX_SKIN ) && !EqualSkin(v.matrix, v.blend, v2.matrix, v2.blend))FlagDisable(add_flag, VTX_SKIN );
  577. if( add_flag&(VTX_HLP|VTX_TEX1|VTX_TEX2|VTX_SIZE)) // uncommon
  578. {
  579. if((add_flag&VTX_HLP ) && !Equal(v.hlp , v2.hlp ))FlagDisable(add_flag, VTX_HLP );
  580. if((add_flag&VTX_TEX1) && !Equal(v.tex1, v2.tex1))FlagDisable(add_flag, VTX_TEX1);
  581. if((add_flag&VTX_TEX2) && !Equal(v.tex2, v2.tex2))FlagDisable(add_flag, VTX_TEX2);
  582. if((add_flag&VTX_SIZE) && !Equal(v.size, v2.size))FlagDisable(add_flag, VTX_SIZE);
  583. }
  584. }
  585. }
  586. }
  587. // merge
  588. if(add_flag&VTX_NRM ){tri.vtx_new.nrm +=trip2.vtx_mid.nrm ; trip2.vtx_nrm =&tri.vtx_new;}
  589. if(add_flag&VTX_TEX0 ){tri.vtx_new.tex0 +=trip2.vtx_mid.tex0 ; trip2.vtx_tex0 =&tri.vtx_new; weight.tex0 +=tri2.weight;}
  590. if(add_flag&VTX_COLOR ){tri.vtx_new.color +=trip2.vtx_mid.color ; trip2.vtx_color =&tri.vtx_new; weight.color +=tri2.weight;}
  591. if(add_flag&VTX_MATERIAL){tri.vtx_new.material+=trip2.vtx_mid.material; trip2.vtx_material=&tri.vtx_new; weight.material+=tri2.weight;}
  592. if(add_flag&VTX_SKIN ){AddSkin(skin, trip2.vtx_mid.matrix, trip2.vtx_mid.blend, tri2.weight); trip2.vtx_skin=&tri.vtx_new;}
  593. if(add_flag&(VTX_HLP|VTX_TEX1|VTX_TEX2|VTX_SIZE)) // uncommon
  594. {
  595. if(add_flag&VTX_HLP ){tri.vtx_new.hlp +=trip2.vtx_mid.hlp ; trip2.vtx_hlp =&tri.vtx_new; weight.hlp +=tri2.weight;}
  596. if(add_flag&VTX_TEX1){tri.vtx_new.tex1+=trip2.vtx_mid.tex1; trip2.vtx_tex1=&tri.vtx_new; weight.tex1+=tri2.weight;}
  597. if(add_flag&VTX_TEX2){tri.vtx_new.tex2+=trip2.vtx_mid.tex2; trip2.vtx_tex2=&tri.vtx_new; weight.tex2+=tri2.weight;}
  598. if(add_flag&VTX_SIZE){tri.vtx_new.size+=trip2.vtx_mid.size; trip2.vtx_size=&tri.vtx_new; weight.size+=tri2.weight;}
  599. }
  600. }
  601. }
  602. // normalize
  603. tri.vtx_new.normalize(weight, tri.flag);
  604. SetSkin(skin, tri.vtx_new.matrix, tri.vtx_new.blend, null);
  605. }
  606. Bool setVtxData(TrianglePtr &trip, Int v0, Int v1)
  607. {
  608. PROF(SET_VTX_DATA);
  609. Triangle &tri=*trip.tri;
  610. if(tri.middle)return true; // middle tris have already been calculated
  611. UInt set_flag=tri.flag; // get what we need to calculate
  612. if( (set_flag&VTX_TEX0) && !trip.vtx_tex0)return false; // if this triangle has TEX0 but we haven't found any middle triangle match, then we can't proceed
  613. // try getting from middle triangle if we've got a connection (prefer middle because this is already calculated, and from all triangles, not just one side)
  614. if(trip.vtx_hlp ){tri.vtx_new.hlp =trip.vtx_hlp ->hlp ; FlagDisable(set_flag, VTX_HLP );}
  615. if(trip.vtx_nrm ){tri.vtx_new.nrm =trip.vtx_nrm ->nrm ; FlagDisable(set_flag, VTX_NRM );}
  616. if(trip.vtx_tex0 ){tri.vtx_new.tex0 =trip.vtx_tex0 ->tex0 ; FlagDisable(set_flag, VTX_TEX0 );}
  617. if(trip.vtx_tex1 ){tri.vtx_new.tex1 =trip.vtx_tex1 ->tex1 ; FlagDisable(set_flag, VTX_TEX1 );}
  618. if(trip.vtx_tex2 ){tri.vtx_new.tex2 =trip.vtx_tex2 ->tex2 ; FlagDisable(set_flag, VTX_TEX2 );}
  619. if(trip.vtx_skin ){tri.vtx_new.matrix =trip.vtx_skin ->matrix ; tri.vtx_new.blend=trip.vtx_skin->blend; FlagDisable(set_flag, VTX_SKIN );}
  620. if(trip.vtx_color ){tri.vtx_new.color =trip.vtx_color ->color ; FlagDisable(set_flag, VTX_COLOR );}
  621. if(trip.vtx_material){tri.vtx_new.material=trip.vtx_material->material; FlagDisable(set_flag, VTX_MATERIAL);}
  622. if(trip.vtx_size ){tri.vtx_new.size =trip.vtx_size ->size ; FlagDisable(set_flag, VTX_SIZE );}
  623. if(set_flag&(VTX_HLP|VTX_NRM|VTX_TEX_ALL|VTX_SKIN|VTX_COLOR|VTX_MATERIAL|VTX_SIZE)) // if we still have something to process
  624. {
  625. Weights weight; MemtN<IndexWeight, 256> skin;
  626. // start with self
  627. if(set_flag&VTX_HLP ){tri.vtx_new.hlp =trip.vtx_mid.hlp ; weight.hlp =tri.weight;}
  628. if(set_flag&VTX_NRM ){tri.vtx_new.nrm =trip.vtx_mid.nrm ;}
  629. if(set_flag&VTX_TEX0 ){tri.vtx_new.tex0 =trip.vtx_mid.tex0 ; weight.tex0 =tri.weight;}
  630. if(set_flag&VTX_TEX1 ){tri.vtx_new.tex1 =trip.vtx_mid.tex1 ; weight.tex1 =tri.weight;}
  631. if(set_flag&VTX_TEX2 ){tri.vtx_new.tex2 =trip.vtx_mid.tex2 ; weight.tex2 =tri.weight;}
  632. if(set_flag&VTX_COLOR ){tri.vtx_new.color =trip.vtx_mid.color ; weight.color =tri.weight;}
  633. if(set_flag&VTX_MATERIAL){tri.vtx_new.material=trip.vtx_mid.material; weight.material=tri.weight;}
  634. if(set_flag&VTX_SIZE ){tri.vtx_new.size =trip.vtx_mid.size ; weight.size =tri.weight;}
  635. if(set_flag&VTX_SKIN )AddSkin(skin, trip.vtx_mid.matrix, trip.vtx_mid.blend, tri.weight);
  636. REPAD(tvi, tri.ind)
  637. {
  638. const Int tv=tri.ind.c[tvi];
  639. if(tv==v0 || tv==v1) // test only edge vertexes, this is to ignore other shared vertexes between the tris which are not on the edge to be removed, this will allow to simplify the mesh more
  640. {
  641. VtxData &v=tri.vtxs[tvi];
  642. REPA(all_tris)
  643. {
  644. C TrianglePtr &trip2=all_tris[i]; if(&trip!=&trip2)
  645. {
  646. C Triangle &tri2=*trip2.tri; REPAD(tvi2, tri2.ind)
  647. {
  648. const Int tv2=tri2.ind.c[tvi2];
  649. if(tv==tv2) // if the same vtx
  650. {
  651. UInt add_flag=(set_flag&tri2.flag);
  652. if( add_flag&VTX_TEX_ALL) // if we want to merge tex coordinates
  653. {
  654. if(tri.mtrl_group!=tri2.mtrl_group // we can merge tex only if materials match
  655. || Dot(tri.tan, tri2.tan)<=0 // and triangles don't have mirrored tex
  656. || Dot(tri.bin, tri2.bin)<=0) // and triangles don't have mirrored tex
  657. FlagDisable(add_flag, VTX_TEX_ALL); // can't merge tex
  658. }
  659. C VtxData &v2=tri2.vtxs[tvi2];
  660. // merge
  661. if((add_flag&VTX_NRM ) && Equal (v.nrm , v2.nrm )){tri.vtx_new.nrm +=trip2.vtx_mid.nrm ;}
  662. if((add_flag&VTX_TEX0 ) && Equal (v.tex0 , v2.tex0 )){tri.vtx_new.tex0 +=trip2.vtx_mid.tex0 ; weight.tex0 +=tri2.weight;}
  663. if((add_flag&VTX_COLOR ) && Equal (v.color , v2.color )){tri.vtx_new.color +=trip2.vtx_mid.color ; weight.color +=tri2.weight;}
  664. if((add_flag&VTX_MATERIAL) && tri.mtrl_group==tri2.mtrl_group && Equal (v.material, v2.material )){tri.vtx_new.material+=trip2.vtx_mid.material; weight.material+=tri2.weight;}
  665. if((add_flag&VTX_SKIN ) && EqualSkin(v.matrix, v.blend, v2.matrix, v2.blend))AddSkin(skin, trip2.vtx_mid.matrix, trip2.vtx_mid.blend, tri2.weight);
  666. if( add_flag&(VTX_HLP|VTX_TEX1|VTX_TEX2|VTX_SIZE)) // uncommon
  667. {
  668. if((add_flag&VTX_HLP ) && Equal(v.hlp , v2.hlp )){tri.vtx_new.hlp +=trip2.vtx_mid.hlp ; weight.hlp +=tri2.weight;}
  669. if((add_flag&VTX_TEX1) && Equal(v.tex1, v2.tex1)){tri.vtx_new.tex1+=trip2.vtx_mid.tex1; weight.tex1+=tri2.weight;}
  670. if((add_flag&VTX_TEX2) && Equal(v.tex2, v2.tex2)){tri.vtx_new.tex2+=trip2.vtx_mid.tex2; weight.tex2+=tri2.weight;}
  671. if((add_flag&VTX_SIZE) && Equal(v.size, v2.size)){tri.vtx_new.size+=trip2.vtx_mid.size; weight.size+=tri2.weight;}
  672. }
  673. break;
  674. }
  675. }
  676. }
  677. }
  678. break;
  679. }
  680. }
  681. // normalize
  682. tri.vtx_new.normalize(weight, set_flag);
  683. if(set_flag&VTX_SKIN)SetSkin(skin, tri.vtx_new.matrix, tri.vtx_new.blend, null);
  684. }
  685. return true;
  686. }
  687. Bool testTriangles(C Vertex &vtx, C Vec &mid_pos) // test if the new triangle data will not be too far from the original, specifically if moving the old vtx into new location will not introduce too much noise
  688. {
  689. PROF(TEST_TRIS);
  690. REP(vtx.tri_num)
  691. {
  692. C Ref &ref=refs[vtx.ref_start+i];
  693. Triangle &tri=tris.absElm(ref.tri_abs);
  694. #if !ADJUST_REFS
  695. if(tri.exists)
  696. #endif
  697. if(!tri.middle) // we don't need to check middle triangles
  698. {
  699. UInt test_flag=(tri.flag&T.test_flag);
  700. C VtxData &vtx_n= tri.vtx_new;
  701. if(TEST_ORIGINAL_POS) // calculate original position in the new triangle
  702. {
  703. const Int tvi =ref.tri_vtx_index,
  704. tvi1 =(tvi+1)%3,
  705. tvi2 =(tvi+2)%3,
  706. tri_v1=tri.ind.c[tvi1],
  707. tri_v2=tri.ind.c[tvi2];
  708. C VtxData &vtx0 =tri.vtxs[tvi ],
  709. &vtx1 =tri.vtxs[tvi1],
  710. &vtx2 =tri.vtxs[tvi2];
  711. C Tri new_tri(mid_pos, vtxs[tri_v1].pos, vtxs[tri_v2].pos); // this is what's going to be after merging
  712. C Vec blend=TriBlend(vtx.pos, new_tri, false);
  713. if(test_flag&VTX_TEX0)
  714. {
  715. Vec2 got_tex=vtx_n.tex0*blend.x + vtx1.tex0*blend.y + vtx2.tex0*blend.z;
  716. if(Dist2(got_tex, vtx0.tex0)>max_uv2)return false;
  717. }
  718. if(test_flag&(VTX_TEX1|VTX_TEX2))
  719. {
  720. if(test_flag&VTX_TEX1)
  721. {
  722. Vec2 got_tex=vtx_n.tex1*blend.x + vtx1.tex1*blend.y + vtx2.tex1*blend.z;
  723. if(Dist2(got_tex, vtx0.tex1)>max_uv2)return false;
  724. }
  725. if(test_flag&VTX_TEX2)
  726. {
  727. Vec2 got_tex=vtx_n.tex2*blend.x + vtx1.tex2*blend.y + vtx2.tex2*blend.z;
  728. if(Dist2(got_tex, vtx0.tex2)>max_uv2)return false;
  729. }
  730. }
  731. if(test_flag&VTX_COLOR)
  732. {
  733. Vec4 got_col=vtx_n.color*blend.x + vtx1.color*blend.y + vtx2.color*blend.z;
  734. if((vtx0.color-got_col).abs().max()>max_color)return false;
  735. }
  736. if(test_flag&VTX_MATERIAL)
  737. {
  738. Vec4 got_material=vtx_n.material*blend.x + vtx1.material*blend.y + vtx2.material*blend.z;
  739. if((vtx0.material-got_material).abs().max()>max_material)return false;
  740. }
  741. if(test_flag&VTX_NRM)
  742. {
  743. Vec got_nrm=vtx_n.nrm*blend.x + vtx1.nrm*blend.y + vtx2.nrm*blend.z;
  744. if(CosBetween(got_nrm, vtx0.nrm)<max_normal)return false;
  745. }
  746. if(test_flag&VTX_SKIN)
  747. {
  748. MemtN<IndexWeight, 256> got_skin;
  749. AddSkin(got_skin, vtx_n.matrix, vtx_n.blend, blend.x);
  750. AddSkin(got_skin, vtx1 .matrix, vtx1 .blend, blend.y);
  751. AddSkin(got_skin, vtx2 .matrix, vtx2 .blend, blend.z);
  752. VecB4 bone_index, bone_weight; SetSkin(got_skin, bone_index, bone_weight, null);
  753. if(SkinDifference(bone_index, bone_weight, vtx0.matrix, vtx0.blend)>max_skin)return false;
  754. }
  755. }else // calculate new position in the old triangle
  756. {
  757. Tri old_tri(vtxs[tri.ind.x].pos, vtxs[tri.ind.y].pos, vtxs[tri.ind.z].pos, &tri.nrm);
  758. Vec blend=TriBlend(mid_pos, old_tri, false);
  759. if(test_flag&VTX_TEX0)
  760. {
  761. Vec2 expected_tex=tri.vtxs[0].tex0*blend.x + tri.vtxs[1].tex0*blend.y + tri.vtxs[2].tex0*blend.z;
  762. if(Dist2(expected_tex, vtx_n.tex0)>max_uv2)return false;
  763. }
  764. if(test_flag&(VTX_TEX1|VTX_TEX2))
  765. {
  766. if(test_flag&VTX_TEX1)
  767. {
  768. Vec2 expected_tex=tri.vtxs[0].tex1*blend.x + tri.vtxs[1].tex1*blend.y + tri.vtxs[2].tex1*blend.z;
  769. if(Dist2(expected_tex, vtx_n.tex1)>max_uv2)return false;
  770. }
  771. if(test_flag&VTX_TEX2)
  772. {
  773. Vec2 expected_tex=tri.vtxs[0].tex2*blend.x + tri.vtxs[1].tex2*blend.y + tri.vtxs[2].tex2*blend.z;
  774. if(Dist2(expected_tex, vtx_n.tex2)>max_uv2)return false;
  775. }
  776. }
  777. if(test_flag&VTX_COLOR)
  778. {
  779. Vec4 expected_col=tri.vtxs[0].color*blend.x + tri.vtxs[1].color*blend.y + tri.vtxs[2].color*blend.z;
  780. if((vtx_n.color-expected_col).abs().max()>max_color)return false;
  781. }
  782. if(test_flag&VTX_MATERIAL)
  783. {
  784. Vec4 expected_material=tri.vtxs[0].material*blend.x + tri.vtxs[1].material*blend.y + tri.vtxs[2].material*blend.z;
  785. if((vtx_n.material-expected_material).abs().max()>max_material)return false;
  786. }
  787. if(test_flag&VTX_NRM)
  788. {
  789. Vec expected_nrm=tri.vtxs[0].nrm*blend.x + tri.vtxs[1].nrm*blend.y + tri.vtxs[2].nrm*blend.z;
  790. if(CosBetween(expected_nrm, vtx_n.nrm)<max_normal)return false;
  791. }
  792. if(test_flag&VTX_SKIN)
  793. {
  794. MemtN<IndexWeight, 256> expected_skin;
  795. AddSkin(expected_skin, tri.vtxs[0].matrix, tri.vtxs[0].blend, blend.x);
  796. AddSkin(expected_skin, tri.vtxs[1].matrix, tri.vtxs[1].blend, blend.y);
  797. AddSkin(expected_skin, tri.vtxs[2].matrix, tri.vtxs[2].blend, blend.z);
  798. VecB4 bone_index, bone_weight; SetSkin(expected_skin, bone_index, bone_weight, null);
  799. if(SkinDifference(bone_index, bone_weight, vtx_n.matrix, vtx_n.blend)>max_skin)return false;
  800. }
  801. }
  802. }
  803. }
  804. return true;
  805. }
  806. void adjustRefs(Triangle &tri, Int v0, Int v1) // this removes the 'tri' reference from its vertexes
  807. {
  808. Int tri_abs=tris.absIndex(&tri);
  809. REPA(tri.ind) // iterate all vtxs of the tri
  810. {
  811. Int v=tri.ind.c[i]; if(v!=v0 && v!=v1) // we only need to process the 3rd vtx, because 'v0' will be adjusted, and 'v1' will be removed
  812. {
  813. Vertex &vtx=vtxs[v]; REP(vtx.tri_num) // iterate all triangles of this vtx
  814. {
  815. Ref &ref=refs[vtx.ref_start+i]; if(ref.tri_abs==tri_abs) // if the tri is this one
  816. {
  817. if(InRange(i+1, vtx.tri_num)) // if there is an element after the one being removed
  818. {
  819. #if 1 // faster
  820. ref=refs[vtx.ref_start+vtx.tri_num-1]; // move the last one to the removed one
  821. #else
  822. refs.moveElm(vtx.ref_start+i, vtx.ref_start+vtx.tri_num-1); // move ref to the last position
  823. #endif
  824. }
  825. vtx.tri_num--;
  826. break; // we have found this triangle, finish
  827. }
  828. }
  829. break; // if we've found that vtx, then the job is done
  830. }
  831. }
  832. }
  833. void resetError(Triangle &tri, Int valid)
  834. {
  835. PROF(RESET_ERROR);
  836. Real error_min=tri.error_min=Min(tri.edge_error[0], tri.edge_error[1], tri.edge_error[2]);
  837. // reposition this triangle in the 'tris' list to preserve order by 'error_min'
  838. #if 1 // binary search
  839. Int elms=Min(tris.elms(), processed_tris), // we can ignore moving to the right of what we've already processed
  840. l=0, r=elms-1;
  841. if(InRange(valid-1, elms) && tris[valid-1].error_min>=error_min)l=valid; // left ok
  842. if(InRange(valid+1, elms) && tris[valid+1].error_min<=error_min)r=valid; // right ok
  843. if(l>=r)return;
  844. for(; l<=r; )
  845. {
  846. Int mid=UInt(l+r)/2;
  847. if( mid==valid) // ignore this element
  848. {
  849. if(mid+1<=r)mid++;else
  850. if(mid-1>=l)mid--;else
  851. return; // we can't move to the right, and can't move to the left, this means that we've limited the range to the original valid index
  852. }
  853. Real e=tris[mid].error_min;
  854. if(e<error_min)r=mid-1;else
  855. if(e>error_min)l=mid+1;else
  856. { // values are the same, however since there can be many same values, then let's move towards the original 'valid' index, so that 'moveElm' can be shortest
  857. if(valid>mid)l=mid+1; // valid is after mid, so try moving right (adjust left boundary)
  858. else r=mid-1; // valid is before mid, so try moving left (adjust right boundary)
  859. }
  860. }
  861. tris.moveElm(valid, (valid<l) ? r : l);
  862. #else // iterative method
  863. Int target=valid;
  864. for(Int range=Min(tris.elms(), processed_tris); ; ) // we can ignore moving to the right of what we've already processed
  865. {
  866. Int next_i=target+1; if(!InRange(next_i, range))break;
  867. C Triangle &next=tris[next_i]; if(next.error_min<=error_min)break; // if OK then stop
  868. target=next_i;
  869. }
  870. for(;;)
  871. {
  872. Int next_i=target-1; if(!InRange(next_i, tris))break;
  873. C Triangle &next=tris[next_i]; if(next.error_min>=error_min)break; // if OK then stop
  874. target=next_i;
  875. }
  876. tris.moveElm(valid, target);
  877. #endif
  878. }
  879. void setNrmTanBin(Triangle &tri)
  880. {
  881. PROF(SET_NRM_TAN_BIN);
  882. C Vec &v0 =vtxs[tri.ind.c[0]].pos,
  883. v10=vtxs[tri.ind.c[1]].pos-v0,
  884. v20=vtxs[tri.ind.c[2]].pos-v0;
  885. // calculate normal
  886. tri.nrm =GetNormalU(v10, v20);
  887. tri.weight=Max(tri.nrm.normalize(), FLT_MIN); // maximize because later there is division by weight, and this will avoid div by zero, this affects both the 'tri.weight' and 'vtx.weight' (which is a sum of all vtx triangle weights)
  888. // calculate tangent and binormal
  889. if(tri.flag&VTX_TEX0)
  890. {
  891. Vec2 tx =tri.vtxs[1].tex0-tri.vtxs[0].tex0,
  892. ty =tri.vtxs[2].tex0-tri.vtxs[0].tex0;
  893. Flt tx_len=tx.normalize(),
  894. ty_len=ty.normalize();
  895. Vec2 ty_n =Perp(ty);
  896. Flt tx_v =-Dot(tx, ty_n);
  897. if(Abs(tx_v)>EPS)
  898. {
  899. tx_v=ty_n.x/tx_v;
  900. Flt ty_v=Dot(tx, ty)*tx_v+ty.x;
  901. tx_v=-tx_v;
  902. if(tx_len)tx_v/=tx_len;
  903. if(ty_len)ty_v/=ty_len;
  904. tri.tan=v10*tx_v + v20*ty_v;
  905. }else tri.tan.zero();
  906. Vec2 tx_n=Perp(tx);
  907. Flt ty_v=-Dot(ty, tx_n);
  908. if(Abs(ty_v)>EPS)
  909. {
  910. ty_v=tx_n.y/ty_v;
  911. Flt tx_v=Dot(ty, tx)*ty_v+tx.y;
  912. ty_v=-ty_v;
  913. if(tx_len)tx_v/=tx_len;
  914. if(ty_len)ty_v/=ty_len;
  915. tri.bin=v10*tx_v + v20*ty_v;
  916. }else tri.bin.zero();
  917. }
  918. }
  919. // update triangle connections and edge error after an edge is collapsed
  920. void updateTriangles(Int vtx_collapse, C Vertex &vtx) // 'vtx_collapse'=vertex to which we are collapsing
  921. {
  922. REP(vtx.tri_num)
  923. {
  924. C Ref &ref=refs[vtx.ref_start+i];
  925. Triangle &tri=tris.absElm(ref.tri_abs);
  926. #if !ADJUST_REFS
  927. if(tri.exists)
  928. #endif
  929. if(!tri.middle) // we don't need to update middle triangles as they are removed
  930. {
  931. Vec mid_pos;
  932. tri.ind.c[ref.tri_vtx_index]=vtx_collapse;
  933. tri.vtxs [ref.tri_vtx_index]=tri.vtx_new;
  934. setNrmTanBin(tri);
  935. tri.edge_error[0]=calculateError(tri.ind.c[0], tri.ind.c[1], mid_pos);
  936. tri.edge_error[1]=calculateError(tri.ind.c[1], tri.ind.c[2], mid_pos);
  937. tri.edge_error[2]=calculateError(tri.ind.c[2], tri.ind.c[0], mid_pos);
  938. resetError(tri, tris.absToValidIndex(ref.tri_abs));
  939. refs.add(Ref(ref)); // !! add as temp variable, because 'add' can cause 'ref' mem address to be invalid
  940. }
  941. }
  942. }
  943. struct VtxConnection
  944. {
  945. Int vtx , // index of the other vertex
  946. tris ; // how many tris reference this edge
  947. C Triangle *tri ; // pointer to the first triangle that has this edge, TODO: this could be a list of all triangles, however we would need a memory container, and in most cases it's not needed
  948. Bool mirror; // mirrored tex between 2 triangles
  949. void init(Int vtx, C Triangle &tri) {T.vtx=vtx; T.tris=1; T.tri=&tri; mirror=false;}
  950. };
  951. void addEdge(Memt<VtxConnection> &edges, Int vtx_index, C Triangle &tri)
  952. {
  953. REPA(edges) // check if this edge was already created by another tri
  954. {
  955. VtxConnection &edge=edges[i]; if(edge.vtx==vtx_index) // found
  956. {
  957. // mark that this edge is referenced by 1 more triangle
  958. C Triangle &edge_tri=*edge.tri;
  959. if(Dot(edge_tri.nrm, tri.nrm)>-EPS_COL_COS) // but only if the other triangle is not mirror co-planar (can happen for meshes which have 2 sided triangles), if it is mirror co-planar, then we don't want to increase the counter, but keep at 1, so the edge is detected as a border
  960. {
  961. edge.tris++;
  962. if(edge_tri.flag&tri.flag&VTX_TEX_ALL)
  963. if(Dot(edge_tri.tan, tri.tan)<=0 // and triangles don't have mirrored tex
  964. || Dot(edge_tri.bin, tri.bin)<=0) // and triangles don't have mirrored tex
  965. edge.mirror=true;
  966. }
  967. return;
  968. }
  969. }
  970. edges.New().init(vtx_index, tri); // create a new edge
  971. }
  972. struct TriLink
  973. {
  974. C Triangle *tri;
  975. VecI2 p ; // indexes of other vertexes
  976. void set(C Triangle &tri, Int v1, Int v2) {T.tri=&tri; p.set(v1, v2);}
  977. };
  978. void processVtxs() // detect which vertexes are on the border
  979. {
  980. Memt<VtxConnection> edges; // list of all 'vtx' neighbors, making a list of edges from 'vtx' to its 'neighbor'
  981. Memt<TriLink > tris_left; // list of triangles
  982. REPAD(vi, vtxs)
  983. {
  984. Vertex &vtx=vtxs[vi];
  985. edges.clear(); tris_left.clear();
  986. REP(vtx.tri_num) // iterate all tris touching this vertex
  987. {
  988. C Ref &ref=refs[vtx.ref_start+i];
  989. C Triangle &tri=tris.absElm(ref.tri_abs);
  990. Int tvi =ref.tri_vtx_index,
  991. //tri_v0=tri.ind.c[ tvi ], this is always 'vi', and we don't need to process it, because we're not interested in 'vi'<->'vi' connection
  992. tri_v1=tri.ind.c[(tvi+1)%3],
  993. tri_v2=tri.ind.c[(tvi+2)%3];
  994. addEdge(edges, tri_v1, tri); // add connection from 'vi' to 'tri_v1'
  995. addEdge(edges, tri_v2, tri); // add connection from 'vi' to 'tri_v2'
  996. tris_left.New().set(tri, tri_v1, tri_v2);
  997. }
  998. // check which edges are referenced by only 1 tri
  999. REPA(edges)
  1000. {
  1001. C VtxConnection &edge=edges[i];
  1002. if(edge.tris==1)vtx.border|=BORDER_POS; // if edge is referenced by only 1 tri
  1003. if(edge.mirror )vtx.border|=BORDER_TEX;
  1004. // normally vtxs will be repositioned only on the triangle surface, however for border vertexes, we don't want them to move away from the border too, so adjust 'qm' and 'planes'
  1005. if(VTX_BORDER_QM>0)
  1006. if(edge.tris==1 // if BORDER_POS (check "edge.tris==1" again, instead of "vtx.border&BORDER_POS", because BORDER_POS could have been enabled due to other edge)
  1007. || edge.mirror // if BORDER_TEX (check "edge.mirror" again, because check below only checks if both have BORDER_TEX)
  1008. || (vtx.border&BORDER_TEX) && (vtxs[edge.vtx].border&BORDER_TEX)) // also check for BORDER_TEX, currently there is no fast check to check this edge, so for simplicity, just check if both vertexes have this enabled
  1009. {
  1010. Vec cross=Cross(vtxs[edge.vtx].pos-vtx.pos, edge.tri->nrm); // this vector points outside (or inside) of the triangle (perpendicular to edge direction and triangle normal), if we use "cross" or "-cross" it's not important, because of how 'QuadricMatrix' works
  1011. cross.normalize();
  1012. vtx.qm+=QuadricMatrix(cross, vtx.pos)*(vtx.weight*VTX_BORDER_QM); // always set this, because it affects mid position
  1013. #if ALLOW_PLANES
  1014. if(mode==SIMPLIFY_PLANES)
  1015. {
  1016. Vertex::Plane plane;
  1017. plane.normal=cross;
  1018. plane.plane =-Dot(plane.normal, vtx.pos);
  1019. if(VTX_BORDER_QM!=1)plane.scale(VTX_BORDER_QM);
  1020. vtx.includePlane(plane);
  1021. }
  1022. #endif
  1023. }
  1024. }
  1025. // add a plane along vertex normal, to preserve details, for example for spike mesh parts /\ where the vertex normal is far away from the triangle normals
  1026. // this algorithm correctly handles cases with double sided faces (when processing a single face, it will remove all its copies including reversed faces, and proceed to next in the vtx triangle fan)
  1027. if(VTX_NORMAL_QM>0)
  1028. {
  1029. Vec tris_n=0;
  1030. for(; tris_left.elms(); )
  1031. {
  1032. TriLink tri_link=tris_left.pop();
  1033. C Triangle *tri =tri_link.tri;
  1034. Bool chs =(Dot(tri->nrm, tris_n)<0); // if this triangle direction points in the opposite of what we've already gathered, then change its (and its neighbors) sign
  1035. again:
  1036. Vec tri_nw =tri->nrm*tri->weight; // tri normal weighted
  1037. if(chs)tris_n-=tri_nw;else tris_n+=tri_nw; // add to gathered normal
  1038. TriLink next; next.tri=null;
  1039. // remove all the same and reversed tris, and find next one
  1040. REPA(tris_left)
  1041. {
  1042. VecI2 p=tris_left[i].p;
  1043. if(tri_link.p==p || (tri_link.p.x==p.y && tri_link.p.y==p.x))tris_left.remove(i);else
  1044. if(tri_link.p.y==p.x)next=tris_left[i]; // if this wasn't removed, and it's the next triangle, then remember it
  1045. }
  1046. if(next.tri)
  1047. {
  1048. tri_link=next;
  1049. tri =tri_link.tri;
  1050. goto again; // proceed to next one, without adjusting 'chs', because we are processing next triangle in fan connected to the previous, so we treat them as continuous
  1051. }
  1052. }
  1053. tris_n.normalize();
  1054. vtx.qm+=QuadricMatrix(tris_n, vtx.pos)*(vtx.weight*VTX_NORMAL_QM); // the best results is when this qm is proportional to 'vtx.weight'
  1055. }
  1056. }
  1057. }
  1058. void add(C MeshBase &mesh, Int part, C MeshPart *mesh_part)
  1059. {
  1060. UInt mesh_flag=mesh.flag();
  1061. // set material group
  1062. MtrlGroup mtrl_group(mesh_flag, mesh_part);
  1063. Int mtrl_group_i=-1; REPA(mtrl_groups)if(mtrl_groups[i].testAndMerge(mtrl_group)){mtrl_group_i=i; break;} // find existing one
  1064. if( mtrl_group_i< 0){MtrlGroup &mg=mtrl_groups[mtrl_group_i=mtrl_groups.addNum(1)]; Swap(mg, mtrl_group);} // create new one
  1065. Int vtx_ofs=vtx_dups.addNum(mesh.vtxs ()),
  1066. tri_ofs=tris .addNum(mesh.trisTotal());
  1067. Box mesh_box=mesh; if(vtx_ofs)T.box|=mesh_box;else T.box=mesh_box;
  1068. FREPA(mesh.vtx)
  1069. {
  1070. VtxDupMap &vtx_dup=vtx_dups[vtx_ofs+i];
  1071. vtx_dup.pos =mesh.vtx.pos (i);
  1072. if(mesh.vtx.tex0())vtx_dup.tex0 =mesh.vtx.tex0(i);else vtx_dup.tex0.zero(); // if not available then zero so BORDER_TEX can be detected properly
  1073. vtx_dup.mtrl_group=mtrl_group_i;
  1074. }
  1075. FREPA(mesh.tri)
  1076. {
  1077. Triangle &tri=tris[tri_ofs+i];
  1078. C VecI &src=mesh.tri.ind(i);
  1079. tri.part =part;
  1080. tri.mtrl_group=mtrl_group_i;
  1081. //tri.flag =mesh_flag; this will be set in 'init'
  1082. tri.ind =src+vtx_ofs;
  1083. REPAO(tri.vtxs).from(mesh, src.c[i]);
  1084. }
  1085. Int quad_ofs=tri_ofs+mesh.tris();
  1086. FREPA(mesh.quad)
  1087. {
  1088. C VecI4 &src=mesh.quad.ind(i);
  1089. VecI t0=src.tri0(), t1=src.tri1();
  1090. {
  1091. Triangle &tri=tris[quad_ofs++];
  1092. tri.part =part;
  1093. tri.mtrl_group=mtrl_group_i;
  1094. //tri.flag =mesh_flag; this will be set in 'init'
  1095. tri.ind =t0+vtx_ofs;
  1096. REPAO(tri.vtxs).from(mesh, t0.c[i]);
  1097. }
  1098. {
  1099. Triangle &tri=tris[quad_ofs++];
  1100. tri.part =part;
  1101. tri.mtrl_group=mtrl_group_i;
  1102. //tri.flag =mesh_flag; this will be set in 'init'
  1103. tri.ind =t1+vtx_ofs;
  1104. REPAO(tri.vtxs).from(mesh, t1.c[i]);
  1105. }
  1106. }
  1107. }
  1108. void checkRefs()C
  1109. {
  1110. REPAD(t, tris)
  1111. {
  1112. C Triangle &tri=tris[t]; REPAD(v, tri.ind)
  1113. {
  1114. C Vertex &vtx=vtxs[tri.ind.c[v]];
  1115. Bool has=false;
  1116. REP(vtx.tri_num)
  1117. {
  1118. C Ref &ref=refs[vtx.ref_start+i];
  1119. C Triangle &vtx_tri=tris.absElm(ref.tri_abs); if(tris.absToValidIndex(ref.tri_abs)<0)Exit("vtx tri doesn't exist");
  1120. if(&tri==&vtx_tri)has=true;
  1121. }
  1122. if(!has)Exit("vtx doesn't have tri");
  1123. }
  1124. }
  1125. }
  1126. void init(Flt pos_eps)
  1127. {
  1128. PROF(INIT);
  1129. Int mtrl_groups_elms=mtrl_groups.elms(); // how many material groups are there, remember this because 'mtrl_groups' will be deleted
  1130. UInt mtrl_groups_flag=0; REPA(mtrl_groups)mtrl_groups_flag|=mtrl_groups[i].flag; // what vtx components are we processing
  1131. REPA(tris) // set actual triangle flag after adding all meshes, because they may modify the mtrl group flag
  1132. {
  1133. Triangle &tri=tris[i]; tri.flag=mtrl_groups[tri.mtrl_group].flag;
  1134. }
  1135. mtrl_groups.del(); // release to free memory as it's no longer needed
  1136. // create unique vertexes
  1137. vtxs.setNum(SetVtxDup(SCAST(Memc<VtxDup>, vtx_dups), box, pos_eps)); Int vs=0;
  1138. REPA(vtx_dups)
  1139. {
  1140. VtxDupMap &vd=vtx_dups[i]; if(vd.dup==i)
  1141. {
  1142. vd.vtxs_index=vs; // set mapping from all points to 'vtxs'
  1143. Vertex &vtx=vtxs[vs++];
  1144. vtx.pos=vd.pos;
  1145. vtx.qm.reset();
  1146. vtx.weight=0;
  1147. vtx.border=0;
  1148. vtx.ref_start=vtx.tri_num=0;
  1149. }
  1150. }
  1151. // now that all 'vtxs' have been set, we need to check for BORDER_TEX
  1152. if(vtxs.elms()!=vtx_dups.elms()) // if number of unique vtxs is different than all vtxs, then it means there are some duplicates (some share the same position), if all are unique then we don't need to do this
  1153. if(mtrl_groups_elms>1 || (mtrl_groups_flag&VTX_TEX0)) // if there are total of more than 1 mtrl groups, or the vtxs have tex, then we need to check if the shared vtxs have different values
  1154. REPA(vtx_dups)
  1155. {
  1156. VtxDupMap &vd=vtx_dups[i]; // get i-th vtx
  1157. if(vd.dup!=i) // if this is not unique vertex
  1158. {
  1159. VtxDupMap &unique=vtx_dups[vd.dup]; // get unique vtx
  1160. if(vd.mtrl_group!=unique.mtrl_group || !EqualWrap(vd.tex0, unique.tex0)) // if this vtx tex is not the same as the unique vtx tex
  1161. vtxs[unique.vtxs_index].border|=BORDER_TEX; // mark it as being at the border
  1162. }
  1163. }
  1164. // init quadrics by plane and edge errors
  1165. REPA(tris)
  1166. {
  1167. Triangle &tri=tris[i];
  1168. REPA(tri.ind)
  1169. {
  1170. Int &tv =tri.ind.c[i]; tv=vtx_dups[vtx_dups[tv].dup].vtxs_index; // remap triangle vertex index to unique
  1171. Vertex &vtx=vtxs[tv] ; vtx.tri_num++;
  1172. }
  1173. tri.tan.zero(); tri.bin.zero(); setNrmTanBin(tri); // zero in init, in case 'setNrmTanBin' doesn't modify the values
  1174. Real plane_dist=-Dot(tri.nrm, vtxs[tri.ind.x].pos);
  1175. QuadricMatrix qm(tri.nrm, plane_dist);
  1176. qm*=tri.weight; // make weighted by triangle surface area
  1177. #if ALLOW_PLANES
  1178. Vertex::Plane plane; plane.normal=tri.nrm; plane.plane=plane_dist;
  1179. #endif
  1180. REP(3)
  1181. {
  1182. Vertex &vtx=vtxs[tri.ind.c[i]];
  1183. vtx.qm +=qm ;
  1184. vtx.weight+=tri.weight;
  1185. #if ALLOW_PLANES
  1186. if(mode==SIMPLIFY_PLANES)vtx.includePlane(plane);
  1187. #endif
  1188. }
  1189. }
  1190. vtx_dups.del(); // release to free memory as it's no longer needed
  1191. Int ref_start=0; REPA(vtxs) // prepare for a single continuous array of unique triangle-vertex pairs
  1192. {
  1193. Vertex &vtx=vtxs[i];
  1194. vtx.ref_start=ref_start; ref_start+=vtx.tri_num;
  1195. vtx.tri_num =0;
  1196. }
  1197. // set references
  1198. refs.setNum(tris.elms()*3);
  1199. REPAD(t, tris)
  1200. {
  1201. Triangle &tri=tris[t];
  1202. Int tri_abs=tris.validToAbsIndex(t);
  1203. REPAD(v, tri.ind)
  1204. {
  1205. Vertex &vtx=vtxs[tri.ind.c[v]];
  1206. Ref &ref=refs[vtx.ref_start + vtx.tri_num++];
  1207. ref.tri_abs =tri_abs;
  1208. ref.tri_vtx_index=v;
  1209. }
  1210. }
  1211. processVtxs(); // call after refs are available
  1212. // once 'vtx.qm' and 'vtx.border' are initialized for all vertexes, calculate edge error
  1213. REPAD(t, tris)
  1214. {
  1215. Triangle &tri=tris[t]; REPAD(v, tri.ind)
  1216. {
  1217. Vec mid_pos; tri.edge_error[v]=calculateError(tri.ind.c[v], tri.ind.c[(v+1)%3], mid_pos);
  1218. }
  1219. tri.error_min=Min(tri.edge_error[0], tri.edge_error[1], tri.edge_error[2]);
  1220. }
  1221. tris.sort(CompareError); // tris are now sorted from highest to lowest error
  1222. }
  1223. void simplify(Flt intensity, Flt max_distance, Flt max_uv, Flt max_color, Flt max_material, Flt max_skin, Flt max_normal, Bool keep_border, MESH_SIMPLIFY mode, Flt pos_eps)
  1224. {
  1225. #if !ALLOW_PLANES
  1226. if(mode==SIMPLIFY_PLANES)mode=SIMPLIFY_QUADRIC;
  1227. #endif
  1228. T.mode =mode;
  1229. T.max_uv2 =Sqr(Sat(max_uv));
  1230. T.max_color =Sat(max_color);
  1231. T.max_material=Sat(max_material);
  1232. T.max_skin =FltToByte(max_skin);
  1233. T.max_normal =Cos(Mid(max_normal, 0.0f, PI));
  1234. T.keep_border =keep_border;
  1235. T.test_flag =((T.max_uv2<1-EPS) ? VTX_TEX_ALL : 0)|((T.max_color<1-EPS) ? VTX_COLOR : 0)|((T.max_material<1-EPS) ? VTX_MATERIAL : 0)|((T.max_skin<255) ? VTX_SKIN : 0)|((T.max_normal>-1+EPS) ? VTX_NRM : 0);
  1236. Real max_error =Max(0.0f, max_distance);
  1237. Int target_tris=Mid(tris.elms()-RoundPos(tris.elms()*intensity), 0, tris.elms());
  1238. if(mode==SIMPLIFY_QUADRIC) // quadric method operates on squared errors
  1239. {
  1240. max_error*=max_error;
  1241. }
  1242. init(pos_eps);
  1243. if(tris.elms()<=target_tris)return; // must be checked after 'init'
  1244. // main iteration loop
  1245. Vec mid_pos;
  1246. REPA(tris)
  1247. if(InRange(i, tris)) // check in case it got deleted
  1248. {
  1249. processed_tris=i;
  1250. again:
  1251. Triangle &tri=tris[i]; if(tri.error_min>max_error || stop())break; // tris are sorted by their error, so if we've reached the one above the limit, then stop
  1252. Int i=MinI(tri.edge_error[0], tri.edge_error[1], tri.edge_error[2]);
  1253. Int edge_vtx0i=tri.ind.c[ i ]; Vertex &edge_vtx0=vtxs[edge_vtx0i];
  1254. Int edge_vtx1i=tri.ind.c[(i+1)%3]; Vertex &edge_vtx1=vtxs[edge_vtx1i];
  1255. // border check
  1256. if(keep_border && (edge_vtx0.border&edge_vtx1.border&BORDER_POS))goto cant; // if both are on the border, then we can't move, if only one is on border, then we can move but only to the border
  1257. // compute vertex to collapse to
  1258. calculateError(edge_vtx0i, edge_vtx1i, mid_pos);
  1259. /* there are 3 types of processed triangles:
  1260. -have only edge_vtx0 , let's call them "left" triangles
  1261. -have both edge_vtx0 and edge_vtx1 (these will be removed), let's call them "middle" triangles
  1262. -have only edge_vtx1 , let's call them "right" triangles */
  1263. // don't remove if flipped
  1264. middle_tris.clear(); all_tris.clear(); // clear before 'flipped' because that modifies this
  1265. if(flipped(mid_pos, edge_vtx1i, edge_vtx0, true ) // middle tris will be collected only from the first vertex
  1266. || flipped(mid_pos, edge_vtx0i, edge_vtx1, false))goto cant;
  1267. // calculate vertex data for all triangles at the middle position
  1268. REPA(all_tris)
  1269. {
  1270. TrianglePtr &triangle_ptr=all_tris[i];
  1271. Triangle &triangle =*triangle_ptr.tri;
  1272. Tri tri(vtxs[triangle.ind.x].pos, vtxs[triangle.ind.y].pos, vtxs[triangle.ind.z].pos, &triangle.nrm);
  1273. Vec blend=TriBlend(mid_pos, tri, false);
  1274. blend*=triangle.weight; // make 'vtx_mid' vertex data weighted by 'triangle.weight'
  1275. triangle_ptr.vtx_mid.lerp(triangle.vtxs[0], triangle.vtxs[1], triangle.vtxs[2], blend, triangle.flag);
  1276. triangle_ptr.vtx_mid.nrm*=triangle.weight; // mul 'nrm' because 'lerp' normalizes the normal
  1277. triangle_ptr.clear();
  1278. }
  1279. /* In most cases, vtx data will be a weighted average of all 3 triangle types
  1280. However, since left/right triangles are not in contact, then first the middle triangles will have their vtx data set from all triangles
  1281. And then left/right (outer) triangles will have this vtx data copied from the middle triangles. */
  1282. REPA(middle_tris) addVtxData(*middle_tris[i], edge_vtx0i, edge_vtx1i);
  1283. REPA( all_tris)if(!setVtxData( all_tris[i], edge_vtx0i, edge_vtx1i))goto cant;
  1284. if(test_flag)
  1285. {
  1286. if(!testTriangles(edge_vtx0, mid_pos)
  1287. || !testTriangles(edge_vtx1, mid_pos))goto cant;
  1288. }
  1289. // remove edge
  1290. {
  1291. #if ADJUST_REFS
  1292. REPA(middle_tris)adjustRefs(*middle_tris[i]->tri, edge_vtx0i, edge_vtx1i);
  1293. #endif
  1294. REPA(middle_tris)tris.removeData(middle_tris[i]->tri, true);
  1295. edge_vtx0.pos=mid_pos;
  1296. edge_vtx0.eat(edge_vtx1);
  1297. Int ref_start=refs.elms(); // remember current number of references
  1298. updateTriangles(edge_vtx0i, edge_vtx0);
  1299. updateTriangles(edge_vtx0i, edge_vtx1);
  1300. if(tris.elms()<=target_tris)break; // if reached the limit
  1301. Int tri_num =refs.elms()-ref_start; // get how many references were added
  1302. if( tri_num<=edge_vtx0.tri_num) // if the new number fits in the previous range, then just replace it, to reduce memory usage
  1303. {
  1304. CopyN(refs.addr(edge_vtx0.ref_start), refs.addr(ref_start), tri_num); // copy to existing location
  1305. refs.setNum(ref_start); // added elements will not be used, so release them
  1306. }else edge_vtx0.ref_start=ref_start; // set start to what we've added
  1307. edge_vtx0.tri_num =tri_num ; // set number of tris
  1308. #if DEBUG_CHECK_REFS && ADJUST_REFS // we can check refs only if they are adjusted
  1309. #pragma message("!! Warning: This will slow down mesh simplification, use only for testing !!")
  1310. checkRefs();
  1311. #endif
  1312. continue;
  1313. }
  1314. cant:
  1315. if(tri.edge_error[i]<FLT_MAX) // this was the min, and if the min was doable, then
  1316. {
  1317. tri.edge_error[i]=FLT_MAX; // set it as not-doable
  1318. resetError(tri, processed_tris); // re-position it in the 'tris' list based on new 'error_min'
  1319. goto again; // try again with the same index
  1320. }
  1321. }
  1322. }
  1323. void store(MeshBase &mesh, UInt flag_and=~0)
  1324. {
  1325. UInt flags=0; REPA(tris)flags|=tris[i].flag; flags&=flag_and;
  1326. mesh.create(tris.elms()*3, 0, tris.elms(), 0, flags&(VTX_ALL&~(VTX_TAN_BIN|VTX_DUP)));
  1327. REPA(tris)
  1328. {
  1329. Triangle &tri=tris[i];
  1330. Int tri_index=i, vtx_index=i*3;
  1331. mesh.tri.ind(tri_index).set(vtx_index, vtx_index+1, vtx_index+2);
  1332. tri.vtxs[0].to(mesh, vtx_index );
  1333. tri.vtxs[1].to(mesh, vtx_index+1);
  1334. tri.vtxs[2].to(mesh, vtx_index+2);
  1335. mesh.vtx.pos(vtx_index )=vtxs[tri.ind.x].pos;
  1336. mesh.vtx.pos(vtx_index+1)=vtxs[tri.ind.y].pos;
  1337. mesh.vtx.pos(vtx_index+2)=vtxs[tri.ind.z].pos;
  1338. }
  1339. mesh.weldVtx(VTX_ALL, EPSD, EPS_COL_COS, -1); // use small epsilon in case mesh is scaled down, ignore degenerate faces
  1340. // recalculate precisely
  1341. if(flags&VTX_TAN)mesh.setTangents ();
  1342. if(flags&VTX_BIN)mesh.setBinormals();
  1343. }
  1344. void store(MemPtr<MeshPart> parts)
  1345. {
  1346. struct PartInfo
  1347. {
  1348. Int tris;
  1349. UInt flag;
  1350. PartInfo() {tris=0; flag=0;}
  1351. };
  1352. MemtN<PartInfo, 256> part_infos;
  1353. REPA(tris)
  1354. {
  1355. C Triangle &tri =tris[i];
  1356. PartInfo &part=part_infos(tri.part);
  1357. part.tris++;
  1358. part.flag|=tri.flag;
  1359. }
  1360. parts.setNum(part_infos.elms());
  1361. REPA(parts)
  1362. {
  1363. MeshBase &mesh=parts[i].base;
  1364. C PartInfo &part=part_infos[i];
  1365. mesh.create(part.tris*3, 0, part.tris, 0, part.flag&(VTX_ALL&~(VTX_TAN_BIN|VTX_DUP)));
  1366. }
  1367. REPAO(part_infos).tris=0;
  1368. REPA (tris)
  1369. {
  1370. Triangle &tri=tris[i];
  1371. Int part=tri.part;
  1372. Int tri_index=(part_infos[part].tris++), vtx_index=tri_index*3;
  1373. MeshBase &mesh=parts[part].base;
  1374. mesh.tri.ind(tri_index).set(vtx_index, vtx_index+1, vtx_index+2);
  1375. tri.vtxs[0].to(mesh, vtx_index );
  1376. tri.vtxs[1].to(mesh, vtx_index+1);
  1377. tri.vtxs[2].to(mesh, vtx_index+2);
  1378. mesh.vtx.pos(vtx_index )=vtxs[tri.ind.x].pos;
  1379. mesh.vtx.pos(vtx_index+1)=vtxs[tri.ind.y].pos;
  1380. mesh.vtx.pos(vtx_index+2)=vtxs[tri.ind.z].pos;
  1381. }
  1382. REPA(parts) // go from the end so we can remove if needed
  1383. {
  1384. MeshBase &mesh=parts[i].base;
  1385. mesh.weldVtx(VTX_ALL, EPSD, EPS_COL_COS, -1); // use small epsilon in case mesh is scaled down, ignore degenerate faces
  1386. if(stop())return;
  1387. if(!mesh.is())parts.remove(i, true);else // if became empty, then just remove it
  1388. {
  1389. // recalculate precisely
  1390. UInt flag=part_infos[i].flag;
  1391. if(flag&VTX_TAN)mesh.setTangents ();
  1392. if(flag&VTX_BIN)mesh.setBinormals();
  1393. }
  1394. }
  1395. }
  1396. };
  1397. /******************************************************************************/
  1398. MeshBase& MeshBase::simplify(Flt intensity, Flt max_distance, Flt max_uv, Flt max_color, Flt max_material, Flt max_skin, Flt max_normal, Bool keep_border, MESH_SIMPLIFY mode, Flt pos_eps, MeshBase *dest)
  1399. {
  1400. if(!dest)dest=this;
  1401. if(intensity<=0)dest->create(T);else
  1402. {
  1403. Simplify s(null);
  1404. s.add(T, 0, null);
  1405. s.simplify(intensity, max_distance, max_uv, max_color, max_material, max_skin, max_normal, keep_border, mode, pos_eps);
  1406. s.store(*dest);
  1407. }
  1408. return *dest;
  1409. }
  1410. MeshLod& MeshLod::simplify(Flt intensity, Flt max_distance, Flt max_uv, Flt max_color, Flt max_material, Flt max_skin, Flt max_normal, Bool keep_border, MESH_SIMPLIFY mode, Flt pos_eps, MeshLod *dest, Bool *stop)
  1411. {
  1412. if(!dest)dest=this;
  1413. if(intensity<=0)dest->create(T);else
  1414. {
  1415. Simplify s(stop);
  1416. if(s.stop())goto stop;
  1417. if(dest!=this)
  1418. {
  1419. dest->copyParams(T);
  1420. //dest->delRender(); don't do this because that would require D._lock, and it's highly possible that we're going to be calling this method on other threads, which would have to wait for the lock
  1421. dest->parts.setNum(parts.elms());
  1422. REPAO(dest->parts).copyParams(parts[i]);
  1423. }
  1424. if(s.stop())goto stop;
  1425. {
  1426. PROF(ADD);
  1427. FREPA(T)
  1428. {
  1429. s.add(parts[i].base, i, &parts[i]);
  1430. if(s.stop())goto stop;
  1431. }
  1432. }
  1433. {
  1434. PROF(SIMPLIFY);
  1435. s.simplify(intensity, max_distance, max_uv, max_color, max_material, max_skin, max_normal, keep_border, mode, pos_eps);
  1436. }
  1437. if(s.stop())goto stop;
  1438. {
  1439. PROF(STORE);
  1440. s.store(dest->parts);
  1441. }
  1442. //if(s.stop())goto stop;
  1443. PROFILE_END;
  1444. }
  1445. stop:
  1446. return *dest;
  1447. }
  1448. /******************************************************************************/
  1449. }
  1450. /******************************************************************************/