Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
indexview.cpp
1#include <Core/Geometry/IndexedGeometry.hpp>
2#include <Core/Geometry/MeshPrimitives.hpp>
3#include <Core/Geometry/StandardAttribNames.hpp>
4#include <catch2/catch_test_macros.hpp>
5
6struct CustomTriangleIndexLayer : public Ra::Core::Geometry::TriangleIndexLayer {
7 inline CustomTriangleIndexLayer() :
8 Ra::Core::Geometry::TriangleIndexLayer( staticSemanticName ) {}
9 static constexpr const char* staticSemanticName = "CustomSemantic";
10};
11
12TEST_CASE( "Core/Geometry/IndexedGeometry", "[unittests][Core][Core/Geometry][IndexedGeometry]" ) {
13 using Ra::Core::Vector3;
14 using namespace Ra::Core::Geometry;
16
17 // Store keys of the layers that should be in the geometry
19
20 TriangleMesh mesh = Ra::Core::Geometry::makeBox();
21
22 // copy AttribArrayGeometry;
23 MultiIndexedGeometry geo( mesh );
24
25 // Create triangle Layer
27 {
28 // TriangleMesh is a MultiIndexedGeometry, so the layer has already
29 // been added
30 REQUIRE( geo.containsLayer( TriangleIndexLayer::staticSemanticName ) );
31 auto [key, layer] = geo.getFirstLayerOccurrence( TriangleIndexLayer::staticSemanticName );
32 keys.insert( key );
33 tilSemantics = layer.semantics();
34 }
35
37 {
39 auto pil = std::make_unique<PointCloudIndexLayer>();
40 // fill indices as linspace
41 pil->linearIndices( geo );
42 // optional: save semantics for later
43 pilSemantics = pil->semantics();
44 // insert with default name
45 bool layerAdded = geo.addLayer( std::move( pil ) ).first;
47 REQUIRE( layerAdded );
48
49 keys.insert( { pilSemantics, "" } );
50 }
51
52 REQUIRE( geo.containsLayer( tilSemantics ) );
53 REQUIRE( geo.containsLayer( pilSemantics ) );
54 REQUIRE( geo.containsLayer( TriangleIndexLayer::staticSemanticName ) );
55 REQUIRE( geo.containsLayer( PointCloudIndexLayer::staticSemanticName ) );
56
57 REQUIRE( geo.countLayers( tilSemantics ) == 1 );
58 REQUIRE( geo.countLayers( pilSemantics ) == 1 );
59 REQUIRE( geo.countLayers( TriangleIndexLayer::staticSemanticName ) == 1 );
60 REQUIRE( geo.countLayers( PointCloudIndexLayer::staticSemanticName ) == 1 );
61
63 auto cil = std::make_unique<CustomTriangleIndexLayer>();
64 cil->collection() = mesh.getIndices();
65 cilSemantics = cil->semantics();
66
67 REQUIRE( !geo.containsLayer( cilSemantics ) );
68 REQUIRE( geo.countLayers( cilSemantics ) == 0 );
69
70 REQUIRE( geo.addLayer( std::move( cil ) ).first );
71 keys.insert( { cilSemantics, "" } );
72
73 REQUIRE( geo.containsLayer( cilSemantics ) );
74 REQUIRE( geo.containsLayer( TriangleIndexLayer::staticSemanticName ) );
75 REQUIRE( geo.containsLayer( PointCloudIndexLayer::staticSemanticName ) );
76 REQUIRE( geo.containsLayer( CustomTriangleIndexLayer::staticSemanticName ) );
77
78 REQUIRE( geo.countLayers( tilSemantics ) == 1 );
79 REQUIRE( geo.countLayers( pilSemantics ) == 1 );
80 REQUIRE( geo.countLayers( cilSemantics ) == 1 );
81 REQUIRE( geo.countLayers( TriangleIndexLayer::staticSemanticName ) == 2 );
82 REQUIRE( geo.countLayers( PointCloudIndexLayer::staticSemanticName ) == 1 );
83 REQUIRE( geo.countLayers( CustomTriangleIndexLayer::staticSemanticName ) == 1 );
84
85 // Check layer keys iterator: we should traverse all keys
86 REQUIRE( keys.size() != 0 );
88 for ( const auto& k : geo.layerKeys() ) {
89 REQUIRE( keys.erase( k ) == 1 );
90 REQUIRE( geo.countLayers( k ) == 1 );
91 }
93 REQUIRE( keys.size() == 0 );
94}
95
96TEST_CASE( "Core/Geometry/IndexedGeometry/Attributes",
97 "[unittests][Core][Core/Geometry][IndexedGeometry]" ) {
98 using Ra::Core::Vector3;
99 using namespace Ra::Core::Geometry;
101 using Vec3AttribHandle = AttribArrayGeometry::Vec3AttribHandle;
102
103 MultiIndexedGeometry mesh( Ra::Core::Geometry::makeBox() );
104
105 // base attributes are automatically added
106 auto h_pos = mesh.getAttribHandle<Vector3>( getAttribName( VERTEX_POSITION ) );
107 REQUIRE( mesh.isValid( h_pos ) );
108 auto h_nor = mesh.getAttribHandle<Vector3>( getAttribName( VERTEX_NORMAL ) );
109 REQUIRE( mesh.isValid( h_nor ) );
110
111 // Add/Remove attributes without filling it
112 auto handleEmpty = mesh.addAttrib<Vec3AttribHandle::value_type>( "empty" );
113 mesh.removeAttrib( handleEmpty );
114 REQUIRE( !mesh.isValid( handleEmpty ) );
115 handleEmpty = mesh.addAttrib<Vec3AttribHandle::value_type>( "empty" );
116 REQUIRE( mesh.isValid( handleEmpty ) );
117 mesh.removeAttrib( handleEmpty );
118 handleEmpty = mesh.getAttribHandle<Vec3AttribHandle::value_type>( "empty" );
119 REQUIRE( !mesh.isValid( handleEmpty ) );
120
121 // Test access to the attribute container
122 auto handleFilled = mesh.addAttrib<Vec3AttribHandle::value_type>( "filled" );
123 auto& attribFilled = mesh.getAttrib( handleFilled );
124 auto& containerFilled = attribFilled.getDataWithLock();
125 REQUIRE( attribFilled.isLocked() );
126
127 // Test filling and removing vec3 attributes
128 for ( int i = 0; i != int( mesh.vertices().size() ); ++i )
129 containerFilled.push_back( Vec3AttribHandle::value_type::Random() );
130 attribFilled.unlock();
131
132 auto handleFilled2 = mesh.getAttribHandle<Vec3AttribHandle::value_type>( "filled" );
133 const auto& containerFilled2 = mesh.getAttrib( handleFilled2 ).data();
134 REQUIRE( containerFilled == containerFilled2 );
135
136 mesh.removeAttrib( handleFilled );
137
138 // Test attribute creation by type, filling and removal
139 auto handle = mesh.addAttrib<Eigen::Matrix<unsigned int, 1, 1>>( "filled2" );
140 auto& container3 = mesh.getAttrib( handle ).getDataWithLock();
141 using HandleType = decltype( handle );
142
143 for ( int i = 0; i != int( mesh.vertices().size() ); ++i )
144 container3.push_back( typename HandleType::value_type( i ) );
145 mesh.getAttrib( handle ).unlock();
146 mesh.removeAttrib( handle );
147
148 // Test dummy handle
149 auto invalid = mesh.getAttribHandle<float>( "toto" );
150 REQUIRE( !mesh.isValid( invalid ) );
151
152 // Test attribute copy
153 const auto v0 = mesh.vertices()[0];
154 TriangleMesh meshCopy;
155 meshCopy.copy( mesh );
156 REQUIRE( mesh.vertices()[0].isApprox( v0 ) );
157 meshCopy.verticesWithLock()[0] += Ra::Core::Vector3( 0.5, 0.5, 0.5 );
158 meshCopy.verticesUnlock();
159 REQUIRE( !meshCopy.vertices()[0].isApprox( v0 ) );
160
161 // For the documentation in doc/developer/mesh.md
163 using Ra::Core::Geometry::TriangleMesh;
164 TriangleMesh m;
165 TriangleMesh::PointAttribHandle::Container vertices;
166 TriangleMesh::NormalAttribHandle::Container normals;
168
169 vertices.push_back( { 0, 0, 0 } );
170 vertices.push_back( { 1, 0, 0 } );
171 vertices.push_back( { 0, 2, 0 } );
172 normals.push_back( { 0, 0, 1 } );
173 normals.push_back( { 0, 0, 1 } );
174 normals.push_back( { 0, 0, 1 } );
175
176 m.setVertices( std::move( vertices ) );
177 m.setNormals( std::move( normals ) );
178
179 m.setIndices( { { 0, 1, 2 } } );
180
181 auto handle1 = m.addAttrib<Vector3>( "vector3_attrib" );
182 auto& attrib1 = m.getAttrib( handle1 );
183 auto& buf = attrib1.getDataWithLock();
184
185 buf.reserve( 3 );
186 buf.push_back( { 1, 1, 1 } );
187 buf.push_back( { 2, 2, 2 } );
188 buf.push_back( { 3, 3, 3 } );
189 attrib1.unlock();
190
191 auto handle2 = m.addAttrib<float>( "float_attrib" );
192 auto& attrib2 = m.getAttrib( handle2 );
193 attrib2.setData( { 1.f, 2.f, 3.f } );
194
195 TriangleMesh m2;
196 m2.copyBaseGeometry( m );
197 m2.copyAttributes( m, handle1 );
199
200 m2.copyAttributes( m, handle2 );
201
202 auto& attribM2_1 = m2.getAttrib( handle1 );
203 auto& attribM2_2 = m2.getAttrib( handle2 );
204 REQUIRE( attribM2_1.getSize() == 3 );
205 REQUIRE( attribM2_1.getNumberOfComponents() == 3 );
206 REQUIRE( attribM2_1.getStride() == sizeof( Vector3 ) );
207 REQUIRE( attribM2_1.getBufferSize() == 3 * sizeof( Vector3 ) );
208 attribM2_1.resize( 10 );
209 REQUIRE( attribM2_1.getSize() == 10 );
210 REQUIRE( attribM2_1.getNumberOfComponents() == 3 );
211 REQUIRE( attribM2_1.getStride() == sizeof( Vector3 ) );
212 REQUIRE( attribM2_1.getBufferSize() == 10 * sizeof( Vector3 ) );
213 REQUIRE( attribM2_2.getSize() == 3 );
214 REQUIRE( attribM2_2.getNumberOfComponents() == 1 );
215 REQUIRE( attribM2_2.getStride() == sizeof( float ) );
216 REQUIRE( attribM2_2.getBufferSize() == 3 * sizeof( float ) );
217
218 auto& attrMgr = m2.vertexAttribs();
219
220 REQUIRE( attrMgr.getAttribPtr( handle1 ) == attrMgr.getAttribBase( attribM2_1.getName() ) );
221 REQUIRE( nullptr == attrMgr.getAttribBase( "unkown" ) );
222
223 int cpt = 0;
224 attrMgr.for_each_attrib( [&cpt, &attribM2_1, &attribM2_2]( Ra::Core::Utils::AttribBase* b ) {
225 cpt++;
226 // 3 since we want to skip position and normals
227 if ( cpt == 3 ) {
228 auto& t = b->cast<Vector3>();
229 const void* p1 = t.dataPtr();
230 const void* p2 = attribM2_1.dataPtr();
231 REQUIRE( p1 == p2 );
232 }
233 if ( cpt == 4 ) {
234 // const to check const cast;
235 const Ra::Core::Utils::AttribBase* cb = b;
236 const Ra::Core::Utils::Attrib<float>& t = cb->cast<float>();
237 REQUIRE( t.dataPtr() == attribM2_2.dataPtr() );
238 }
239 } );
240 REQUIRE( cpt == attrMgr.getNumAttribs() );
241 const Ra::Core::Utils::AttribHandle<float>::Container newData { 0.f, 1.f, 2.f };
242 attrMgr.setAttrib( handle2, newData );
243 REQUIRE( m2.getAttrib( handle2 ).data() == newData );
244}
245
246TEST_CASE( "Core/Geometry/IndexedGeometry/CopyAllAttributes",
247 "[unittests][Core][Core/Geometry][IndexedGeometry]" ) {
248 using Ra::Core::Vector2;
249 using Ra::Core::Vector3;
250 using namespace Ra::Core::Geometry;
251 using Ra::Core::Geometry::TriangleMesh;
252
253 TriangleMesh m;
254 TriangleMesh::PointAttribHandle::Container vertices;
255 TriangleMesh::NormalAttribHandle::Container normals;
257
258 vertices.push_back( { 0, 0, 0 } );
259 vertices.push_back( { 1, 0, 0 } );
260 vertices.push_back( { 0, 2, 0 } );
261 normals.push_back( { 0, 0, 1 } );
262 normals.push_back( { 0, 0, 1 } );
263 normals.push_back( { 0, 0, 1 } );
264
265 m.setVertices( std::move( vertices ) );
266 m.setNormals( std::move( normals ) );
267
268 m.setIndices( { { 0, 1, 2 } } );
269
270 auto handle1 = m.addAttrib<Vector2>( "vector2_attrib" );
271 auto& attrib1 = m.getAttrib( handle1 );
272 auto& buf1 = attrib1.getDataWithLock();
273
274 buf1.reserve( 3 );
275 buf1.push_back( { 1, 1 } );
276 buf1.push_back( { 2, 2 } );
277 buf1.push_back( { 3, 3 } );
278 attrib1.unlock();
279
280 auto handle2 = m.addAttrib<Scalar>( "float_attrib" );
281 auto& attrib2 = m.getAttrib( handle2 );
282 attrib2.setData( { 1.f, 2.f, 3.f } );
283
284 using Vector5 = Eigen::Matrix<Scalar, 5, 1>;
285 auto handle3 = m.addAttrib<Vector5>( "vector5_attrib" );
286 auto& attrib3 = m.getAttrib( handle3 );
287 auto& buf3 = attrib3.getDataWithLock();
288
289 buf3.reserve( 3 );
290 for ( int val = 1; val <= 3; ++val ) {
291 Vector5 v;
292 v << val, val, val, val, val;
293 buf3.push_back( v );
294 }
295 attrib3.unlock();
296
297 TriangleMesh m3;
298
299 m3.copyBaseGeometry( m );
300
301 m3.copyAllAttributes( m );
302
303 REQUIRE( m3.getAttribHandle<Vector3>( attrib1.getName() ).idx().isValid() );
304 REQUIRE( m3.getAttribHandle<Scalar>( attrib2.getName() ).idx().isValid() );
305 REQUIRE( m3.getAttribHandle<Vector5>( attrib3.getName() ).idx().isValid() );
306 REQUIRE( m3.vertexAttribs().hasSameAttribs( m.vertexAttribs() ) );
307 REQUIRE( m.vertexAttribs().hasSameAttribs( m3.vertexAttribs() ) );
308
309 // but we can copy it explicitly
310 auto handleM3 = m3.addAttrib( "vector5_attrib", m.getAttrib( handle3 ).data() );
311
312 REQUIRE( m3.vertexAttribs().hasSameAttribs( m.vertexAttribs() ) );
313 REQUIRE( m.vertexAttribs().hasSameAttribs( m3.vertexAttribs() ) );
314
315 m3.getAttribBase( "vector5_attrib" )->setName( "better" );
316 REQUIRE( m3.getAttrib( handleM3 ).getName() == "better" );
317}
AbstractGeometry with per-vertex attributes and layers of indices. Each layer represents a different ...
Attrib< T > & cast()
Downcast from AttribBase to Attrib<T>.
Definition Attribs.hpp:469
const void * dataPtr() const override
Definition Attribs.hpp:513
Object associated with one or multiple semantic names.
T erase(T... args)
T insert(T... args)
T move(T... args)
@ 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:4
T push_back(T... args)
T size(T... args)
Index layer for triangle mesh.