Loading [MathJax]/extensions/tex2jax.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
MeshPrimitives.cpp
1 #include <Core/Containers/Grid.hpp>
2 #include <Core/Geometry/MeshPrimitives.hpp>
3 #include <Core/Geometry/StandardAttribNames.hpp>
4 #include <Core/Math/Math.hpp> // areApproxEqual
5 #include <Core/Types.hpp>
6 
7 #include <array>
8 #include <string>
9 
10 namespace Ra {
11 namespace Core {
12 namespace Geometry {
13 
14 TriangleMesh makeXNormalQuad( const Vector2& halfExts,
15  const Utils::optional<Utils::Color>& color,
16  bool generateTexCoord ) {
17  Transform T = Transform::Identity();
18  T.linear().col( 0 ).swap( T.linear().col( 1 ) );
19  T.linear().col( 1 ).swap( T.linear().col( 2 ) );
20  return makePlaneGrid( 1, 1, halfExts, T, color, generateTexCoord );
21 }
22 
23 TriangleMesh makeYNormalQuad( const Vector2& halfExts,
24  const Utils::optional<Utils::Color>& color,
25  bool generateTexCoord ) {
26  Transform T = Transform::Identity();
27  T.linear().col( 1 ).swap( T.linear().col( 2 ) );
28  T.linear().col( 0 ).swap( T.linear().col( 1 ) );
29  return makePlaneGrid( 1, 1, halfExts, T, color, generateTexCoord );
30 }
31 
32 TriangleMesh makeZNormalQuad( const Vector2& halfExts,
33  const Utils::optional<Utils::Color>& color,
34  bool generateTexCoord ) {
35  return makePlaneGrid( 1, 1, halfExts, Transform::Identity(), color, generateTexCoord );
36 }
37 
38 TriangleMesh makeBox( const Vector3& halfExts, const Utils::optional<Utils::Color>& color ) {
39  Aabb aabb( -halfExts, halfExts );
40  return makeBox( aabb, color );
41 }
42 
43 TriangleMesh makeBox( const Aabb& aabb, const Utils::optional<Utils::Color>& color ) {
44  TriangleMesh result;
45  result.setVertices( { aabb.corner( Aabb::BottomLeftFloor ),
46  aabb.corner( Aabb::BottomRightFloor ),
47  aabb.corner( Aabb::TopLeftFloor ),
48  aabb.corner( Aabb::TopRightFloor ),
49  aabb.corner( Aabb::BottomLeftCeil ),
50  aabb.corner( Aabb::BottomRightCeil ),
51  aabb.corner( Aabb::TopLeftCeil ),
52  aabb.corner( Aabb::TopRightCeil ) } );
53 
54  static const Scalar a = 1_ra / std::sqrt( 3_ra );
55 
56  result.setNormals( { Vector3( -a, -a, -a ),
57  Vector3( +a, -a, -a ),
58  Vector3( -a, +a, -a ),
59  Vector3( +a, +a, -a ),
60  Vector3( -a, -a, +a ),
61  Vector3( +a, -a, +a ),
62  Vector3( -a, +a, +a ),
63  Vector3( +a, +a, +a ) } );
64 
65  result.setIndices( {
66  Vector3ui( 0, 2, 1 ),
67  Vector3ui( 1, 2, 3 ), // Floor
68  Vector3ui( 0, 1, 4 ),
69  Vector3ui( 4, 1, 5 ), // Front
70  Vector3ui( 3, 2, 6 ),
71  Vector3ui( 3, 6, 7 ), // Back
72  Vector3ui( 5, 1, 3 ),
73  Vector3ui( 5, 3, 7 ), // Right
74  Vector3ui( 2, 0, 4 ),
75  Vector3ui( 2, 4, 6 ), // Left
76  Vector3ui( 4, 5, 6 ),
77  Vector3ui( 6, 5, 7 ) // Top
78  } );
79 
80  if ( bool( color ) ) result.colorize( *color );
81  result.checkConsistency();
82 
83  return result;
84 }
85 
86 TriangleMesh makeSharpBox( const Vector3& halfExts,
87  const Utils::optional<Utils::Color>& color,
88  bool generateTexCoord ) {
89  Aabb aabb( -halfExts, halfExts );
90  return makeSharpBox( aabb, color, generateTexCoord );
91 }
92 
93 TriangleMesh makeSharpBox( const Aabb& aabb,
94  const Utils::optional<Utils::Color>& color,
95  bool generateTexCoord ) {
96  TriangleMesh result;
97  result.setVertices( { // Floor Face
98  aabb.corner( Aabb::BottomLeftFloor ),
99  aabb.corner( Aabb::TopLeftFloor ),
100  aabb.corner( Aabb::TopRightFloor ),
101  aabb.corner( Aabb::BottomRightFloor ),
102 
103  // Ceil Face
104  aabb.corner( Aabb::BottomLeftCeil ),
105  aabb.corner( Aabb::BottomRightCeil ),
106  aabb.corner( Aabb::TopRightCeil ),
107  aabb.corner( Aabb::TopLeftCeil ),
108 
109  // Left Face
110  aabb.corner( Aabb::TopLeftFloor ),
111  aabb.corner( Aabb::BottomLeftFloor ),
112  aabb.corner( Aabb::BottomLeftCeil ),
113  aabb.corner( Aabb::TopLeftCeil ),
114 
115  // Right Face
116  aabb.corner( Aabb::BottomRightFloor ),
117  aabb.corner( Aabb::TopRightFloor ),
118  aabb.corner( Aabb::TopRightCeil ),
119  aabb.corner( Aabb::BottomRightCeil ),
120 
121  // Bottom Face
122  aabb.corner( Aabb::BottomLeftFloor ),
123  aabb.corner( Aabb::BottomRightFloor ),
124  aabb.corner( Aabb::BottomRightCeil ),
125  aabb.corner( Aabb::BottomLeftCeil ),
126 
127  // Top face
128  aabb.corner( Aabb::TopLeftFloor ),
129  aabb.corner( Aabb::TopLeftCeil ),
130  aabb.corner( Aabb::TopRightCeil ),
131  aabb.corner( Aabb::TopRightFloor ) } );
132 
133  result.setNormals( { // Floor face
134  Vector3( 0, 0, -1 ),
135  Vector3( 0, 0, -1 ),
136  Vector3( 0, 0, -1 ),
137  Vector3( 0, 0, -1 ),
138  // Ceil Face
139  Vector3( 0, 0, +1 ),
140  Vector3( 0, 0, +1 ),
141  Vector3( 0, 0, +1 ),
142  Vector3( 0, 0, +1 ),
143  // Left Face
144  Vector3( -1, 0, 0 ),
145  Vector3( -1, 0, 0 ),
146  Vector3( -1, 0, 0 ),
147  Vector3( -1, 0, 0 ),
148  // Right Face
149  Vector3( +1, 0, 0 ),
150  Vector3( +1, 0, 0 ),
151  Vector3( +1, 0, 0 ),
152  Vector3( +1, 0, 0 ),
153  // Bottom Face
154  Vector3( 0, -1, 0 ),
155  Vector3( 0, -1, 0 ),
156  Vector3( 0, -1, 0 ),
157  Vector3( 0, -1, 0 ),
158  // Top Face
159  Vector3( 0, +1, 0 ),
160  Vector3( 0, +1, 0 ),
161  Vector3( 0, +1, 0 ),
162  Vector3( 0, +1, 0 ) } );
163 
164  if ( generateTexCoord ) {
165  Vector3Array texCoords {
166  // Floor face
167  Vector3( 0, 0, 0 ),
168  Vector3( 1, 0, 0 ),
169  Vector3( 1, 1, 0 ),
170  Vector3( 0, 1, 0 ),
171  // Ceil Face
172  Vector3( 0, 0, 0 ),
173  Vector3( 1, 0, 0 ),
174  Vector3( 1, 1, 0 ),
175  Vector3( 0, 1, 0 ),
176  // Left Face
177  Vector3( 0, 0, 0 ),
178  Vector3( 1, 0, 0 ),
179  Vector3( 1, 1, 0 ),
180  Vector3( 0, 1, 0 ),
181  // Right Face
182  Vector3( 0, 0, 0 ),
183  Vector3( 1, 0, 0 ),
184  Vector3( 1, 1, 0 ),
185  Vector3( 0, 1, 0 ),
186  // Bottom Face
187  Vector3( 0, 0, 0 ),
188  Vector3( 1, 0, 0 ),
189  Vector3( 1, 1, 0 ),
190  Vector3( 0, 1, 0 ),
191  // Top Face
192  Vector3( 0, 0, 0 ),
193  Vector3( 1, 0, 0 ),
194  Vector3( 1, 1, 0 ),
195  Vector3( 0, 1, 0 ),
196  };
197 
198  result.addAttrib( "in_texcoord", std::move( texCoords ) );
199  }
200 
201  result.setIndices( {
202 
203  Vector3ui( 0, 1, 2 ),
204  Vector3ui( 0, 2, 3 ), // Floor
205  Vector3ui( 4, 5, 6 ),
206  Vector3ui( 4, 6, 7 ), // Ceil
207  Vector3ui( 8, 9, 10 ),
208  Vector3ui( 8, 10, 11 ), // Left
209  Vector3ui( 12, 13, 14 ),
210  Vector3ui( 12, 14, 15 ), // Right
211  Vector3ui( 16, 17, 18 ),
212  Vector3ui( 16, 18, 19 ), // Bottom
213  Vector3ui( 20, 21, 22 ),
214  Vector3ui( 20, 22, 23 ) // Top
215  } );
216 
217  if ( bool( color ) ) result.colorize( *color );
218  result.checkConsistency();
219 
220  return result;
221 }
222 
223 TriangleMesh
224 makeGeodesicSphere( Scalar radius, uint numSubdiv, const Utils::optional<Utils::Color>& color ) {
225  TriangleMesh result;
226  uint faceCount = uint( std::pow( 4, numSubdiv ) ) * 20;
227 
228  TriangleMesh::PointAttribHandle::Container vertices;
229  TriangleMesh::NormalAttribHandle::Container normals;
230  TriangleMesh::IndexContainerType indices;
231  vertices.reserve( faceCount - 8 );
232  normals.reserve( faceCount - 8 );
233  indices.reserve( faceCount );
234 
235  // First, make an icosahedron.
236  // Top vertex
237  vertices.emplace_back( 0, 0, radius );
238  normals.emplace_back( 0, 0, 1 );
239 
240  const Scalar sq5_5 = radius * std::sqrt( 5_ra ) / 5_ra;
241 
242  // Middle vertices are on pentagons inscribed on a circle of radius 2*sqrt(5)
243  for ( int i = 0; i < 5; ++i ) {
244  for ( int j = 0; j < 2; ++j ) {
245  const Scalar theta = ( Scalar( i ) + ( j * 0.5_ra ) ) * Math::PiMul2 / 5_ra;
246 
247  const Scalar x = 2_ra * sq5_5 * std::cos( theta );
248  const Scalar y = 2_ra * sq5_5 * std::sin( theta );
249  const Scalar z = j == 0 ? sq5_5 : -sq5_5;
250  vertices.emplace_back( x, y, z );
251  normals.push_back( vertices.back().normalized() );
252  }
253  }
254 
255  // Bottom vertex
256  vertices.emplace_back( 0, 0, -radius );
257  normals.emplace_back( 0, 0, -1 );
258 
259  for ( int i = 0; i < 5; ++i ) {
260  uint i1 = ( i + 1 ) % 5;
261  // Top triangles
262  indices.emplace_back( 0, 2 * i + 1, ( 2 * i1 + 1 ) );
263 
264  // Bottom triangles
265  indices.emplace_back( 2 * i + 2, 11, ( 2 * i1 + 2 ) );
266  }
267  for ( uint i = 0; i < 10; ++i ) {
268  uint i1 = ( i + 0 ) % 10 + 1;
269  uint i2 = ( i + 1 ) % 10 + 1;
270  uint i3 = ( i + 2 ) % 10 + 1;
271  ( i % 2 ) ? indices.emplace_back( i3, i2, i1 ) : indices.emplace_back( i2, i3, i1 );
272  }
273 
274  for ( uint n = 0; n < numSubdiv; ++n ) {
275  VectorArray<Vector3ui> newTris; //= indices;
276  // Now subdivide every face into 4 triangles.
277  for ( uint i = 0; i < indices.size(); ++i ) {
278  const Vector3ui& tri = indices[i];
279  std::array<Vector3, 3> triVertices = {
280  vertices[tri[0]], vertices[tri[1]], vertices[tri[2]] };
281  std::array<uint, 3> middles;
282 
283  for ( uint v = 0; v < 3; ++v ) {
284  middles[v] = uint( vertices.size() );
285 
286  Vector3 vertex = 0.5_ra * ( triVertices[v] + triVertices[( v + 1 ) % 3] );
287  vertex.normalize();
288 
289  vertices.push_back( radius * vertex );
290  normals.push_back( vertex );
291  }
292 
293  newTris.emplace_back( tri[0], middles[0], middles[2] );
294  newTris.emplace_back( middles[0], tri[1], middles[1] );
295  newTris.emplace_back( middles[2], middles[1], tri[2] );
296  newTris.emplace_back( middles[0], middles[1], middles[2] );
297  }
298  indices = newTris;
299  }
300 
301  result.setVertices( std::move( vertices ) );
302  result.setNormals( std::move( normals ) );
303  result.setIndices( std::move( indices ) );
304  if ( bool( color ) ) result.colorize( *color );
305  result.checkConsistency();
306 
307  return result;
308 }
309 
310 TriangleMesh makeCylinder( const Vector3& a,
311  const Vector3& b,
312  Scalar radius,
313  uint sideSegments,
314  uint fillSegments,
315  const Utils::optional<Utils::Color>& color ) {
316  TriangleMesh result;
317 
318  TriangleMesh::PointAttribHandle::Container vertices;
319  TriangleMesh::NormalAttribHandle::Container normals;
320  TriangleMesh::IndexContainerType indices;
321 
322  const Vector3 ab = b - a;
323  const Vector3 dir = ab.normalized();
324 
325  // Create two circles normal centered on A and B and normal to ab (use dir, since first vector
326  // must be normalized)
327  Vector3 xPlane, yPlane;
328  Math::getOrthogonalVectors( dir, xPlane, yPlane );
329  xPlane.normalize();
330  yPlane.normalize();
331 
332  vertices.push_back( a );
333  normals.push_back( -dir );
334  vertices.push_back( b );
335  normals.push_back( dir );
336 
337  const Scalar thetaInc( Core::Math::PiMul2 / Scalar( sideSegments ) );
338  for ( uint i = 0; i < sideSegments; ++i ) {
339  const Scalar theta = i * thetaInc;
340  const Vector3 normal = std::cos( theta ) * xPlane + std::sin( theta ) * yPlane;
341 
342  // Even indices are A circle and odd indices are B circle.
343  vertices.push_back( a + radius * normal );
344  normals.push_back( -dir );
345 
346  vertices.push_back( b + radius * normal );
347  normals.push_back( dir );
348  }
349 
350  for ( uint i = 0; i < sideSegments; ++i ) {
351  uint bl = 2 * i + 2;
352  uint br = 2 + ( 2 * ( ( i + 1 ) % sideSegments ) );
353  uint tl = 2 * i + 3;
354  uint tr = 3 + ( 2 * ( ( i + 1 ) % sideSegments ) );
355  // order consistency (ccw face) here is important, e.g. when creating topomesh
356  indices.emplace_back( 0, br, bl );
357  indices.emplace_back( 1, tl, tr );
358  }
359 
360  // sew tube between circles.
361  const uint offset = vertices.size();
362  Vector3 c = a;
363  const Vector3 dh = ab / Scalar( fillSegments );
364  for ( uint j = 0; j <= fillSegments; ++j ) {
365  for ( uint i = 0; i < sideSegments; ++i ) {
366  const Scalar theta = i * thetaInc;
367  Vector3 normal = std::cos( theta ) * xPlane + std::sin( theta ) * yPlane;
368 
369  vertices.push_back( c + radius * normal );
370  normals.push_back( normal );
371  }
372  c += dh;
373  }
374 
375  for ( uint j = 0; j < fillSegments; ++j ) {
376  for ( uint i = 0; i < sideSegments; ++i ) {
377  uint i0 = offset + i + j * sideSegments;
378  uint i1 = offset + ( i + 1 ) % sideSegments + j * sideSegments;
379  uint i2 = i0 + sideSegments;
380  uint i3 = i1 + sideSegments;
381 
382  indices.emplace_back( i0, i1, i2 );
383  indices.emplace_back( i2, i1, i3 );
384  }
385  }
386 
387  result.setVertices( std::move( vertices ) );
388  result.setNormals( std::move( normals ) );
389  result.setIndices( std::move( indices ) );
390  if ( bool( color ) ) result.colorize( *color );
391  result.checkConsistency();
392 
393  return result;
394 }
395 
396 TriangleMesh makeCapsule( Scalar length,
397  Scalar radius,
398  uint nFaces,
399  const Utils::optional<Utils::Color>& color ) {
400  TriangleMesh result;
401 
402  TriangleMesh::PointAttribHandle::Container vertices;
403  TriangleMesh::NormalAttribHandle::Container normals;
404  TriangleMesh::IndexContainerType indices;
405 
406  vertices.reserve( nFaces * nFaces + nFaces + 2 );
407  normals.reserve( nFaces * nFaces + nFaces + 2 );
408  indices.reserve( 2 * ( nFaces * nFaces + nFaces ) );
409 
410  const Scalar l = length / 2_ra;
411 
412  // We create a capsule by joining a cylinder with two half spheres.
413 
414  // Part 1 : create the cylinder based on 3 circles
415  // Cylinder vertices.
416  const Scalar thetaInc( Core::Math::PiMul2 / Scalar( nFaces ) );
417  for ( uint i = 0; i < nFaces; ++i ) {
418  const Scalar theta = i * thetaInc;
419  Vector3 normal( std::cos( theta ), std::sin( theta ), 0 );
420 
421  // Create 3 circles
422  Vector3 vertex = radius * normal;
423  vertices.emplace_back( vertex[0], vertex[1], -l );
424  vertices.emplace_back( vertex[0], vertex[1], 0.0 );
425  vertices.emplace_back( vertex[0], vertex[1], l );
426 
427  normals.push_back( normal );
428  normals.push_back( normal );
429  normals.push_back( normal );
430  }
431 
432  // Cylinder side faces
433  for ( uint i = 0; i < nFaces; ++i ) {
434  uint bl = 3 * i; // bottom left corner of face
435  uint br = 3 * ( ( i + 1 ) % nFaces ); // bottom right corner of face
436  uint ml = bl + 1; // mid left
437  uint mr = br + 1; // mid right
438  uint tl = ml + 1; // top left
439  uint tr = mr + 1; // top right
440 
441  indices.emplace_back( bl, br, ml );
442  indices.emplace_back( br, mr, ml );
443 
444  indices.emplace_back( ml, mr, tl );
445  indices.emplace_back( mr, tr, tl );
446  }
447 
448  // Part 2 : create the bottom hemisphere.
449  const Scalar phiInc = Core::Math::Pi / Scalar( nFaces );
450  uint vert_count = uint( vertices.size() );
451 
452  // Bottom hemisphere vertices
453  for ( uint j = 1; j <= nFaces / 2 - 1; ++j ) {
454  const Scalar phi = Core::Math::PiDiv2 + j * phiInc;
455 
456  for ( uint i = 0; i < nFaces; ++i ) {
457  const Scalar theta = i * thetaInc;
458 
459  const Vector3 normal( std::cos( theta ) * std::sin( phi ),
460  std::sin( theta ) * std::sin( phi ),
461  std::cos( phi ) );
462 
463  Vector3 vertex = radius * normal;
464  vertex[2] -= l;
465 
466  vertices.push_back( vertex );
467  normals.push_back( normal );
468  }
469  }
470  // Add bottom point (south pole)
471  vertices.emplace_back( 0, 0, -( l + radius ) );
472  normals.emplace_back( 0, 0, -1 );
473 
474  // First ring of sphere triangles (joining with the cylinder)
475  for ( uint i = 0; i < nFaces; ++i ) {
476  uint bl = 3 * i;
477  uint br = 3 * ( ( i + 1 ) % nFaces );
478 
479  uint tl = vert_count + i;
480  uint tr = vert_count + ( i + 1 ) % nFaces;
481 
482  indices.emplace_back( br, bl, tl );
483  indices.emplace_back( br, tl, tr );
484  }
485 
486  // Next rings of the sphere triangles
487  for ( uint j = 0; j < ( nFaces / 2 ) - 2; ++j ) {
488  for ( uint i = 0; i < nFaces; ++i ) {
489  uint bl = vert_count + j * nFaces + i;
490  uint br = vert_count + j * nFaces + ( i + 1 ) % nFaces;
491 
492  uint tl = vert_count + ( j + 1 ) * nFaces + i;
493  uint tr = vert_count + ( j + 1 ) * nFaces + ( i + 1 ) % nFaces;
494 
495  indices.emplace_back( br, bl, tl );
496  indices.emplace_back( br, tl, tr );
497  }
498  }
499 
500  // End cap triangles, joining with the pole
501  for ( uint i = 0; i < nFaces; ++i ) {
502  const uint j = nFaces / 2 - 2;
503  uint bl = vert_count + j * nFaces + i;
504  uint br = vert_count + j * nFaces + ( i + 1 ) % nFaces;
505  uint bot = uint( vertices.size() - 1 );
506  indices.emplace_back( br, bl, bot );
507  }
508 
509  // Part 3 : create the top hemisphere
510  vert_count = uint( vertices.size() );
511 
512  // Top hemisphere vertices
513  for ( uint j = 1; j <= nFaces / 2 - 1; ++j ) {
514  const Scalar phi = Core::Math::PiDiv2 - j * phiInc;
515 
516  for ( uint i = 0; i < nFaces; ++i ) {
517  const Scalar theta = i * thetaInc;
518 
519  const Vector3 normal( std::cos( theta ) * std::sin( phi ),
520  std::sin( theta ) * std::sin( phi ),
521  std::cos( phi ) );
522 
523  Vector3 vertex = radius * normal;
524  vertex[2] += l;
525 
526  vertices.push_back( vertex );
527  normals.push_back( normal );
528  }
529  }
530 
531  // Add top point (north pole)
532  vertices.emplace_back( 0, 0, ( l + radius ) );
533  normals.emplace_back( 0, 0, 1 );
534 
535  // First ring of sphere triangles (joining with the cylinder)
536  for ( uint i = 0; i < nFaces; ++i ) {
537  uint bl = 3 * i + 2;
538  uint br = 3 * ( ( i + 1 ) % nFaces ) + 2;
539 
540  uint tl = vert_count + i;
541  uint tr = vert_count + ( i + 1 ) % nFaces;
542 
543  indices.emplace_back( bl, br, tl );
544  indices.emplace_back( br, tr, tl );
545  }
546 
547  // Next rigns of the sphere triangles
548  for ( uint j = 0; j < ( nFaces / 2 ) - 2; ++j ) {
549  for ( uint i = 0; i < nFaces; ++i ) {
550  uint bl = vert_count + j * nFaces + i;
551  uint br = vert_count + j * nFaces + ( i + 1 ) % nFaces;
552 
553  uint tl = vert_count + ( j + 1 ) * nFaces + i;
554  uint tr = vert_count + ( j + 1 ) * nFaces + ( i + 1 ) % nFaces;
555 
556  indices.emplace_back( bl, br, tl );
557  indices.emplace_back( br, tr, tl );
558  }
559  }
560 
561  // End cap triangles, joining with the pole
562  for ( uint i = 0; i < nFaces; ++i ) {
563  const uint j = nFaces / 2 - 2;
564  uint bl = vert_count + j * nFaces + i;
565  uint br = vert_count + j * nFaces + ( i + 1 ) % nFaces;
566  uint top = uint( vertices.size() ) - 1;
567  indices.emplace_back( bl, br, top );
568  }
569  result.setVertices( std::move( vertices ) );
570  result.setNormals( std::move( normals ) );
571  result.setIndices( std::move( indices ) );
572  if ( bool( color ) ) result.colorize( *color );
573  result.checkConsistency();
574 
575  return result;
576 }
577 
578 TriangleMesh makeTube( const Vector3& a,
579  const Vector3& b,
580  Scalar outerRadius,
581  Scalar innerRadius,
582  uint nFaces,
583  const Utils::optional<Utils::Color>& color ) {
584 
585  CORE_ASSERT( outerRadius > innerRadius, "Outer radius must be bigger than inner." );
586 
587  TriangleMesh result;
588  TriangleMesh::PointAttribHandle::Container vertices;
589  TriangleMesh::NormalAttribHandle::Container normals;
590  TriangleMesh::IndexContainerType indices;
591  vertices.reserve( 6 * nFaces );
592  normals.reserve( 6 * nFaces );
593  indices.reserve( 12 * nFaces );
594 
595  Vector3 ab = b - a;
596  Vector3 dir = ab.normalized();
597 
598  // Create two circles normal centered on A and B and normal to ab;
599  Vector3 xPlane, yPlane;
600  Math::getOrthogonalVectors( dir, xPlane, yPlane );
601  xPlane.normalize();
602  yPlane.normalize();
603 
604  Vector3 c = 0.5 * ( a + b );
605 
606  const Scalar thetaInc( Core::Math::PiMul2 / Scalar( nFaces ) );
607  for ( uint i = 0; i < nFaces; ++i ) {
608  const Scalar theta = i * thetaInc;
609 
610  Vector3 normal = std::cos( theta ) * xPlane + std::sin( theta ) * yPlane;
611 
612  vertices.push_back( a + outerRadius * normal );
613  vertices.push_back( c + outerRadius * normal );
614  vertices.push_back( b + outerRadius * normal );
615 
616  vertices.push_back( a + innerRadius * normal );
617  vertices.push_back( c + innerRadius * normal );
618  vertices.push_back( b + innerRadius * normal );
619 
620  normals.push_back( ( -dir + normal ).normalized() );
621  normals.push_back( normal );
622  normals.push_back( ( dir + normal ).normalized() );
623 
624  normals.push_back( ( -dir - normal ).normalized() );
625  normals.push_back( -normal );
626  normals.push_back( ( dir - normal ).normalized() );
627  }
628 
629  for ( uint i = 0; i < nFaces; ++i ) {
630  // Outer face.
631  uint obl = 6 * i; // bottom left corner of outer face
632  uint obr = 6 * ( ( i + 1 ) % nFaces ); // bottom right corner of outer face
633  uint oml = obl + 1; // mid left
634  uint omr = obr + 1; // mid right
635  uint otl = oml + 1; // top left
636  uint otr = omr + 1; // top right
637 
638  // Inner face
639  uint ibl = 6 * i + 3; // bottom left corner of inner face
640  uint ibr = 6 * ( ( i + 1 ) % nFaces ) + 3; // bottom right corner of inner face
641  uint iml = ibl + 1; // mid left
642  uint imr = ibr + 1; // mid right
643  uint itl = iml + 1; // top left
644  uint itr = imr + 1; // top right
645 
646  // Outer face triangles, just like a regular cylinder.
647 
648  indices.emplace_back( obl, obr, oml );
649  indices.emplace_back( obr, omr, oml );
650 
651  indices.emplace_back( oml, omr, otl );
652  indices.emplace_back( omr, otr, otl );
653 
654  // Inner face triangles (note how order is reversed because inner face points inwards).
655 
656  indices.emplace_back( ibr, ibl, iml );
657  indices.emplace_back( ibr, iml, imr );
658 
659  indices.emplace_back( imr, iml, itl );
660  indices.emplace_back( imr, itl, itr );
661 
662  // Bottom face quad
663  indices.emplace_back( ibr, obr, ibl );
664  indices.emplace_back( obl, ibl, obr );
665 
666  // Top face quad
667  indices.emplace_back( otr, itr, itl );
668  indices.emplace_back( itl, otl, otr );
669  }
670  result.setVertices( std::move( vertices ) );
671  result.setNormals( std::move( normals ) );
672  result.setIndices( std::move( indices ) );
673  if ( bool( color ) ) result.colorize( *color );
674  result.checkConsistency();
675 
676  return result;
677 }
678 
679 TriangleMesh makeCone( const Vector3& base,
680  const Vector3& tip,
681  Scalar radius,
682  uint nFaces,
683  const Utils::optional<Utils::Color>& color ) {
684  TriangleMesh result;
685  TriangleMesh::PointAttribHandle::Container vertices;
686  TriangleMesh::NormalAttribHandle::Container normals;
687  TriangleMesh::IndexContainerType indices;
688  vertices.reserve( 2 + nFaces );
689  normals.reserve( 2 + nFaces );
690  indices.reserve( 2 * nFaces );
691 
692  Vector3 ab = tip - base;
693  Vector3 dir = ab.normalized();
694 
695  // Create two circles normal centered on A and B and normal to ab;
696  Vector3 xPlane, yPlane;
697  Math::getOrthogonalVectors( dir, xPlane, yPlane );
698  xPlane.normalize();
699  yPlane.normalize();
700 
701  vertices.push_back( base );
702  vertices.push_back( tip );
703  normals.push_back( -dir );
704  normals.push_back( dir );
705 
706  const Scalar thetaInc( Core::Math::PiMul2 / Scalar( nFaces ) );
707  for ( uint i = 0; i < nFaces; ++i ) {
708  const Scalar theta = i * thetaInc;
709  Vector3 normal = std::cos( theta ) * xPlane + std::sin( theta ) * yPlane;
710 
711  vertices.push_back( base + radius * normal );
712  normals.push_back( ( normal - dir ).normalized() );
713  }
714 
715  for ( uint i = 0; i < nFaces; ++i ) {
716  uint bl = i + 2; // bottom left corner of face
717  uint br = ( ( i + 1 ) % nFaces ) + 2; // bottom right corner of face
718 
719  indices.emplace_back( 0, br, bl );
720  indices.emplace_back( 1, bl, br );
721  }
722  result.setVertices( std::move( vertices ) );
723  result.setNormals( std::move( normals ) );
724  result.setIndices( std::move( indices ) );
725  if ( bool( color ) ) result.colorize( *color );
726  result.checkConsistency();
727 
728  return result;
729 }
730 
731 TriangleMesh makePlaneGrid( const uint rows,
732  const uint cols,
733  const Vector2& halfExts,
734  const Transform& T,
735  const Utils::optional<Utils::Color>& color,
736  bool generateTexCoord ) {
737  TriangleMesh result;
738  TriangleMesh::PointAttribHandle::Container vertices;
739  TriangleMesh::NormalAttribHandle::Container normals;
740  TriangleMesh::IndexContainerType indices;
741  Ra::Core::Vector3Array texCoords;
742 
743  const uint R = ( rows + 1 );
744  const uint C = ( cols + 1 );
745  const uint v_size = C * R;
746  const uint t_size = 2 * cols * rows;
747 
748  vertices.resize( v_size );
749  normals.resize( v_size );
750  indices.reserve( t_size );
751  texCoords.reserve( v_size );
752 
753  const Vector3 X = T.linear().col( 0 ).normalized();
754  const Vector3 Y = T.linear().col( 1 ).normalized();
755  const Vector3 Z = T.linear().col( 2 ).normalized();
756 
757  const Vector3 x = ( 2_ra * halfExts[0] * X ) / Scalar( cols );
758  const Vector3 y = ( 2_ra * halfExts[1] * Y ) / Scalar( rows );
759  const Vector3 o = T.translation() - ( halfExts[0] * X ) - ( halfExts[1] * Y );
760 
761  const Scalar du = 1_ra / rows;
762  const Scalar dv = 1_ra / cols;
763 
764  Grid<uint, 2> v( { R, C } );
765  for ( uint i = 0; i < R; ++i ) {
766  for ( uint j = 0; j < C; ++j ) {
767  const uint id = ( i * C ) + j;
768  v.at( { i, j } ) = id;
769  vertices[id] = o + ( i * y ) + ( j * x );
770  normals[id] = Z;
771  texCoords.emplace_back( i * du, j * dv, 0_ra );
772  }
773  }
774 
775  for ( uint i = 0; i < rows; ++i ) {
776  for ( uint j = 0; j < cols; ++j ) {
777  indices.emplace_back(
778  v.at( { i, j } ), v.at( { i, j + 1 } ), v.at( { i + 1, j + 1 } ) );
779  indices.emplace_back(
780  v.at( { i, j } ), v.at( { i + 1, j + 1 } ), v.at( { i + 1, j } ) );
781  }
782  }
783  result.setVertices( std::move( vertices ) );
784  result.setNormals( std::move( normals ) );
785  result.setIndices( std::move( indices ) );
786  if ( generateTexCoord )
787  result.addAttrib( getAttribName( MeshAttrib::VERTEX_TEXCOORD ), std::move( texCoords ) );
788  if ( bool( color ) ) result.colorize( *color );
789  result.checkConsistency();
790 
791  return result;
792 }
793 } // namespace Geometry
794 } // namespace Core
795 } // namespace Ra
void getOrthogonalVectors(const Vector3 &fx, Eigen::Ref< Vector3 > fy, Eigen::Ref< Vector3 > fz)
Definition: Cage.cpp:3