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>
7 inline CustomTriangleIndexLayer() :
8 Ra::Core::
Geometry::TriangleIndexLayer( staticSemanticName ) {}
9 static constexpr const char* staticSemanticName =
"CustomSemantic";
12TEST_CASE(
"Core/Geometry/IndexedGeometry",
"[unittests][Core][Core/Geometry][IndexedGeometry]" ) {
13 using Ra::Core::Vector3;
14 using namespace Ra::Core::Geometry;
20 TriangleMesh mesh = Ra::Core::Geometry::makeBox();
30 REQUIRE( geo.containsLayer( TriangleIndexLayer::staticSemanticName ) );
31 auto [key, layer] = geo.getFirstLayerOccurrence( TriangleIndexLayer::staticSemanticName );
33 tilSemantics = layer.semantics();
39 auto pil = std::make_unique<PointCloudIndexLayer>();
41 pil->linearIndices( geo );
43 pilSemantics = pil->semantics();
45 bool layerAdded = geo.addLayer(
std::move( pil ) ).first;
47 REQUIRE( layerAdded );
49 keys.
insert( { pilSemantics,
"" } );
52 REQUIRE( geo.containsLayer( tilSemantics ) );
53 REQUIRE( geo.containsLayer( pilSemantics ) );
54 REQUIRE( geo.containsLayer( TriangleIndexLayer::staticSemanticName ) );
55 REQUIRE( geo.containsLayer( PointCloudIndexLayer::staticSemanticName ) );
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 );
63 auto cil = std::make_unique<CustomTriangleIndexLayer>();
64 cil->collection() = mesh.getIndices();
65 cilSemantics = cil->semantics();
67 REQUIRE( !geo.containsLayer( cilSemantics ) );
68 REQUIRE( geo.countLayers( cilSemantics ) == 0 );
70 REQUIRE( geo.addLayer(
std::move( cil ) ).first );
71 keys.
insert( { cilSemantics,
"" } );
73 REQUIRE( geo.containsLayer( cilSemantics ) );
74 REQUIRE( geo.containsLayer( TriangleIndexLayer::staticSemanticName ) );
75 REQUIRE( geo.containsLayer( PointCloudIndexLayer::staticSemanticName ) );
76 REQUIRE( geo.containsLayer( CustomTriangleIndexLayer::staticSemanticName ) );
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 );
86 REQUIRE( keys.
size() != 0 );
88 for (
const auto& k : geo.layerKeys() ) {
89 REQUIRE( keys.
erase( k ) == 1 );
90 REQUIRE( geo.countLayers( k ) == 1 );
93 REQUIRE( keys.
size() == 0 );
96TEST_CASE(
"Core/Geometry/IndexedGeometry/Attributes",
97 "[unittests][Core][Core/Geometry][IndexedGeometry]" ) {
98 using Ra::Core::Vector3;
99 using namespace Ra::Core::Geometry;
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 ) );
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 ) );
122 auto handleFilled = mesh.addAttrib<Vec3AttribHandle::value_type>(
"filled" );
123 auto& attribFilled = mesh.getAttrib( handleFilled );
124 auto& containerFilled = attribFilled.getDataWithLock();
125 REQUIRE( attribFilled.isLocked() );
128 for (
int i = 0; i != int( mesh.vertices().size() ); ++i )
129 containerFilled.push_back( Vec3AttribHandle::value_type::Random() );
130 attribFilled.unlock();
132 auto handleFilled2 = mesh.getAttribHandle<Vec3AttribHandle::value_type>(
"filled" );
133 const auto& containerFilled2 = mesh.getAttrib( handleFilled2 ).data();
134 REQUIRE( containerFilled == containerFilled2 );
136 mesh.removeAttrib( handleFilled );
139 auto handle = mesh.addAttrib<Eigen::Matrix<unsigned int, 1, 1>>(
"filled2" );
140 auto& container3 = mesh.getAttrib( handle ).getDataWithLock();
141 using HandleType =
decltype( handle );
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 );
149 auto invalid = mesh.getAttribHandle<
float>(
"toto" );
150 REQUIRE( !mesh.isValid( invalid ) );
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 ) );
163 using Ra::Core::Geometry::TriangleMesh;
165 TriangleMesh::PointAttribHandle::Container vertices;
166 TriangleMesh::NormalAttribHandle::Container normals;
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 } );
179 m.setIndices( { { 0, 1, 2 } } );
181 auto handle1 = m.addAttrib<Vector3>(
"vector3_attrib" );
182 auto& attrib1 = m.getAttrib( handle1 );
183 auto& buf = attrib1.getDataWithLock();
186 buf.push_back( { 1, 1, 1 } );
187 buf.push_back( { 2, 2, 2 } );
188 buf.push_back( { 3, 3, 3 } );
191 auto handle2 = m.addAttrib<
float>(
"float_attrib" );
192 auto& attrib2 = m.getAttrib( handle2 );
193 attrib2.setData( { 1.f, 2.f, 3.f } );
196 m2.copyBaseGeometry( m );
197 m2.copyAttributes( m, handle1 );
200 m2.copyAttributes( m, handle2 );
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 ) );
218 auto& attrMgr = m2.vertexAttribs();
220 REQUIRE( attrMgr.getAttribPtr( handle1 ) == attrMgr.getAttribBase( attribM2_1.getName() ) );
221 REQUIRE(
nullptr == attrMgr.getAttribBase(
"unkown" ) );
228 auto& t = b->
cast<Vector3>();
229 const void* p1 = t.dataPtr();
230 const void* p2 = attribM2_1.dataPtr();
237 REQUIRE( t.
dataPtr() == attribM2_2.dataPtr() );
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 );
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;
254 TriangleMesh::PointAttribHandle::Container vertices;
255 TriangleMesh::NormalAttribHandle::Container normals;
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 } );
268 m.setIndices( { { 0, 1, 2 } } );
270 auto handle1 = m.addAttrib<Vector2>(
"vector2_attrib" );
271 auto& attrib1 = m.getAttrib( handle1 );
272 auto& buf1 = attrib1.getDataWithLock();
275 buf1.push_back( { 1, 1 } );
276 buf1.push_back( { 2, 2 } );
277 buf1.push_back( { 3, 3 } );
280 auto handle2 = m.addAttrib<Scalar>(
"float_attrib" );
281 auto& attrib2 = m.getAttrib( handle2 );
282 attrib2.setData( { 1.f, 2.f, 3.f } );
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();
290 for (
int val = 1; val <= 3; ++val ) {
292 v << val, val, val, val, val;
299 m3.copyBaseGeometry( m );
301 m3.copyAllAttributes( m );
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() ) );
310 auto handleM3 = m3.addAttrib(
"vector5_attrib", m.getAttrib( handle3 ).data() );
312 REQUIRE( m3.vertexAttribs().hasSameAttribs( m.vertexAttribs() ) );
313 REQUIRE( m.vertexAttribs().hasSameAttribs( m3.vertexAttribs() ) );
315 m3.getAttribBase(
"vector5_attrib" )->setName(
"better" );
316 REQUIRE( m3.getAttrib( handleM3 ).getName() ==
"better" );
AbstractGeometry with per-vertex attributes and layers of indices. Each layer represents a different ...
Attrib< T > & cast()
Downcast from AttribBase to Attrib<T>.
const void * dataPtr() const override
Object associated with one or multiple semantic names.
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet
Index layer for triangle mesh.