Radium Engine  1.5.20
Loading...
Searching...
No Matches
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
10namespace Ra {
11namespace Core {
12namespace Geometry {
13
14TriangleMesh 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
23TriangleMesh 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
32TriangleMesh 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
38TriangleMesh makeBox( const Vector3& halfExts, const Utils::optional<Utils::Color>& color ) {
39 Aabb aabb( -halfExts, halfExts );
40 return makeBox( aabb, color );
41}
42
43TriangleMesh 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
86TriangleMesh 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
93TriangleMesh 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
223TriangleMesh
224makeGeodesicSphere( 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
310TriangleMesh 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
396TriangleMesh 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
578TriangleMesh 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
679TriangleMesh 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
731TriangleMesh 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
T cos(T... args)
T move(T... args)
void getOrthogonalVectors(const Vector3 &fx, Eigen::Ref< Vector3 > fy, Eigen::Ref< Vector3 > fz)
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T pow(T... args)
T sin(T... args)
T sqrt(T... args)