1  // 

2  // Lol Engine 

3  // 

4  // Copyright: (c) 20102013 Sam Hocevar <sam@hocevar.net> 

5  // (c) 20102013 Benjamin "Touky" Huet <huet.benjamin@gmail.com> 

6  // This program is free software; you can redistribute it and/or 

7  // modify it under the terms of the Do What The Fuck You Want To 

8  // Public License, Version 2, as published by Sam Hocevar. See 

9  // http://www.wtfpl.net/ for more details. 

10  // 

11  

12  // 

13  // The EasyMesh class 

14  //  

15  // 

16  

17  #if defined HAVE_CONFIG_H 

18  # include "config.h" 

19  #endif 

20  

21  #if defined _XBOX 

22  # define _USE_MATH_DEFINES /* for M_PI */ 

23  # include <xtl.h> 

24  # undef near /* Fuck Microsoft */ 

25  # undef far /* Fuck Microsoft again */ 

26  #elif defined _WIN32 

27  # define _USE_MATH_DEFINES /* for M_PI */ 

28  # define WIN32_LEAN_AND_MEAN 

29  # include <windows.h> 

30  # undef near /* Fuck Microsoft */ 

31  # undef far /* Fuck Microsoft again */ 

32  #endif 

33  

34  #include "core.h" 

35  

36  namespace lol 

37  { 

38  

39  int CsgBsp::AddLeaf(int leaf_type, vec3 origin, vec3 normal, int above_idx) 

40  { 

41  if (leaf_type > 2 && leaf_type < 1) 

42  return 1; 

43  

44  if ((m_tree.Count() == 0 && above_idx == 1)  

45  (above_idx >= 0 && 

46  above_idx < m_tree.Count() && 

47  leaf_type > LEAF_CURRENT && 

48  leaf_type < LEAF_ABOVE && 

49  m_tree[above_idx].m_leaves[leaf_type] == 1)) 

50  { 

51  if (m_tree.Count() != 0) 

52  m_tree[above_idx].m_leaves[leaf_type] = m_tree.Count(); 

53  m_tree.Push(CsgBspLeaf(origin, normal, above_idx)); 

54  return m_tree.Count()  1; 

55  } 

56  

57  return 1; 

58  } 

59  

60  int CsgBsp::TestPoint(int leaf_idx, vec3 point) 

61  { 

62  if (leaf_idx >= 0 && leaf_idx < m_tree.Count()) 

63  { 

64  vec3 p2o = point  m_tree[leaf_idx].m_origin; 

65  

66  if (length(p2o) < CSG_EPSILON) 

67  return LEAF_CURRENT; 

68  

69  float p2o_dot = dot(normalize(p2o), m_tree[leaf_idx].m_normal); 

70  

71  if (p2o_dot > CSG_EPSILON) 

72  return LEAF_FRONT; 

73  else if (p2o_dot < CSG_EPSILON) 

74  return LEAF_BACK; 

75  } 

76  return LEAF_CURRENT; 

77  } 

78  

79  void CsgBsp::AddTriangleToTree(int const &tri_idx, vec3 const &tri_v0, vec3 const &tri_v1, vec3 const &tri_v2) 

80  { 

81  //<Leaf_Id, v0, v1, v2> 

82  Array< int, vec3, vec3, vec3 > tri_to_process; 

83  //<FW/BW, Leaf_Id, v0, v1, v2, twin_leaf> 

84  Array< int, int, vec3, vec3, vec3, int > Leaf_to_add; 

85  

86  //Tree is empty, so this leaf is the first 

87  if (m_tree.Count() == 0) 

88  { 

89  AddLeaf(LEAF_CURRENT, tri_v0, cross(normalize(tri_v1  tri_v0), normalize(tri_v2  tri_v1)), LEAF_CURRENT); 

90  m_tree.Last().m_tri_list.Push(tri_idx, tri_v0, tri_v1, tri_v2); 

91  return; 

92  } 

93  

94  tri_to_process.Reserve(20); 

95  tri_to_process.Push(0, tri_v0, tri_v1, tri_v2); 

96  

97  while (tri_to_process.Count()) 

98  { 

99  int leaf_idx = tri_to_process.Last().m1; 

100  vec3 v[3] = { tri_to_process.Last().m2, tri_to_process.Last().m3, tri_to_process.Last().m4 }; 

101  

102  int res_nb[3] = { 0, 0, 0 }; 

103  int res_side[3] = { 1, 1, 1 }; 

104  

105  //Check where each point is located 

106  for (int i = 0; i < 3; i++) 

107  { 

108  int result = TestPoint(leaf_idx, v[i]); 

109  if (result != LEAF_CURRENT) 

110  { 

111  res_nb[result]++; 

112  res_side[i] = result; 

113  } 

114  } 

115  

116  //Points are located on each sides, let's do the mumbojumbo 

117  if (res_nb[LEAF_BACK] && res_nb[LEAF_FRONT]) 

118  { 

119  //there are two intersections, no more. 

120  vec3 isec_v[2] = { vec3(.0f), vec3(.0f) }; 

121  int isec_i[2] = { 0, 0 }; 

122  int isec_base = 0; 

123  int isec_idx = 0; 

124  

125  for (int i = 0; i < 3; i++) 

126  { 

127  vec3 ray = v[(i + 1) % 3]  v[i]; 

128  

129  if (RayIsectPlane(v[i], v[(i + 1) % 3],


130  m_tree[leaf_idx].m_origin, m_tree[leaf_idx].m_normal,


131  isec_v[isec_idx]))


132  isec_i[isec_idx++] = i;


133  else


134  isec_base = i;


135  } 

136  

137  int v_idx0 = (isec_base == 1)?(1):(0); 

138  int v_idx1 = (isec_base == 1)?(0):(1); 

139  int leaf_type = res_side[(isec_base + 2) % 3]; 

140  

141  tri_to_process.Pop(); 

142  

143  #if 1 

144  if (m_tree[leaf_idx].m_leaves[leaf_type] == LEAF_CURRENT) 

145  Leaf_to_add.Push(leaf_type, leaf_idx, v[((isec_base + 2) % 3)], isec_v[v_idx1], isec_v[v_idx0], 1); 

146  else 

147  tri_to_process.Push(leaf_idx, v[((isec_base + 2) % 3)], isec_v[v_idx1], isec_v[v_idx0]); 

148  

149  if (m_tree[leaf_idx].m_leaves[1  leaf_type] == LEAF_CURRENT) 

150  { 

151  Leaf_to_add.Push(1  leaf_type, leaf_idx, v[isec_base], v[((isec_base + 1) % 3)], isec_v[v_idx0], 1); 

152  Leaf_to_add.Push(1  leaf_type, leaf_idx, v[isec_base], isec_v[v_idx0], isec_v[v_idx1], Leaf_to_add.Count()  1); 

153  } 

154  else 

155  { 

156  tri_to_process.Push(m_tree[leaf_idx].m_leaves[1  leaf_type], v[isec_base], v[((isec_base + 1) % 3)], isec_v[v_idx0]); 

157  tri_to_process.Push(m_tree[leaf_idx].m_leaves[1  leaf_type], v[isec_base], isec_v[v_idx0], isec_v[v_idx1]); 

158  } 

159  #else 

160  vec3 new_v[9] = { v[((isec_base + 2) % 3)], isec_v[v_idx1], isec_v[v_idx0], 

161  v[isec_base], v[((isec_base + 1) % 3)], isec_v[v_idx0], 

162  v[isec_base], isec_v[v_idx0], isec_v[v_idx1] }; 

163  

164  //Error check : Skip the triangle where two points are on the same location. 

165  //it fixes the problem of having an intersection with one of the isecpoint being on one of the triangle vertices. 

166  //(the problem being a very funny infinite loop) 

167  for(int k = 0; k < 9; k += 3) 

168  { 

169  bool skip_tri = false; 

170  for(int l = 0; l < 3; l++) 

171  { 

172  if (length(new_v[k + l]  new_v[k + (l + 1) % 3]) < CSG_EPSILON) 

173  { 

174  skip_tri = true; 

175  break; 

176  } 

177  } 

178  

179  if (skip_tri) 

180  continue; 

181  

182  tri_to_process.Push(0, new_v[k], new_v[k + 1], new_v[k + 2]); 

183  } 

184  #endif 

185  } 

186  //All points are on one side, transfer to the next leaf 

187  else if (res_nb[LEAF_BACK]  res_nb[LEAF_FRONT]) 

188  { 

189  int new_leaf_type = ((res_nb[LEAF_FRONT])?(LEAF_FRONT):(LEAF_BACK)); 

190  int new_leaf = m_tree[leaf_idx].m_leaves[new_leaf_type]; 

191  

192  //No leaf exist, so add a new one 

193  if (new_leaf == LEAF_CURRENT) 

194  { 

195  tri_to_process.Pop(); 

196  Leaf_to_add.Push(new_leaf_type, leaf_idx, v[0], v[1], v[2], 1); 

197  } 

198  else 

199  tri_to_process.Last().m1 = new_leaf; 

200  } 

201  //All points are on the current leaf, add the tri_idx to the list of this leaf. 

202  else 

203  { 

204  tri_to_process.Pop(); 

205  

206  bool already_exist = false; 

207  for (int i = 0; !already_exist && i < m_tree[leaf_idx].m_tri_list.Count(); i++) 

208  already_exist = (m_tree[leaf_idx].m_tri_list[i].m1 == tri_idx); 

209  if (!already_exist) 

210  m_tree[leaf_idx].m_tri_list.Push(tri_idx, tri_v0, tri_v1, tri_v2); 

211  } 

212  } 

213  

214  //Add all leaves to the tree. 

215  for (int i = 0; i < Leaf_to_add.Count(); i++) 

216  { 

217  //If we had it to an already existing leaf. 

218  if (Leaf_to_add[i].m2 < m_tree.Count() && m_tree[Leaf_to_add[i].m2].m_leaves[Leaf_to_add[i].m1] == LEAF_CURRENT) 

219  { 

220  AddLeaf(Leaf_to_add[i].m1, tri_v0, cross(normalize(tri_v1  tri_v0), normalize(tri_v2  tri_v1)), Leaf_to_add[i].m2); 

221  m_tree.Last().m_tri_list.Push(tri_idx, tri_v0, tri_v1, tri_v2); 

222  } 

223  

224  /* 

225  if (Leaf_to_add[i].m6 == 1) 

226  { 

227  AddLeaf(Leaf_to_add[i].m1, tri_v0, cross(normalize(tri_v1  tri_v0), normalize(tri_v2  tri_v1)), Leaf_to_add[i].m2); 

228  m_tree.Last().m_tri_list.Push(tri_idx, tri_v0, tri_v1, tri_v2); 

229  } 

230  else 

231  m_tree[Leaf_to_add[i].m6].m_tri_list.Push(tri_idx, tri_v0, tri_v1, tri_v2); 

232  */ 

233  } 

234  } 

235  

236  //return 0 when no split has been done. 

237  //return 1 when split has been done. 

238  //return 1 when error. 

239  int CsgBsp::TestTriangleToTree(vec3 const &tri_v0, vec3 const &tri_v1, vec3 const &tri_v2, 

240  //In order to easily build the actual vertices list afterward, this list stores each Vertices location and its source vertices & Alpha. 

241  //<Point_Loc, Src_V0, Src_V1, Alpha> as { Point_Loc = Src_V0 + (Src_V1  Src_V0) * Alpha; } 

242  Array< vec3, int, int, float > &vert_list, 

243  //This is the final triangle list : If Side_Status is LEAF_CURRENT, a new test will be done point by point. 

244  //<{INOUT}side_status, v0, v1, v2> 

245  Array< int, int, int, int > &tri_list) 

246  { 

247  //This list stores the current triangles to process. 

248  //<Leaf_Id_List, v0, v1, v2, Should_Point_Test> 

249  Array< Array< int >, int, int, int, int > tri_to_process; 

250  

251  //Tree is empty, ABORT ! 

252  if (m_tree.Count() == 0) 

253  return 1; 

254  

255  //Let's push the source vertices in here. 

256  vert_list.Push(tri_v0, 1, 1, .0f); 

257  vert_list.Push(tri_v1, 1, 1, .0f); 

258  vert_list.Push(tri_v2, 1, 1, .0f); 

259  

260  //Let's push the triangle in here. 

261  tri_to_process.Reserve(20); 

262  tri_to_process.Push( Array< int >(), 0, 1, 2, 0); 

263  tri_to_process.Last().m1.Push(0); 

264  

265  while (tri_to_process.Count()) 

266  { 

267  while (tri_to_process.Count()) 

268  { 

269  int leaf_idx = tri_to_process.Last().m1.Last(); 

270  int t[3] = { tri_to_process.Last().m2, 

271  tri_to_process.Last().m3, 

272  tri_to_process.Last().m4 }; 

273  vec3 v[3] = { vert_list[t[0]].m1, 

274  vert_list[t[1]].m1, 

275  vert_list[t[2]].m1 }; 

276  

277  int res_nb[3] = { 0, 0, 0 }; 

278  int res_side[3] = { 1, 1, 1 }; 

279  //Check where each point is located 

280  for (int i = 0; i < 3; i++) 

281  { 

282  int result = TestPoint(leaf_idx, v[i]); 

283  if (result != LEAF_CURRENT) 

284  { 

285  res_nb[result]++; 

286  res_side[i] = result; 

287  } 

288  } 

289  

290  //Points are located on each sides, let's do the mumbojumbo 

291  if (res_nb[LEAF_BACK] && res_nb[LEAF_FRONT]) 

292  { 

293  //there are two intersections, no more. 

294  vec3 isec_v[2] = { vec3(.0f), vec3(.0f) }; 

295  int isec_i[2] = { 0, 0 }; 

296  int new_v_idx[2] = { 0, 0 }; 

297  int isec_base = 0; 

298  int isec_idx = 0; 

299  

300  int i = 0; 

301  for (; i < m_tree[leaf_idx].m_tri_list.Count(); i++) 

302  { 

303  if (TriangleIsectTriangle(v[0], v[1], v[2], 

304  m_tree[leaf_idx].m_tri_list[i].m2, m_tree[leaf_idx].m_tri_list[i].m3, m_tree[leaf_idx].m_tri_list[i].m4, 

305  isec_v[0], isec_v[1])) 

306  break; 

307  } 

308  

309  //There was no triangle intersection, the complex case. 

310  if (i == m_tree[leaf_idx].m_tri_list.Count()) 

311  { 

312  tri_to_process.Last().m1.Pop(); 

313  

314  //Register the triangle as needing to intersect with Front & back leaves. 

315  if (m_tree[leaf_idx].m_leaves[LEAF_FRONT] != LEAF_CURRENT) 

316  tri_to_process.Last().m1.Push(m_tree[leaf_idx].m_leaves[LEAF_FRONT]); 

317  if (m_tree[leaf_idx].m_leaves[LEAF_BACK] != LEAF_CURRENT) 

318  tri_to_process.Last().m1.Push(m_tree[leaf_idx].m_leaves[LEAF_BACK]); 

319  //Mark the triangle as needing point by point test 

320  tri_to_process.Last().m5 = 1; 

321  } 

322  //there was an intersection, so let's split the triangle. 

323  else 

324  { 

325  //Get intersection on actual triangle sides. 

326  if (RayIsectTriangleSide(v[0], v[1], v[2], 

327  isec_v[0], isec_v[1], 

328  isec_v[0], isec_i[0], isec_v[1], isec_i[1])) 

329  { 

330  { 

331  for(int k = 0; k < 2; k++) 

332  { 

333  if (isec_base == isec_i[k]) 

334  isec_base++; 

335  

336  #if 1 //Skip point creation if it's on the same location a one of the triangle. 

337  bool skip_point = false; 

338  int l = 0; 

339  for(; l < 3; l++) 

340  { 

341  if (length(v[l]  isec_v[k]) < CSG_EPSILON) 

342  { 

343  skip_point = true; 

344  new_v_idx[k] = t[l]; 

345  break; 

346  } 

347  } 

348  

349  if (skip_point) 

350  continue; 

351  #endif 

352  new_v_idx[k] = vert_list.Count(); 

353  vec3 PmV0 = (isec_v[k]  vert_list[t[isec_i[k]]].m1); 

354  vec3 V1mV0 = (vert_list[t[(isec_i[k] + 1) % 3]].m1  vert_list[t[isec_i[k]]].m1); 

355  float alpha = length(PmV0) / length(V1mV0); 

356  vert_list.Push(isec_v[k], 

357  t[isec_i[k]], t[(isec_i[k] + 1) % 3], 

358  //Alpha = length((Point_Loc  Src_V0) / (Src_V1  Src_V0)); 

359  alpha); 

360  } 

361  

362  int v_idx0 = (isec_base == 1)?(1):(0); 

363  int v_idx1 = (isec_base == 1)?(0):(1); 

364  //Leaf_type is the type for the triangle that is alone on its side. 

365  int leaf_type = res_side[(isec_base + 2) % 3]; 

366  int tri_to_remove = tri_to_process.Count()  1; 

367  

368  #if 0 

369  if (m_tree[leaf_idx].m_leaves[leaf_type] == LEAF_CURRENT && tri_to_process.Last().m1.Last() == 1) 

370  tri_list.Push(leaf_type, 

371  t[(isec_base + 2) % 3], new_v_idx[v_idx1], new_v_idx[v_idx0]); 

372  else 

373  { 

374  tri_to_process.Push(Array< int >(), t[(isec_base + 2) % 3], new_v_idx[v_idx1], new_v_idx[v_idx0], 0); 

375  tri_to_process.Last().m1.Push(0); 

376  } 

377  

378  if (m_tree[leaf_idx].m_leaves[1  leaf_type] == LEAF_CURRENT && tri_to_process.Last().m1.Last() == 1) 

379  { 

380  tri_list.Push((tri_to_process.Last().m5)?(LEAF_CURRENT):(1  leaf_type), 

381  t[isec_base], new_v_idx[((isec_base + 1) % 3)], new_v_idx[v_idx0]); 

382  tri_list.Push((tri_to_process.Last().m5)?(LEAF_CURRENT):(1  leaf_type), 

383  t[isec_base], new_v_idx[v_idx0], new_v_idx[v_idx1]); 

384  } 

385  else 

386  { 

387  tri_to_process.Push(Array< int >(), t[isec_base], t[((isec_base + 1) % 3)], new_v_idx[v_idx0], 0); 

388  tri_to_process.Last().m1.Push(0); 

389  tri_to_process.Push(Array< int >(), t[isec_base], new_v_idx[v_idx0], new_v_idx[v_idx1], 0); 

390  tri_to_process.Last().m1.Push(0); 

391  } 

392  #else 

393  int new_t[9] = { t[(isec_base + 2) % 3], new_v_idx[v_idx1], new_v_idx[v_idx0], 

394  t[isec_base], t[((isec_base + 1) % 3)], new_v_idx[v_idx0], 

395  t[isec_base], new_v_idx[v_idx0], new_v_idx[v_idx1] }; 

396  int new_side[3] = { res_side[(isec_base + 2) % 3], 

397  (res_side[isec_base] == LEAF_CURRENT)?(res_side[((isec_base + 1) % 3)]):(res_side[isec_base]), 

398  res_side[isec_base] }; 

399  

400  //Error check : Skip the triangle where two points are on the same location. 

401  //it fixes the problem of having an intersection with one of the isecpoint being on one of the triangle vertices. 

402  //(the problem being a very funny infinite loop) 

403  for(int k = 0; k < 9; k += 3) 

404  { 

405  #if 1 //Error check 

406  bool skip_tri = false; 

407  for(int l = 0; l < 3; l++) 

408  { 

409  if (length(vert_list[new_t[k + l]].m1  vert_list[new_t[k + (l + 1) % 3]].m1) < CSG_EPSILON) 

410  { 

411  skip_tri = true; 

412  break; 

413  } 

414  } 

415  

416  if (skip_tri) 

417  continue; 

418  #endif 

419  #if 0 //Send the newly created triangle back to the beginning 

420  tri_to_process.Push(Array< int >(), new_t[k], new_t[k + 1], new_t[k + 2], 0); 

421  tri_to_process.Last().m1.Push(0); 

422  #else //Inherit parent tree 

423  if (m_tree[leaf_idx].m_leaves[new_side[k / 3]] == LEAF_CURRENT && tri_to_process[tri_to_remove].m1.Count() == 1) 

424  tri_list.Push(new_side[k / 3], new_t[k], new_t[k + 1], new_t[k + 2]); 

425  else 

426  { 

427  tri_to_process.Push(Array< int >(), new_t[k], new_t[k + 1], new_t[k + 2], 0); 

428  tri_to_process.Last().m1 = tri_to_process[tri_to_remove].m1; 

429  if (m_tree[leaf_idx].m_leaves[new_side[k / 3]] == LEAF_CURRENT) 

430  tri_to_process.Last().m1.Pop(); 

431  else 

432  tri_to_process.Last().m1.Last() = m_tree[leaf_idx].m_leaves[new_side[k / 3]]; 

433  } 

434  #endif 

435  } 

436  #endif 

437  

438  tri_to_process.Remove(tri_to_remove); 

439  } 

440  } 

441  } 

442  } 

443  //All points are on one side, transfer to the next leaf 

444  else if (res_nb[LEAF_BACK]  res_nb[LEAF_FRONT]) 

445  { 

446  int new_leaf_type = ((res_nb[LEAF_FRONT])?(LEAF_FRONT):(LEAF_BACK)); 

447  int new_leaf = m_tree[leaf_idx].m_leaves[new_leaf_type]; 

448  

449  //No leaf exist, we're at the end 

450  if (new_leaf == LEAF_CURRENT) 

451  { 

452  //We still need to test other leaves. 

453  if (tri_to_process.Last().m1.Count() > 1) 

454  tri_to_process.Last().m1.Pop(); 

455  else 

456  { 

457  tri_list.Push((tri_to_process.Last().m5)?(LEAF_CURRENT):(new_leaf_type), 

458  tri_to_process.Last().m2, tri_to_process.Last().m3, tri_to_process.Last().m4); 

459  tri_to_process.Pop(); 

460  } 

461  } 

462  else 

463  tri_to_process.Last().m1.Last() = new_leaf; 

464  } 

465  //All points are on the current leaf, add the tri_idx to the list of this leaf. 

466  else 

467  { 

468  //TODO : Special case, handle coplanar cut. 

469  tri_list.Push(LEAF_CURRENT, tri_to_process.Last().m2, tri_to_process.Last().m3, tri_to_process.Last().m4); 

470  tri_to_process.Pop(); 

471  } 

472  } 

473  

474  //Now that we have all the split points, let's doublecheck the results 

475  for (int i = 0; i < tri_list.Count(); i++) 

476  { 

477  #define TEST_MAX 4 

478  int t[3] = { tri_list[i].m2, 

479  tri_list[i].m3, 

480  tri_list[i].m4 }; 

481  vec3 v[4] = { vert_list[t[0]].m1, 

482  vert_list[t[1]].m1, 

483  vert_list[t[2]].m1, 

484  (vert_list[t[0]].m1 +


485  vert_list[t[1]].m1 +


486  vert_list[t[2]].m1) / 3.0f }; 

487  

488  int res_total = 0; 

489  int res_nb[3] = { 0, 0, 0 }; 

490  

491  int res_Leaf[4] = { 0, 0, 0, 0 }; 

492  int res_side[4] = { 1, 1, 1, 1 }; 

493  while (res_total < TEST_MAX) 

494  { 

495  for (int k = 0; k < TEST_MAX; k++) 

496  { 

497  if (res_Leaf[k] != LEAF_CURRENT) 

498  { 

499  int result = TestPoint(res_Leaf[k], v[k]); 

500  if (result != LEAF_CURRENT) 

501  { 

502  res_Leaf[k] = m_tree[res_Leaf[k]].m_leaves[result]; 

503  res_side[k] = result; 

504  if (res_Leaf[k] == LEAF_CURRENT) 

505  { 

506  res_total++; 

507  res_nb[result]++; 

508  } 

509  } 

510  else 

511  { 

512  res_Leaf[k] = LEAF_CURRENT; 

513  res_side[k] = LEAF_CURRENT; 

514  res_total++; 

515  } 

516  } 

517  } 

518  } 

519  int k = 0; 

520  if (res_nb[LEAF_BACK] && res_nb[LEAF_FRONT]) 

521  { 

522  res_total = res_total; 

523  tri_list[i].m1 = LEAF_BACK; 

524  #if 0 

525  tri_to_process.Push( Array< int >(), tri_list[i].m2, tri_list[i].m3, tri_list[i].m4, 0); 

526  tri_to_process.Last().m1.Push(0); 

527  tri_list.Remove(i); 

528  break; 

529  #endif 

530  } 

531  else 

532  { 

533  for (; k < TEST_MAX; k++) 

534  { 

535  if (res_side[k] != LEAF_CURRENT) 

536  { 

537  tri_list[i].m1 = res_side[k]; 

538  break; 

539  } 

540  } 

541  if (k == TEST_MAX) 

542  tri_list[i].m1 = LEAF_FRONT; 

543  } 

544  } 

545  } 

546  

547  if (tri_list.Count() == 1) 

548  return 0; 

549  return 1; 

550  } 

551  

552  } /* namespace lol */ 

553  
