diff --git a/Shaders/Lines/Wireframe.geom.glsl b/Shaders/Lines/Wireframe.geom.glsl index 3933f78bcdf..2698dc8aa21 100644 --- a/Shaders/Lines/Wireframe.geom.glsl +++ b/Shaders/Lines/Wireframe.geom.glsl @@ -20,6 +20,7 @@ in vec4 vPosition[2]; out vec4 gColor; out float pixelWidthDiv2; uniform vec2 viewport; +uniform float pixelWidth; vec4 vColor[2]; void main() { @@ -34,7 +35,6 @@ void main() { vec4 scss1 = css1 * css0.w; vec3 dir = ( scss1 - scss0 ).xyz; - float pixelWidth = 1.8; const float border = 4.; vec3 slope = normalize( vec3( -dir.y, dir.x, 0 ) ); vec4 n = vec4( vec3( pixelWidth + border ) / vp * slope, 0 ); diff --git a/examples/DrawPrimitives/AllPrimitivesComponent.cpp b/examples/DrawPrimitives/AllPrimitivesComponent.cpp index 3437251299c..0e18e1ff7bd 100644 --- a/examples/DrawPrimitives/AllPrimitivesComponent.cpp +++ b/examples/DrawPrimitives/AllPrimitivesComponent.cpp @@ -131,18 +131,18 @@ void AllPrimitivesComponent::initialize() { //// CUBES //// if ( ENABLE_CUBES ) { - std::shared_ptr cube1( new Mesh( "Cube" ) ); + std::shared_ptr cube1( new GeometryDisplayable( "Cube" ) ); auto coord = cellSize / 16_ra; cube1->loadGeometry( - Geometry::makeSharpBox( Vector3 { coord, coord, coord }, Color::Green() ) ); + Geometry::makeSharpBox2( Vector3 { coord, coord, coord }, Color::Green() ) ); auto renderObject1 = RenderObject::createRenderObject( "Cube1", this, RenderObjectType::Geometry, cube1, {} ); renderObject1->setLocalTransform( Transform { Translation( cellCorner ) } ); renderObject1->setMaterial( blinnPhongMaterial ); addRenderObject( renderObject1 ); - std::shared_ptr texCube( new Mesh( "Cube" ) ); - texCube->loadGeometry( Geometry::makeSharpBox( + std::shared_ptr texCube( new GeometryDisplayable( "Cube" ) ); + texCube->loadGeometry( Geometry::makeSharpBox2( Vector3 { 1.2_ra * coord, 1.2_ra * coord, 1.2_ra * coord }, Color::White(), true ) ); auto renderObjectTexCube = RenderObject::createRenderObject( "TexCube", this, RenderObjectType::Geometry, texCube, {} ); @@ -153,14 +153,15 @@ void AllPrimitivesComponent::initialize() { addRenderObject( renderObjectTexCube ); // another cube - std::shared_ptr cube2( new Mesh( "Cube" ) ); + std::shared_ptr cube2( new GeometryDisplayable( "Cube" ) ); coord = cellSize / 4_ra; - cube2->loadGeometry( Geometry::makeSharpBox( Vector3 { coord, coord, coord } ) ); + cube2->loadGeometry( Geometry::makeSharpBox2( Vector3 { coord, coord, coord } ) ); + const std::string myColourName { "colour" }; cube2->getCoreGeometry().addAttrib( myColourName, Vector4Array { cube2->getNumVertices(), Color::Red() } ); - cube2->setAttribNameCorrespondance( + cube2->setAttribNameMatching( myColourName, Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_COLOR ) ); auto renderObject2 = RenderObject::createRenderObject( "CubeRO_2", this, RenderObjectType::Geometry, cube2, {} ); diff --git a/examples/HelloRadium/main.cpp b/examples/HelloRadium/main.cpp index 8c149f2a9b3..c7461ae86df 100644 --- a/examples/HelloRadium/main.cpp +++ b/examples/HelloRadium/main.cpp @@ -27,7 +27,7 @@ int main( int argc, char* argv[] ) { //! [Verifying the OpenGL version available to the engine] //! [Creating the cube] - auto cube = Ra::Core::Geometry::makeSharpBox( { 0.1f, 0.1f, 0.1f } ); + auto cube { std::move( Ra::Core::Geometry::makeSharpBox2( { 0.1f, 0.1f, 0.1f } ) ) }; //! [Creating the cube] //! [Colorize the Cube] @@ -41,8 +41,8 @@ int main( int argc, char* argv[] ) { //! [Create the engine entity for the cube] //! [Create a geometry component with the cube] - auto c = - new Ra::Engine::Scene::TriangleMeshComponent( "Cube Mesh", e, std::move( cube ), nullptr ); + auto c = new Ra::Engine::Scene::GeometryDisplayableComponent( + "Cube Mesh", e, std::move( cube ), nullptr ); //! [Create a geometry component with the cube] //! [Register the entity/component association to the geometry system ] diff --git a/examples/SimpleSkinning/main.cpp b/examples/SimpleSkinning/main.cpp index e6da7f6e7e4..ef0da15ac66 100644 --- a/examples/SimpleSkinning/main.cpp +++ b/examples/SimpleSkinning/main.cpp @@ -5,12 +5,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -71,6 +74,13 @@ class SkinningSystem : public Scene::System }; void setupScene( Ra::Engine::RadiumEngine* engine ) { + + DefaultLightManager* lightManager = + static_cast( engine->getSystem( "DefaultLightManager" ) ); + auto light = new Engine::Scene::DirectionalLight( + Ra::Engine::Scene::SystemEntity::getInstance(), "light" ); + lightManager->addLight( light ); + auto animationSystem = new SkinningSystem; engine->registerSystem( "Simple animation system", animationSystem ); diff --git a/src/Core/Animation/LinearBlendSkinning.cpp b/src/Core/Animation/LinearBlendSkinning.cpp index ca53ab54e07..47b106bdba3 100644 --- a/src/Core/Animation/LinearBlendSkinning.cpp +++ b/src/Core/Animation/LinearBlendSkinning.cpp @@ -1,3 +1,4 @@ +#include "Core/Types.hpp" #include #include @@ -33,9 +34,10 @@ void linearBlendSkinning( const SkinningRefData& refData, const Scalar w = it.value(); // prepare the pose w.r.t. the bind matrix and the mesh transform const Transform M = refData.m_meshTransformInverse * pose[j] * bindMatrix[j]; + const Matrix3 N = M.matrix().inverse().transpose().block<3, 3>( 0, 0 ); // apply LBS frameData.m_currentPosition[i] += w * ( M * vertices[i] ); - frameData.m_currentNormal[i] += w * ( M.linear() * normals[i] ); + frameData.m_currentNormal[i] += w * ( N * normals[i] ); frameData.m_currentTangent[i] += w * ( M.linear() * tangents[i] ); frameData.m_currentBitangent[i] += w * ( M.linear() * bitangents[i] ); } diff --git a/src/Core/Animation/SkinningData.hpp b/src/Core/Animation/SkinningData.hpp index d7437548643..dd238dc5d6a 100644 --- a/src/Core/Animation/SkinningData.hpp +++ b/src/Core/Animation/SkinningData.hpp @@ -13,7 +13,7 @@ namespace Animation { /// \brief Skinning data that get set at startup including the "reference state". struct SkinningRefData { /// The mesh in reference position. - Geometry::TriangleMesh m_referenceMesh; + Geometry::AttribArrayGeometry m_referenceMesh; /// The inverse of the mesh's transform. Transform m_meshTransformInverse; diff --git a/src/Core/Geometry/AbstractGeometry.hpp b/src/Core/Geometry/AbstractGeometry.hpp index 6191e7a7f4d..df324a71249 100644 --- a/src/Core/Geometry/AbstractGeometry.hpp +++ b/src/Core/Geometry/AbstractGeometry.hpp @@ -13,8 +13,9 @@ namespace Geometry { /// \warning: Part of the current proposal for a modular implementation of /// displayable objects. /// -/// \note We use a struct because all members are public anyway -struct RA_CORE_API AbstractGeometry { +class RA_CORE_API AbstractGeometry +{ + public: /* * Note: Explicitly defaulted virtual destructor, copy/move constructors, * copy/move assignment operators diff --git a/src/Core/Geometry/IndexedGeometry.cpp b/src/Core/Geometry/IndexedGeometry.cpp index eaf65538284..2726ebe15f1 100644 --- a/src/Core/Geometry/IndexedGeometry.cpp +++ b/src/Core/Geometry/IndexedGeometry.cpp @@ -1,4 +1,5 @@ #include + #include namespace Ra { @@ -215,7 +216,7 @@ MultiIndexedGeometry::addLayer( std::unique_ptr&& layer, const bool withLock, const std::string& layerName ) { LayerKeyType key { layer->semantics(), layerName }; - std::pair elt { key, std::make_pair( false, std::move( layer ) ) }; + auto elt = std::make_pair( key, std::make_pair( false, std::move( layer ) ) ); auto [pos, inserted] = m_indices.insert( std::move( elt ) ); notify(); @@ -223,8 +224,8 @@ MultiIndexedGeometry::addLayer( std::unique_ptr&& layer, CORE_ASSERT( !pos->second.first, "try to get already locked layer" ); pos->second.first = true; } - /// If not inserted, the pointer is deleted. So the caller must ensure this possible deletion - /// is safe before calling this method. + /// If not inserted, the pointer is deleted. So the caller must ensure this possible + /// deletion is safe before calling this method. return { inserted, *( pos->second.second ) }; } @@ -245,7 +246,7 @@ void MultiIndexedGeometry::deepClear() { ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// -std::size_t MultiIndexedGeometry::KeyHash::operator()( const LayerKeyType& k ) const { +std::size_t MultiIndexedGeometry::LayerKeyHash::operator()( const LayerKeyType& k ) const { // Mix semantic collection into a single identifier string std::ostringstream stream; std::copy( k.first.begin(), k.first.end(), std::ostream_iterator( stream, "" ) ); diff --git a/src/Core/Geometry/IndexedGeometry.hpp b/src/Core/Geometry/IndexedGeometry.hpp index 52ab27bae42..5c9bc50499f 100644 --- a/src/Core/Geometry/IndexedGeometry.hpp +++ b/src/Core/Geometry/IndexedGeometry.hpp @@ -13,6 +13,44 @@ namespace Ra { namespace Core { namespace Geometry { +template +VectorArray triangulate( const VectorArray& in ) { + VectorArray out; + + out.reserve( in.size() ); + for ( const auto& face : in ) { + if ( face.size() == 3 ) { out.push_back( face ); } + else { + /// simple sew triangulation + int minus { int( face.size() ) - 1 }; + int plus { 0 }; + while ( plus + 1 < minus ) { + if ( ( plus - minus ) % 2 ) { + out.emplace_back( face[plus], face[plus + 1], face[minus] ); + ++plus; + } + else { + out.emplace_back( face[minus], face[plus], face[minus - 1] ); + --minus; + } + } + } + } + return out; +} + +template <> +inline VectorArray triangulate( const VectorArray& in ) { + VectorArray out; + out.reserve( 2 * in.size() ); + // assume quads are convex + for ( const auto& face : in ) { + out.emplace_back( face[0], face[1], face[2] ); + out.emplace_back( face[0], face[2], face[3] ); + } + return out; +} + /// \brief Base class for index collections stored in MultiIndexedGeometry class RA_CORE_API GeometryIndexLayerBase : public Utils::ObservableVoid, public Utils::ObjectWithSemantic, @@ -63,7 +101,7 @@ struct GeometryIndexLayer : public GeometryIndexLayerBase { inline size_t getSize() const override final; - inline std::unique_ptr clone() override final; + inline std::unique_ptr clone() override; inline size_t getNumberOfComponents() const override final; @@ -383,20 +421,31 @@ class RA_CORE_API MultiIndexedGeometry : public AttribArrayGeometry, public Util /// \brief Clear attributes stored as pointers void deepClear(); - using EntryType = std::pair>; + /// bool -> locked, ptr -> actual data + using LayerEntryType = std::pair>; - struct RA_CORE_API KeyHash { + public: + /// Hash function for layer keys + struct RA_CORE_API LayerKeyHash { std::size_t operator()( const LayerKeyType& k ) const; }; + private: /// Collection of pairs /// \note There is no natural ordering for these elements, thus /// we need an unordered_map. In contrast to map, transparent hashing /// require c++20, so we need to implement them explicitely here /// https://en.cppreference.com/w/cpp/container/unordered_map/find - std::unordered_map m_indices; + std::unordered_map m_indices; }; +#define INDEX_LAYER_CLONE_IMPLEMENTATION( TYPE ) \ + inline std::unique_ptr clone() override { \ + auto copy = std::make_unique( *this ); \ + copy->collection() = collection(); \ + return copy; \ + } + /// \name Predefined index layers /// The use of these layers helps in generic management of geometries /// \{ @@ -414,6 +463,7 @@ struct RA_CORE_API PointCloudIndexLayer : public GeometryIndexLayer { void linearIndices( const AttribArrayGeometry& attr ); static constexpr const char* staticSemanticName = "PointCloud"; + INDEX_LAYER_CLONE_IMPLEMENTATION( PointCloudIndexLayer ) protected: template @@ -425,6 +475,7 @@ struct RA_CORE_API PointCloudIndexLayer : public GeometryIndexLayer { struct RA_CORE_API TriangleIndexLayer : public GeometryIndexLayer { inline TriangleIndexLayer(); static constexpr const char* staticSemanticName = "TriangleMesh"; + INDEX_LAYER_CLONE_IMPLEMENTATION( TriangleIndexLayer ) protected: template @@ -436,6 +487,7 @@ struct RA_CORE_API TriangleIndexLayer : public GeometryIndexLayer { struct RA_CORE_API QuadIndexLayer : public GeometryIndexLayer { inline QuadIndexLayer(); static constexpr const char* staticSemanticName = "QuadMesh"; + INDEX_LAYER_CLONE_IMPLEMENTATION( QuadIndexLayer ) protected: template @@ -448,6 +500,7 @@ struct RA_CORE_API QuadIndexLayer : public GeometryIndexLayer { struct RA_CORE_API PolyIndexLayer : public GeometryIndexLayer { inline PolyIndexLayer(); static constexpr const char* staticSemanticName = "PolyMesh"; + INDEX_LAYER_CLONE_IMPLEMENTATION( PolyIndexLayer ) protected: template @@ -459,6 +512,7 @@ struct RA_CORE_API PolyIndexLayer : public GeometryIndexLayer { struct RA_CORE_API LineIndexLayer : public GeometryIndexLayer { inline LineIndexLayer(); static constexpr const char* staticSemanticName = "LineMesh"; + INDEX_LAYER_CLONE_IMPLEMENTATION( LineIndexLayer ) protected: template @@ -467,6 +521,8 @@ struct RA_CORE_API LineIndexLayer : public GeometryIndexLayer { /// \} +#undef INDEX_LAYER_CLONE_IMPLEMENTATION + /// Temporary class providing the old API for TriangleMesh, LineMesh and PolyMesh /// This class will be marked as deprecated soon. namespace IndexLayerType { @@ -762,6 +818,7 @@ inline void IndexedGeometry::setIndices( IndexContainerType&& indices ) { auto& abstractLayer = getLayerWithLock( m_mainIndexLayerKey ); static_cast::DefaultLayerType&>( abstractLayer ).collection() = std::move( indices ); + indicesUnlock(); notify(); } @@ -769,6 +826,7 @@ template inline void IndexedGeometry::setIndices( const IndexContainerType& indices ) { auto& abstractLayer = getLayerWithLock( m_mainIndexLayerKey ); static_cast::DefaultLayerType&>( abstractLayer ).collection() = indices; + indicesUnlock(); notify(); } diff --git a/src/Core/Geometry/MeshPrimitives.cpp b/src/Core/Geometry/MeshPrimitives.cpp index 04dbe9eb249..956bb18bae9 100644 --- a/src/Core/Geometry/MeshPrimitives.cpp +++ b/src/Core/Geometry/MeshPrimitives.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include // areApproxEqual @@ -90,6 +91,139 @@ TriangleMesh makeSharpBox( const Vector3& halfExts, return makeSharpBox( aabb, color, generateTexCoord ); } +MultiIndexedGeometry makeSharpBox2( const Aabb& aabb, + const Utils::optional& color, + bool generateTexCoord ) { + MultiIndexedGeometry result; + result.setVertices( { // Floor Face + aabb.corner( Aabb::BottomLeftFloor ), + aabb.corner( Aabb::TopLeftFloor ), + aabb.corner( Aabb::TopRightFloor ), + aabb.corner( Aabb::BottomRightFloor ), + + // Ceil Face + aabb.corner( Aabb::BottomLeftCeil ), + aabb.corner( Aabb::BottomRightCeil ), + aabb.corner( Aabb::TopRightCeil ), + aabb.corner( Aabb::TopLeftCeil ), + + // Left Face + aabb.corner( Aabb::TopLeftFloor ), + aabb.corner( Aabb::BottomLeftFloor ), + aabb.corner( Aabb::BottomLeftCeil ), + aabb.corner( Aabb::TopLeftCeil ), + + // Right Face + aabb.corner( Aabb::BottomRightFloor ), + aabb.corner( Aabb::TopRightFloor ), + aabb.corner( Aabb::TopRightCeil ), + aabb.corner( Aabb::BottomRightCeil ), + + // Bottom Face + aabb.corner( Aabb::BottomLeftFloor ), + aabb.corner( Aabb::BottomRightFloor ), + aabb.corner( Aabb::BottomRightCeil ), + aabb.corner( Aabb::BottomLeftCeil ), + + // Top face + aabb.corner( Aabb::TopLeftFloor ), + aabb.corner( Aabb::TopLeftCeil ), + aabb.corner( Aabb::TopRightCeil ), + aabb.corner( Aabb::TopRightFloor ) } ); + + result.setNormals( { // Floor face + Vector3( 0, 0, -1 ), + Vector3( 0, 0, -1 ), + Vector3( 0, 0, -1 ), + Vector3( 0, 0, -1 ), + // Ceil Face + Vector3( 0, 0, +1 ), + Vector3( 0, 0, +1 ), + Vector3( 0, 0, +1 ), + Vector3( 0, 0, +1 ), + // Left Face + Vector3( -1, 0, 0 ), + Vector3( -1, 0, 0 ), + Vector3( -1, 0, 0 ), + Vector3( -1, 0, 0 ), + // Right Face + Vector3( +1, 0, 0 ), + Vector3( +1, 0, 0 ), + Vector3( +1, 0, 0 ), + Vector3( +1, 0, 0 ), + // Bottom Face + Vector3( 0, -1, 0 ), + Vector3( 0, -1, 0 ), + Vector3( 0, -1, 0 ), + Vector3( 0, -1, 0 ), + // Top Face + Vector3( 0, +1, 0 ), + Vector3( 0, +1, 0 ), + Vector3( 0, +1, 0 ), + Vector3( 0, +1, 0 ) } ); + + if ( generateTexCoord ) { + Vector3Array texCoords { + // Floor face + Vector3( 0, 0, 0 ), + Vector3( 1, 0, 0 ), + Vector3( 1, 1, 0 ), + Vector3( 0, 1, 0 ), + // Ceil Face + Vector3( 0, 0, 0 ), + Vector3( 1, 0, 0 ), + Vector3( 1, 1, 0 ), + Vector3( 0, 1, 0 ), + // Left Face + Vector3( 0, 0, 0 ), + Vector3( 1, 0, 0 ), + Vector3( 1, 1, 0 ), + Vector3( 0, 1, 0 ), + // Right Face + Vector3( 0, 0, 0 ), + Vector3( 1, 0, 0 ), + Vector3( 1, 1, 0 ), + Vector3( 0, 1, 0 ), + // Bottom Face + Vector3( 0, 0, 0 ), + Vector3( 1, 0, 0 ), + Vector3( 1, 1, 0 ), + Vector3( 0, 1, 0 ), + // Top Face + Vector3( 0, 0, 0 ), + Vector3( 1, 0, 0 ), + Vector3( 1, 1, 0 ), + Vector3( 0, 1, 0 ), + }; + + result.addAttrib( "in_texcoord", std::move( texCoords ) ); + } + + auto layer = std::make_unique(); + + layer->collection() = { + Vector4ui( 0, 1, 2, 3 ), // Floor + Vector4ui( 4, 5, 6, 7 ), // Ceil + Vector4ui( 8, 9, 10, 11 ), // Left + Vector4ui( 12, 13, 14, 15 ), // Right + Vector4ui( 16, 17, 18, 19 ), // Bottom + Vector4ui( 20, 21, 22, 23 ) // Top + }; + auto semantic = layer->semantics(); + + result.addLayer( std::move( layer ) ); + if ( bool( color ) ) result.colorize( *color ); + result.checkConsistency(); + return MultiIndexedGeometry { std::move( result ) }; +} + +MultiIndexedGeometry makeSharpBox2( const Vector3& halfExts, + const Utils::optional& color, + bool generateTexCoord ) { + Aabb aabb( -halfExts, halfExts ); + return makeSharpBox2( aabb, color, generateTexCoord ); +} + TriangleMesh makeSharpBox( const Aabb& aabb, const Utils::optional& color, bool generateTexCoord ) { diff --git a/src/Core/Geometry/MeshPrimitives.hpp b/src/Core/Geometry/MeshPrimitives.hpp index c585f9aee57..9d598bd211a 100644 --- a/src/Core/Geometry/MeshPrimitives.hpp +++ b/src/Core/Geometry/MeshPrimitives.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -67,6 +68,18 @@ RA_CORE_API TriangleMesh makeSharpBox( const Aabb& aabb, const Utils::optional& color = {}, bool generateTexCoord = false ); +/// Create an axis-aligned cubic mesh with the given half extents, centered on the origin. +RA_CORE_API MultiIndexedGeometry makeSharpBox2( const Vector3& halfExts = Vector3( .5_ra, + .5_ra, + .5_ra ), + const Utils::optional& color = {}, + bool generateTexCoord = false ); + +/// Create an axis-aligned cubic mesh +RA_CORE_API MultiIndexedGeometry makeSharpBox2( const Aabb& aabb, + const Utils::optional& color = {}, + bool generateTexCoord = false ); + /// Create a parametric spherical mesh of given radius. Template parameters set the resolution. /// \param generateTexCoord: maps parametric (u,v) to texture corrdinates [0,1]^2 template diff --git a/src/Core/Geometry/TriangleMesh.hpp b/src/Core/Geometry/TriangleMesh.hpp index 24b1a4a4e01..c10c9a4b8a1 100644 --- a/src/Core/Geometry/TriangleMesh.hpp +++ b/src/Core/Geometry/TriangleMesh.hpp @@ -13,7 +13,7 @@ namespace Ra { namespace Core { namespace Geometry { -/// \brief This class represents vertex + attributes per vertex. Toplogy is handled in +/// \brief This class represents vertex + attributes per vertex. Topology is handled in /// MultiIndexedGeometry subclass. /// /// Attributes are unique per vertex, so that same position with different diff --git a/src/Core/filelist.cmake b/src/Core/filelist.cmake index 7aabbe1b640..ca60adf4e02 100644 --- a/src/Core/filelist.cmake +++ b/src/Core/filelist.cmake @@ -11,7 +11,7 @@ set(core_sources Animation/HandleWeightOperation.cpp Animation/LinearBlendSkinning.cpp Animation/PoseOperation.cpp - Animation/RotationCenterSkinning.cpp + # Animation/RotationCenterSkinning.cpp Animation/Sequence.cpp Animation/Skeleton.cpp Asset/AnimationData.cpp @@ -56,7 +56,7 @@ set(core_headers Animation/LinearBlendSkinning.hpp Animation/Pose.hpp Animation/PoseOperation.hpp - Animation/RotationCenterSkinning.hpp + # Animation/RotationCenterSkinning.hpp Animation/Sequence.hpp Animation/Skeleton.hpp Animation/SkinningData.hpp diff --git a/src/Engine/Data/Mesh.cpp b/src/Engine/Data/Mesh.cpp index cf2cbde539f..9dcdfdde310 100644 --- a/src/Engine/Data/Mesh.cpp +++ b/src/Engine/Data/Mesh.cpp @@ -1,15 +1,17 @@ #include -#include - +#include #include #include +#include #include #include #include #include +#include + namespace Ra { namespace Engine { namespace Data { @@ -149,6 +151,292 @@ void AttribArrayDisplayable::setDirty( const Ra::Core::Geometry::MeshAttrib& typ m_isDirty = true; } +//////////////// MultiIndexedGeometry /////////////////////////////// + +GeometryDisplayable::GeometryDisplayable( const std::string& name ) : base( name ) {} + +GeometryDisplayable::GeometryDisplayable( const std::string& name, + typename Core::Geometry::MultiIndexedGeometry&& geom ) : + base( name ) { + loadGeometry( std::move( geom ) ); +} + +GeometryDisplayable::~GeometryDisplayable() {} + +void GeometryDisplayable::loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh ) { + m_geomLayers.clear(); + m_geom = std::move( mesh ); + setupCoreMeshObservers(); + + if ( m_geom.containsLayer( Core::Geometry::LineIndexLayer::staticSemanticName ) ) { + auto [key, layer] = + m_geom.getFirstLayerOccurrence( Core::Geometry::LineIndexLayer::staticSemanticName ); + m_activeLayerKey = key; + setRenderMode( AttribArrayDisplayable::RM_LINES ); + + auto ok = addRenderLayer( key, AttribArrayDisplayable::RM_LINES ); + if ( !ok ) { LOG( logERROR ) << "loadGeometry could not add layer"; } + } + else if ( m_geom.containsLayer( Core::Geometry::TriangleIndexLayer::staticSemanticName ) ) { + auto [key, layer] = m_geom.getFirstLayerOccurrence( + Core::Geometry::TriangleIndexLayer::staticSemanticName ); + m_activeLayerKey = key; + addRenderLayer( key, AttribArrayDisplayable::RM_TRIANGLES ); + } + else if ( m_geom.containsLayer( Core::Geometry::QuadIndexLayer::staticSemanticName ) ) { + auto [key, layer] = + m_geom.getFirstLayerOccurrence( Core::Geometry::QuadIndexLayer::staticSemanticName ); + + const auto& quadLayer = dynamic_cast( layer ); + + auto triangleLayer = std::make_unique(); + triangleLayer->collection() = Core::Geometry::triangulate( quadLayer.collection() ); + + LayerKeyType triangleKey = { triangleLayer->semantics(), "triangulation" }; + auto layerAdded = m_geom.addLayer( std::move( triangleLayer ), false, "triangulation" ); + + if ( !layerAdded.first ) { LOG( logERROR ) << "failed to add triangleLayer"; } + else { + m_activeLayerKey = triangleKey; + addRenderLayer( triangleKey, AttribArrayDisplayable::RM_TRIANGLES ); + } + } + else if ( m_geom.containsLayer( Core::Geometry::PolyIndexLayer::staticSemanticName ) ) { + auto [key, layer] = + m_geom.getFirstLayerOccurrence( Core::Geometry::PolyIndexLayer::staticSemanticName ); + + const auto& polyLayer = + dynamic_cast( m_geom.getLayer( key ) ); + + auto triangleLayer = std::make_unique(); + triangleLayer->collection() = Core::Geometry::triangulate( polyLayer.collection() ); + LayerKeyType triangleKey = { triangleLayer->semantics(), "triangulation" }; + auto layerAdded = m_geom.addLayer( std::move( triangleLayer ), false, "triangulation" ); + if ( !layerAdded.first ) { LOG( logERROR ) << "failed to add triangleLayer"; } + else { + m_activeLayerKey = triangleKey; + addRenderLayer( triangleKey, AttribArrayDisplayable::RM_TRIANGLES ); + } + } + else { LOG( logERROR ) << "no valid layer found"; } + m_isDirty = true; +} + +void GeometryDisplayable::setupCoreMeshObservers() { + int idx = 0; + m_dataDirty.resize( m_geom.vertexAttribs().getNumAttribs() ); + m_vbos.resize( m_geom.vertexAttribs().getNumAttribs() ); + // here capture ref to idx to propagate idx incrementation + m_geom.vertexAttribs().for_each_attrib( [&idx, this]( Ra::Core::Utils::AttribBase* b ) { + auto name = b->getName(); + m_handleToBuffer[name] = idx; + m_dataDirty[idx] = true; + + // create a identity translation if name is not already translated. + addToTranslationTable( name ); + + b->attach( AttribObserver( this, idx ) ); + ++idx; + } ); + + // add an observer on attrib manipulation. + m_geom.vertexAttribs().attachMember( this, &GeometryDisplayable::addAttribObserver ); + m_isDirty = true; +} + +void GeometryDisplayable::addToTranslationTable( const std::string& name ) { + m_translationTable.insert( name, name ); +} + +void GeometryDisplayable::addAttribObserver( const std::string& name ) { + // this observer is called each time an attrib is added or removed from m_mesh + auto attrib = m_geom.getAttribBase( name ); + // if attrib not nullptr, then it's an attrib add, so attach an observer to it + + if ( attrib ) { + auto itr = m_handleToBuffer.find( name ); + if ( itr == m_handleToBuffer.end() ) { + m_handleToBuffer[name] = m_dataDirty.size(); + + // if there isn't a matching yet, add identity + if ( !m_translationTable.valueIfExists( name ) ) addToTranslationTable( name ); + + m_dataDirty.push_back( true ); + m_vbos.emplace_back( nullptr ); + } + auto idx = m_handleToBuffer[name]; + attrib->attach( AttribObserver( this, idx ) ); + } + // else it's an attrib remove, do nothing, cleanup will be done in updateGL() + else {} +} + +void GeometryDisplayable::setAttribNameMatching( const std::string& meshAttribName, + const std::string& shaderAttribName ) { + + m_translationTable.replace( meshAttribName, shaderAttribName ); +} + +bool GeometryDisplayable::addRenderLayer( LayerKeyType key, base::MeshRenderMode renderMode ) { + if ( !m_geom.containsLayer( key ) ) return false; + auto it = m_geomLayers.find( key ); + if ( it != m_geomLayers.end() ) return false; + + auto& l = m_geomLayers.insert( { key, LayerEntryType() } ).first->second; + + l.observerId = -1; + l.renderMode = renderMode; + l.indices.dirty = true; + + setDirty( true ); + return true; +} + +bool GeometryDisplayable::removeRenderLayer( LayerKeyType key ) { + auto it = m_geomLayers.find( key ); + if ( it == m_geomLayers.end() ) return false; + + // the layer might have already been deleted + if ( m_geom.containsLayer( key ) ) { + auto& geomLayer = m_geom.getLayerWithLock( key ); + geomLayer.detach( it->second.observerId ); + m_geom.unlockLayer( key ); + } + it->second.vao.reset(); + m_geomLayers.erase( it ); + + return true; +} + +void GeometryDisplayable::updateGL() { + if ( m_isDirty ) { + + // create vao + for ( auto& itr : m_geomLayers ) { + const auto& abstractLayer = m_geom.getLayerWithLock( itr.first ); + auto& l = itr.second; + + if ( !l.vao ) { l.vao = globjects::VertexArray::create(); } + + auto& vbo = l.indices; + if ( !vbo.buffer ) { + vbo.buffer = globjects::Buffer::create(); + vbo.dirty = true; + vbo.numElements = abstractLayer.getSize() * abstractLayer.getNumberOfComponents(); + } + + // upload data to gpu + if ( vbo.dirty ) { + vbo.buffer->setData( static_cast( abstractLayer.getBufferSize() ), + abstractLayer.dataPtr(), + GL_STATIC_DRAW ); + vbo.dirty = false; + } + m_geom.unlockLayer( itr.first ); + l.vao->bind(); + l.vao->bindElementBuffer( vbo.buffer.get() ); + l.vao->unbind(); + GL_CHECK_ERROR; + } + + auto func = [this]( Ra::Core::Utils::AttribBase* b ) { + auto idx = m_handleToBuffer[b->getName()]; + + if ( m_dataDirty[idx] ) { + if ( !m_vbos[idx] ) { m_vbos[idx] = globjects::Buffer::create(); } + m_vbos[idx]->setData( b->getBufferSize(), b->dataPtr(), GL_DYNAMIC_DRAW ); + m_dataDirty[idx] = false; + } + }; + + m_geom.vertexAttribs().for_each_attrib( func ); + + // cleanup removed attrib + for ( auto buffer : m_handleToBuffer ) { + // do not remove name from handleToBuffer to keep index ... + // we could also update handleToBuffer, m_vbos, m_dataDirty + if ( !m_geom.hasAttrib( buffer.first ) && m_vbos[buffer.second] ) { + m_vbos[buffer.second].reset( nullptr ); + m_dataDirty[buffer.second] = false; + } + } + + GL_CHECK_ERROR; + m_isDirty = false; + } +} + +void GeometryDisplayable::render( const ShaderProgram* prog ) { + render( prog, m_activeLayerKey ); +} + +void GeometryDisplayable::render( const ShaderProgram* prog, const LayerKeyType& key ) { + GL_CHECK_ERROR; + if ( m_geomLayers.find( key ) != m_geomLayers.end() ) { + if ( m_geomLayers[key].vao ) { + m_geomLayers[key].vao->bind(); + GL_CHECK_ERROR; + autoVertexAttribPointer( prog, key ); + GL_CHECK_ERROR; + m_geomLayers[key].vao->drawElements( + static_cast( m_geomLayers[key].renderMode ), + GLsizei( m_geomLayers[key].indices.numElements ), + GL_UNSIGNED_INT, + nullptr ); + GL_CHECK_ERROR; + m_geomLayers[key].vao->unbind(); + GL_CHECK_ERROR; + } + else { + LOG( logERROR ) << getName() << "try to draw an invalid layer " << *key.first.begin() + << " [" << key.second << "]\n"; + } + } + else { LOG( logERROR ) << "layer was not added as a render layer"; } +} + +void GeometryDisplayable::autoVertexAttribPointer( const ShaderProgram* prog, + const LayerKeyType& key ) { + + auto glprog = prog->getProgramObject(); + gl::GLint attribCount = glprog->get( GL_ACTIVE_ATTRIBUTES ); + + for ( GLint idx = 0; idx < attribCount; ++idx ) { + const gl::GLsizei bufSize = 256; + gl::GLchar name[bufSize]; + gl::GLsizei length; + gl::GLint size; + gl::GLenum type; + glprog->getActiveAttrib( idx, bufSize, &length, &size, &type, name ); + auto loc = glprog->getAttributeLocation( name ); + + auto attribNameOpt = m_translationTable.keyIfExists( name ); + if ( attribNameOpt ) { + auto attribName = *attribNameOpt; + auto attrib = m_geom.getAttribBase( attribName ); + if ( attrib && attrib->getSize() > 0 ) { + m_geomLayers[key].vao->enable( loc ); + auto binding = m_geomLayers[key].vao->binding( idx ); + + binding->setAttribute( loc ); + CORE_ASSERT( m_vbos[m_handleToBuffer[attribName]].get(), "vbo is nullptr" ); +#ifdef CORE_USE_DOUBLE + binding->setBuffer( m_vbos[m_handleToBuffer[attribName]].get(), + 0, + attrib->getNumberOfComponents() * sizeof( float ) ); +#else + + binding->setBuffer( + m_vbos[m_handleToBuffer[attribName]].get(), 0, attrib->getStride() ); +#endif + binding->setFormat( attrib->getNumberOfComponents(), GL_SCALAR ); + } + else { m_geomLayers[key].vao->disable( loc ); } + } + else { m_geomLayers[key].vao->disable( loc ); } + GL_CHECK_ERROR; + } +} Ra::Core::Utils::optional AttribArrayDisplayable::getVaoHandle() { if ( m_vao ) return m_vao->id(); diff --git a/src/Engine/Data/Mesh.hpp b/src/Engine/Data/Mesh.hpp index 5f97b7ddc70..f790ea51cf7 100644 --- a/src/Engine/Data/Mesh.hpp +++ b/src/Engine/Data/Mesh.hpp @@ -47,6 +47,8 @@ class RA_ENGINE_API Vao * with a specific render mode (e.g. GL_TRIANGLES or GL_LINES). * It maintains the attributes and keeps them in sync with the GPU. * \note Attribute names are used to automatic location binding when using shaders. + * + * \TODO Remove rendermode attribute */ class RA_ENGINE_API AttribArrayDisplayable : public Displayable { @@ -75,9 +77,11 @@ class RA_ENGINE_API AttribArrayDisplayable : public Displayable public: explicit AttribArrayDisplayable( const std::string& name, MeshRenderMode renderMode = RM_TRIANGLES ); + AttribArrayDisplayable( const AttribArrayDisplayable& rhs ) = delete; void operator=( const AttribArrayDisplayable& rhs ) = delete; + // no need to detach listener since TriangleMesh is owned by Mesh. ~AttribArrayDisplayable() {} using Displayable::getName; @@ -91,7 +95,7 @@ class RA_ENGINE_API AttribArrayDisplayable : public Displayable /// Mark attrib data as dirty, forcing an update of the OpenGL buffer. ///@{ - /// Use g_attribName to find the corresponding name and call setDirty(const std::string& name). + /// Use g_attribName to find the matching name and call setDirty(const std::string& name). /// \param type: the data to set to MeshAttrib void setDirty( const Core::Geometry::MeshAttrib& type ); @@ -114,7 +118,7 @@ class RA_ENGINE_API AttribArrayDisplayable : public Displayable virtual Core::Geometry::AttribArrayGeometry& getAttribArrayGeometry() = 0; ///@} - /// \brief Get opengl's vbo handle (uint) corresponding to attrib \b name. + /// \brief Get opengl's vbo handle (uint) matching to attrib \b name. /// /// If vbo is not initialized or name do not correponds to an actual attrib name, the returned /// optional is empty @@ -186,10 +190,11 @@ class RA_ENGINE_API VaoIndices void operator()() { m_displayable->m_indicesDirty = true; } private: - VaoIndices* m_displayable; + VaoIndices* m_displayable { nullptr }; }; protected: + // vbo std::unique_ptr m_indices { nullptr }; bool m_indicesDirty { true }; /// number of elements to draw (i.e number of indices to use) @@ -197,32 +202,6 @@ class RA_ENGINE_API VaoIndices size_t m_numElements { 0 }; }; -/// This class handles an attrib array displayable on gpu only, without core -/// geometry. Use only when you don't need to access the cpu geometry again, or -/// when you need to specify special indices. -template -class IndexedAttribArrayDisplayable : public AttribArrayDisplayable, public VaoIndices -{ - using IndexType = I; - using IndexContainerType = Ra::Core::AlignedStdVector; - - template - inline void addAttrib( const std::string& name, - const typename Ra::Core::Utils::Attrib::Container& data ); - template - inline void addAttrib( const std::string& name, - const typename Ra::Core ::Utils::Attrib::Container&& data ); - inline void updateGL() override; - - inline void render( const ShaderProgram* prog ) override; - - protected: - /// assume m_vao is bound. - inline void autoVertexAttribPointer( const ShaderProgram* prog ); - IndexContainerType m_cpu_indices; - AttribManager m_attribManager; -}; - /// Template class to manage the Displayable aspect of a Core Geomertry, such as TriangleMesh. template class CoreGeometryDisplayable : public AttribArrayDisplayable @@ -280,8 +259,8 @@ class CoreGeometryDisplayable : public AttribArrayDisplayable /// \param meshAttribName: name of the attribute on the CoreGeomtry side /// \param shaderAttribName: name of the input vertex attribute on the /// shader side. - void setAttribNameCorrespondance( const std::string& meshAttribName, - const std::string& shaderAttribName ); + void setAttribNameMatching( const std::string& meshAttribName, + const std::string& shaderAttribName ); protected: virtual void updateGL_specific_impl() {} @@ -350,44 +329,132 @@ class IndexedGeometry : public CoreGeometryDisplayable, public VaoIndices }; /// An engine mesh owning a MultiIndexedCoreGeometry, with multiple indices layer. -/// \todo Work in progress. -template -class MultiIndexedGeometry : public CoreGeometryDisplayable +class RA_ENGINE_API GeometryDisplayable : public AttribArrayDisplayable { public: - using base = CoreGeometryDisplayable; - using CoreGeometryDisplayable::CoreGeometryDisplayable; - explicit MultiIndexedGeometry( - const std::string& name, - typename base::CoreGeometry&& geom, - typename base::MeshRenderMode renderMode = base::MeshRenderMode::RM_TRIANGLES ); + using base = AttribArrayDisplayable; + + using LayerSemanticCollection = + typename Core::Geometry::MultiIndexedGeometry::LayerSemanticCollection; + using LayerSemantic = typename Core::Geometry::MultiIndexedGeometry::LayerSemantic; + using LayerKeyType = typename Core::Geometry::MultiIndexedGeometry::LayerKeyType; + using LayerKeyHash = Core::Geometry::MultiIndexedGeometry::LayerKeyHash; + + explicit GeometryDisplayable( const std::string& name ); + explicit GeometryDisplayable( const std::string& name, + typename Core::Geometry::MultiIndexedGeometry&& geom ); + virtual ~GeometryDisplayable(); void render( const ShaderProgram* prog ) override; + void render( const ShaderProgram* prog, const LayerKeyType& key ); - void loadGeometry( T&& mesh ) override; + ///@{ + /** Returns the underlying CoreGeometry as an Core::Geometry::AbstractGeometry */ + inline const Core::Geometry::AbstractGeometry& getAbstractGeometry() const override { + return m_geom; + } + inline Core::Geometry::AbstractGeometry& getAbstractGeometry() override { return m_geom; } + ///@} + inline const Core::Geometry::AttribArrayGeometry& getAttribArrayGeometry() const override { + return m_geom; + } + inline Core::Geometry::AttribArrayGeometry& getAttribArrayGeometry() override { return m_geom; } + + inline Core::Geometry::MultiIndexedGeometry& getCoreGeometry() { return m_geom; } + inline const Core::Geometry::MultiIndexedGeometry& getCoreGeometry() const { return m_geom; } + + /// Bind meshAttribName to shaderAttribName. + /// meshAttribName is a vertex attrib added to the underlying CoreGeometry + /// shaderAttribName is the name of the input paramter of the shader. + /// By default the same name is used, but this mecanism allows to override + /// this behavior. + /// Only one shaderAttribName can be bound to a meshAttribName and the other + /// way round. + /// \param meshAttribName: name of the attribute on the CoreGeometry side + /// \param shaderAttribName: name of the input vertex attribute on the + /// shader side. + void setAttribNameMatching( const std::string& meshAttribName, + const std::string& shaderAttribName ); + + void loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh ); + inline void loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh, + LayerKeyType key, + base::MeshRenderMode renderMode ) { + loadGeometry( std::move( mesh ) ); + addRenderLayer( key, renderMode ); + } + + /// \param r is a collection of keys and renderMode, e.g. { {key1, RM_TRIANGLES}, {key2, + /// RM_LINES} } + template + inline void loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh, + const RangeOfLayerKeys& r ) { + loadGeometry( std::move( mesh ) ); + for ( const auto& k : r ) + addRenderLayer( k.first, k.second ); + } + bool addRenderLayer( LayerKeyType key, base::MeshRenderMode renderMode ); + bool removeRenderLayer( LayerKeyType key ); + // bool setRenderMode( LayerKeyType key, RenderMode ); + // RenderMode getRenderMode( LayerKeyType key ); + + /// Update (i.e. send to GPU) the buffers marked as dirty + void updateGL() override; + + inline size_t getNumVertices() const override { return m_geom.vertices().size(); } protected: - void updateGL_specific_impl() override; + void setupCoreMeshObservers(); - using LayerSemanticCollection = Core::Utils::ObjectWithSemantic::SemanticNameCollection; - using LayerSemantic = Core::Utils::ObjectWithSemantic::SemanticName; - using LayerKeyType = std::pair; - - using EntryType = std::pair; - struct RA_CORE_API KeyHash { - std::size_t operator()( const LayerKeyType& k ) const { - // Mix semantic collection into a single identifier string - std::ostringstream stream; - std::copy( - k.first.begin(), k.first.end(), std::ostream_iterator( stream, "" ) ); - std::string result = stream.str(); - std::sort( result.begin(), result.end() ); - - // Combine with layer name hash - return std::hash {}( result ) ^ - ( std::hash {}( k.second ) << 1 ); - } + /// assume m_vao is bound. + void autoVertexAttribPointer( const ShaderProgram* prog, const LayerKeyType& key ); + + /// m_mesh Observer method, called whenever an attrib is added or removed from + /// m_mesh. + /// it adds an observer to the new attrib. + void addAttribObserver( const std::string& name ); + + void addToTranslationTable( const std::string& name ); + + private: + Core::Geometry::MultiIndexedGeometry m_geom; + + // for vertex attribs, with dirty + struct VBOEntryType { + bool dirty { false }; + std::unique_ptr buffer { nullptr }; + }; + + // for indices, with dirty and num elements + struct IndicesVBO { + bool dirty { false }; + std::unique_ptr buffer { nullptr }; + size_t numElements { 0 }; + }; + + /// LayerKey with its corresponding indices. + struct LayerEntryType { + int observerId { -1 }; + std::unique_ptr vao { nullptr }; + IndicesVBO indices; + base::MeshRenderMode renderMode { RM_TRIANGLES }; + + inline LayerEntryType() = default; }; - std::unordered_map m_indices; + + /// The collection of indices layer we can use for rendering + std::unordered_map m_geomLayers; + + /// "main" triangle layer + LayerKeyType m_activeLayerKey; + + /// \todo use this in place of m_vbos + // using VBOCollection = std::vector; + /// Collection of VBOs for per-vertex attributes + // VBOCollection m_attribVBOs; + + /// Core::Mesh attrib name to Render::Mesh attrib name + /// key: core mesh name, value: shader name + BijectiveAssociation m_translationTable {}; }; /// LineMesh, own a Core::Geometry::LineMesh @@ -438,7 +505,7 @@ class RA_ENGINE_API Mesh : public IndexedGeometry /// Each face of the polyhedron (typically quads) are assume to be planar and convex. /// Simple triangulation is performed on the fly before sending data to the GPU. template -class RA_ENGINE_API GeneralMesh : public IndexedGeometry +class GeneralMesh : public IndexedGeometry { using base = IndexedGeometry; using IndexType = Core::Vector3ui; @@ -451,8 +518,7 @@ class RA_ENGINE_API GeneralMesh : public IndexedGeometry inline void updateGL_specific_impl() override; private: - inline void triangulate(); - Core::AlignedStdVector m_triangleIndices; + Core::VectorArray m_triangleIndices; }; using PolyMesh = GeneralMesh; @@ -522,6 +588,12 @@ template <> struct getType { using Type = Ra::Engine::Data::PolyMesh; }; + +template <> +struct getType { + using Type = Ra::Engine::Data::GeometryDisplayable; +}; + } // namespace RenderMeshType /// create Mesh, PolyMesh Engine::Data::*Mesh * from GeometryData @@ -555,127 +627,6 @@ void VaoIndices::setIndicesDirty() { m_indicesDirty = true; } -///////////////// IndexedAttribArrayDisplayable /////////////////////// - -template -template -void IndexedAttribArrayDisplayable::addAttrib( - const std::string& name, - const typename Ra::Core::Utils::Attrib::Container& data ) { - auto handle = m_attribManager.addAttrib( name ); - m_attribManager.getAttrib( handle ).setData( data ); - m_handleToBuffer[name] = m_dataDirty.size(); - m_dataDirty.push_back( true ); - m_vbos.emplace_back( nullptr ); - m_isDirty = true; -} - -template -template -void IndexedAttribArrayDisplayable::addAttrib( - const std::string& name, - const typename Ra::Core ::Utils::Attrib::Container&& data ) { - auto handle = m_attribManager.addAttrib( name ); - m_attribManager.getAttrib( handle ).setData( std::move( data ) ); - m_handleToBuffer[name] = m_dataDirty.size(); - m_dataDirty.push_back( true ); - m_vbos.emplace_back( nullptr ); - m_isDirty = true; -} - -template -void IndexedAttribArrayDisplayable::updateGL() { - if ( m_isDirty ) { - // Check that our dirty bits are consistent. - ON_ASSERT( bool dirtyTest = false; for ( const auto& d - : m_dataDirty ) { dirtyTest = dirtyTest || d; } ); - CORE_ASSERT( dirtyTest == m_isDirty, "Dirty flags inconsistency" ); - - if ( !m_indices ) { - m_indices = globjects::Buffer::create(); - m_indicesDirty = true; - } - if ( m_indicesDirty ) { - m_indices->setData( - static_cast( m_cpu_indices.size() * sizeof( IndexType ) ), - m_cpu_indices.data(), - GL_STATIC_DRAW ); - m_indicesDirty = false; - } - - m_numElements = m_cpu_indices.size(); - - if ( !m_vao ) { m_vao = globjects::VertexArray::create(); } - m_vao->bind(); - m_vao->bindElementBuffer( m_indices.get() ); - m_vao->unbind(); - - auto func = [this]( Ra::Core::Utils::AttribBase* b ) { - auto idx = m_handleToBuffer[b->getName()]; - - if ( m_dataDirty[idx] ) { - if ( !m_vbos[idx] ) { m_vbos[idx] = globjects::Buffer::create(); } - m_vbos[idx]->setData( b->getBufferSize(), b->dataPtr(), GL_DYNAMIC_DRAW ); - m_dataDirty[idx] = false; - } - }; - m_attribManager.for_each_attrib( func ); - GL_CHECK_ERROR; - m_isDirty = false; - } -} - -template -void IndexedAttribArrayDisplayable::autoVertexAttribPointer( const ShaderProgram* prog ) { - - auto glprog = prog->getProgramObject(); - gl::GLint attribCount = glprog->get( GL_ACTIVE_ATTRIBUTES ); - - for ( GLint idx = 0; idx < attribCount; ++idx ) { - const gl::GLsizei bufSize = 256; - gl::GLchar name[bufSize]; - gl::GLsizei length; - gl::GLint size; - gl::GLenum type; - glprog->getActiveAttrib( idx, bufSize, &length, &size, &type, name ); - auto loc = glprog->getAttributeLocation( name ); - - auto attribName = name; // m_translationTableShaderToMesh[name]; - auto attrib = m_attribManager.getAttribBase( attribName ); - - if ( attrib && attrib->getSize() > 0 ) { - m_vao->enable( loc ); - auto binding = m_vao->binding( idx ); - binding->setAttribute( loc ); - CORE_ASSERT( m_vbos[m_handleToBuffer[attribName]].get(), "vbo is nullptr" ); -#ifdef CORE_USE_DOUBLE - binding->setBuffer( m_vbos[m_handleToBuffer[attribName]].get(), - 0, - attrib->getNumberOfComponents() * sizeof( float ) ); -#else - - binding->setBuffer( - m_vbos[m_handleToBuffer[attribName]].get(), 0, attrib->getStride() ); -#endif - binding->setFormat( attrib->getNumberOfComponents(), GL_SCALAR ); - } - else { m_vao->disable( loc ); } - } -} - -template -void IndexedAttribArrayDisplayable::render( const ShaderProgram* prog ) { - if ( m_vao ) { - autoVertexAttribPointer( prog ); - m_vao->bind(); - m_vao->drawElements( static_cast( m_renderMode ), - GLsizei( m_numElements ), - GL_UNSIGNED_INT, - nullptr ); - m_vao->unbind(); - } -} - //////////////// CoreGeometryDisplayable /////////////////////////////// template @@ -905,7 +856,7 @@ void CoreGeometryDisplayable::updateGL() { } template -void CoreGeometryDisplayable::setAttribNameCorrespondance( +void CoreGeometryDisplayable::setAttribNameMatching( const std::string& meshAttribName, const std::string& shaderAttribName ) { @@ -973,78 +924,6 @@ void IndexedGeometry::render( const ShaderProgram* prog ) { } } -//////////////// MultiIndexedGeometry /////////////////////////////// - -template -MultiIndexedGeometry::MultiIndexedGeometry( const std::string& name, - typename base::CoreGeometry&& geom, - typename base::MeshRenderMode renderMode ) : - base( name, renderMode ) { - loadGeometry( std::move( geom ) ); -} - -template -void MultiIndexedGeometry::loadGeometry( T&& mesh ) { - m_indices.clear(); - - base::loadGeometry_common( std::move( mesh ) ); - CORE_ASSERT( false, "not implemented yet" ); - - /// \todo HERE - // indices - // base::m_mesh.attach( IndicesObserver( this ) ); -} - -template -void MultiIndexedGeometry::updateGL_specific_impl() { - CORE_ASSERT( false, "not implemented yet" ); - // if ( !m_indices ) - // { - // m_indices = globjects::Buffer::create(); - // m_indicesDirty = true; - // } - // if ( m_indicesDirty ) - // { - // /// this one do not work since m_indices is not a std::vector - // // m_indices->setData( m_mesh.m_indices, GL_DYNAMIC_DRAW ); - // m_numElements = - // base::m_mesh.getIndices().size() * - // base::CoreGeometry::IndexType::RowsAtCompileTime; - // - // m_indices->setData( - // static_cast( base::m_mesh.getIndices().size() * - // sizeof( typename base::CoreGeometry::IndexType ) ), - // base::m_mesh.getIndices().data(), - // GL_STATIC_DRAW ); - // m_indicesDirty = false; - // } - // if ( !base::m_vao ) { base::m_vao = globjects::VertexArray::create(); } - // base::m_vao->bind(); - // base::m_vao->bindElementBuffer( m_indices.get() ); - // base::m_vao->unbind(); - /// \todo implement ! -} - -template -void MultiIndexedGeometry::render( const ShaderProgram* ) { - CORE_ASSERT( false, "not implemented yet" ); - // if ( base::m_vao ) - // { - // GL_CHECK_ERROR; - // base::m_vao->bind(); - // base::autoVertexAttribPointer( prog ); - // GL_CHECK_ERROR; - // base::m_vao->drawElements( static_cast( base::m_renderMode ), - // GLsizei( m_numElements ), - // GL_UNSIGNED_INT, - // nullptr ); - // GL_CHECK_ERROR; - // base::m_vao->unbind(); - // GL_CHECK_ERROR; - // } - /// \todo implement ! -} - ///////// PointCloud ////////// PointCloud::PointCloud( const std::string& name, @@ -1081,7 +960,8 @@ void GeneralMesh::updateGL_specific_impl() { this->m_indicesDirty = true; } if ( this->m_indicesDirty ) { - triangulate(); + m_triangleIndices = Core::Geometry::triangulate( this->m_mesh.getIndices() ); + /// this one do not work since m_indices is not a std::vector // m_indices->setData( m_mesh.m_indices, GL_DYNAMIC_DRAW ); this->m_numElements = m_triangleIndices.size() * GeneralMesh::IndexType::RowsAtCompileTime; @@ -1098,41 +978,6 @@ void GeneralMesh::updateGL_specific_impl() { base::m_vao->unbind(); } -template -void GeneralMesh::triangulate() { - m_triangleIndices.clear(); - m_triangleIndices.reserve( this->m_mesh.getIndices().size() ); - for ( const auto& face : this->m_mesh.getIndices() ) { - if ( face.size() == 3 ) { m_triangleIndices.push_back( face ); } - else { - /// simple sew triangulation - int minus { int( face.size() ) - 1 }; - int plus { 0 }; - while ( plus + 1 < minus ) { - if ( ( plus - minus ) % 2 ) { - m_triangleIndices.emplace_back( face[plus], face[plus + 1], face[minus] ); - ++plus; - } - else { - m_triangleIndices.emplace_back( face[minus], face[plus], face[minus - 1] ); - --minus; - } - } - } - } -} - -template <> -inline void GeneralMesh::triangulate() { - m_triangleIndices.clear(); - m_triangleIndices.reserve( 2 * this->m_mesh.getIndices().size() ); - // assume quads are convex - for ( const auto& face : this->m_mesh.getIndices() ) { - m_triangleIndices.emplace_back( face[0], face[1], face[2] ); - m_triangleIndices.emplace_back( face[0], face[2], face[3] ); - } -} - } // namespace Data } // namespace Engine } // namespace Ra diff --git a/src/Engine/Rendering/ForwardRenderer.cpp b/src/Engine/Rendering/ForwardRenderer.cpp index 3facaf7cec2..d578a9f6352 100644 --- a/src/Engine/Rendering/ForwardRenderer.cpp +++ b/src/Engine/Rendering/ForwardRenderer.cpp @@ -1,15 +1,22 @@ +#include +#include +#include #include #include #include #include +#include #include +#include #include #include - +#include #include #include +#include #include +#include #include #include #include @@ -17,6 +24,7 @@ #include #include #include +#include #include #include @@ -27,6 +35,8 @@ #include +#include +#include #include #include @@ -202,6 +212,29 @@ void computeIndices( Core::Geometry::LineMesh::IndexContainerType& indices, indices.erase( std::unique( indices.begin(), indices.end() ), indices.end() ); } +template +void computeIndices2( Core::Geometry::LineIndexLayer::IndexContainerType& indices, + const IndexContainerType& other ) { + + for ( const auto& index : other ) { + auto s = index.size(); + for ( unsigned int i = 0; i < s; ++i ) { + int i1 = index[i]; + int i2 = index[( i + 1 ) % s]; + if ( i1 > i2 ) std::swap( i1, i2 ); + indices.emplace_back( i1, i2 ); + } + } + + std::sort( indices.begin(), + indices.end(), + []( const Core::Geometry::LineMesh::IndexType& a, + const Core::Geometry::LineMesh::IndexType& b ) { + return a[0] < b[0] || ( a[0] == b[0] && a[1] < b[1] ); + } ); + indices.erase( std::unique( indices.begin(), indices.end() ), indices.end() ); +} + // store LineMesh and Core, define the observer functor to update data one core update for wireframe // linemesh template @@ -256,6 +289,30 @@ void setupLineMesh( std::shared_ptr& disp, CoreGeometry& core ) else { disp.reset(); } } +// create a linemesh to draw wireframe given a core mesh +template +void setupLineMesh( Data::GeometryDisplayable& displayable, const std::string& name ) { + auto lineLayer = std::make_unique(); + auto& indices = lineLayer->collection(); + auto& coreGeometry = displayable.getCoreGeometry(); + + if ( coreGeometry.containsLayer( IndexLayer::staticSemanticName ) ) { + auto layerOccurence = + coreGeometry.getFirstLayerOccurrence( IndexLayer::staticSemanticName ); + auto& layer = dynamic_cast( layerOccurence.second ); + computeIndices2( indices, layer.collection() ); + + if ( indices.size() > 0 ) { + + Geometry::MultiIndexedGeometry::LayerKeyType lineKey = { + { Core::Geometry::LineIndexLayer::staticSemanticName }, name }; + auto layerAdded = coreGeometry.addLayer( std::move( lineLayer ), false, name ); + if ( !layerAdded.first ) { LOG( logERROR ) << "failed to add wireframe"; } + else { displayable.addRenderLayer( lineKey, Data::AttribArrayDisplayable::RM_LINES ); } + } + } +} + void ForwardRenderer::renderInternal( const Data::ViewingParameters& renderData ) { m_fbo->bind(); @@ -441,62 +498,81 @@ void ForwardRenderer::renderInternal( const Data::ViewingParameters& renderData glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO ); GL_ASSERT( glDrawBuffers( 1, buffers ) ); // Draw color texture - auto drawWireframe = [this, &renderData]( const auto& ro ) { - std::shared_ptr wro; + auto drawWireframeNew = [this, &renderData]( const auto& ro ) { + auto displayable = ro->getMesh(); + using dispmesh = Ra::Engine::Data::GeometryDisplayable; + auto td = std::dynamic_pointer_cast( displayable ); + if ( td ) { + using namespace Core::Geometry; + using LayerKeyType = Core::Geometry::MultiIndexedGeometry::LayerKeyType; + using LineIndexLayer = Core::Geometry::LineIndexLayer; - WireMap::iterator it = m_wireframes.find( ro.get() ); - if ( it == m_wireframes.end() ) { - std::shared_ptr disp; + auto& coreGeom = td->getCoreGeometry(); - using trimesh = Ra::Engine::Data::IndexedGeometry; - using polymesh = Ra::Engine::Data::IndexedGeometry; - using quadmesh = Ra::Engine::Data::IndexedGeometry; + LayerKeyType lineKey = { { LineIndexLayer::staticSemanticName }, + "wireframe triangles" }; - auto displayable = ro->getMesh(); - auto tm = std::dynamic_pointer_cast( displayable ); - auto tp = std::dynamic_pointer_cast( displayable ); - auto tq = std::dynamic_pointer_cast( displayable ); + LayerKeyType lineKey2 = { { LineIndexLayer::staticSemanticName }, + "wireframe main" }; - auto processLineMesh = []( auto cm, std::shared_ptr& lm ) { - if ( cm->getRenderMode() == - Data::AttribArrayDisplayable::MeshRenderMode::RM_TRIANGLES ) { - setupLineMesh( lm, cm->getCoreGeometry() ); - } - }; - if ( tm ) { processLineMesh( tm, disp ); } - if ( tp ) { processLineMesh( tp, disp ); } - if ( tq ) { processLineMesh( tq, disp ); } + bool hasTriangleLayer = + coreGeom.containsLayer( TriangleIndexLayer::staticSemanticName ); + bool hasQuadLayer = coreGeom.containsLayer( QuadIndexLayer::staticSemanticName ); + bool hasPolyLayer = coreGeom.containsLayer( PolyIndexLayer::staticSemanticName ); - m_wireframes[ro.get()] = disp; - wro = disp; - } - else { wro = it->second; } + if ( hasTriangleLayer && !coreGeom.containsLayer( lineKey ) ) { + setupLineMesh( *td, "wireframe triangles" ); + } + + if ( hasPolyLayer && !coreGeom.containsLayer( lineKey2 ) ) { + setupLineMesh( *td, "wireframe main" ); + } + else if ( hasQuadLayer && !coreGeom.containsLayer( lineKey2 ) ) { + setupLineMesh( *td, "wireframe main" ); + } - const Data::ShaderProgram* shader = - m_shaderProgramManager->getShaderProgram( "Wireframe" ); + const Data::ShaderProgram* shader = + m_shaderProgramManager->getShaderProgram( "Wireframe" ); - if ( shader && wro ) { - shader->bind(); - if ( ro->isVisible() ) { - wro->updateGL(); + if ( shader && ro->isVisible() ) { + GL_CHECK_ERROR; + td->updateGL(); + GL_CHECK_ERROR; + shader->bind(); + GL_CHECK_ERROR; Core::Matrix4 modelMatrix = ro->getTransformAsMatrix(); shader->setUniform( "transform.proj", renderData.projMatrix ); shader->setUniform( "transform.view", renderData.viewMatrix ); shader->setUniform( "transform.model", modelMatrix ); shader->setUniform( "viewport", Core::Vector2 { m_width, m_height } ); - wro->render( shader ); + if ( hasTriangleLayer && ( hasQuadLayer || hasPolyLayer ) ) + shader->setUniform( "pixelWidth", 1.2f ); + else + shader->setUniform( "pixelWidth", 2.8f ); + + GL_CHECK_ERROR; + td->render( shader, lineKey ); + + if ( ( hasQuadLayer || hasPolyLayer ) ) { + shader->setUniform( "pixelWidth", 2.8f ); + GL_CHECK_ERROR; + td->render( shader, lineKey2 ); + } GL_CHECK_ERROR; } } + else { + // skip + } }; for ( const auto& ro : m_fancyRenderObjects ) { - drawWireframe( ro ); + drawWireframeNew( ro ); } for ( const auto& ro : m_transparentRenderObjects ) { - drawWireframe( ro ); + drawWireframeNew( ro ); } } diff --git a/src/Engine/Scene/GeometryComponent.cpp b/src/Engine/Scene/GeometryComponent.cpp index c9056fed4dd..2e7900758b0 100644 --- a/src/Engine/Scene/GeometryComponent.cpp +++ b/src/Engine/Scene/GeometryComponent.cpp @@ -29,6 +29,18 @@ namespace Ra { namespace Engine { namespace Scene { +template <> +SurfaceMeshComponent::SurfaceMeshComponent( + const std::string& name, + Entity* entity, + Ra::Core::Geometry::MultiIndexedGeometry&& mesh, + Core::Asset::MaterialData* mat ) : + GeometryComponent( name, entity ), + m_displayMesh( new Data::GeometryDisplayable( name, std::move( mesh ) ) ) { + setContentName( name ); + finalizeROFromGeometry( mat, Core::Transform::Identity() ); +} + void GeometryComponent::setupIO( const std::string& id ) { const auto& cm = ComponentMessenger::getInstance(); auto roOut = std::bind( &GeometryComponent::roIndexRead, this ); @@ -39,6 +51,20 @@ const Index* GeometryComponent::roIndexRead() const { return &m_roIndex; } +template <> +void SurfaceMeshComponent::generateMesh( + const Ra::Core::Asset::GeometryData* data ) { + m_contentName = data->getName(); + m_displayMesh = Ra::Core::make_shared( m_contentName ); + using CoreMeshType = Ra::Core::Geometry::MultiIndexedGeometry; + CoreMeshType mesh { data->getGeometry() }; + + m_displayMesh->loadGeometry( std::move( mesh ) ); + + finalizeROFromGeometry( data->hasMaterial() ? &( data->getMaterial() ) : nullptr, + data->getFrame() ); +} + /*-----------------------------------------------------------------------------------------------*/ /*--------------------------------- PointCloud Component----------------------------------------*/ /*-----------------------------------------------------------------------------------------------*/ diff --git a/src/Engine/Scene/GeometryComponent.hpp b/src/Engine/Scene/GeometryComponent.hpp index 16fa5104e15..f333bd8bd2a 100644 --- a/src/Engine/Scene/GeometryComponent.hpp +++ b/src/Engine/Scene/GeometryComponent.hpp @@ -3,11 +3,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -107,10 +109,10 @@ class SurfaceMeshComponent : public GeometryComponent std::shared_ptr m_displayMesh { nullptr }; }; -using TriangleMeshComponent = SurfaceMeshComponent; -using LineMeshComponent = SurfaceMeshComponent; -using QuadMeshComponent = SurfaceMeshComponent; -using PolyMeshComponent = SurfaceMeshComponent; +using TriangleMeshComponent = SurfaceMeshComponent; +using GeometryDisplayableComponent = SurfaceMeshComponent; +using QuadMeshComponent = SurfaceMeshComponent; +using PolyMeshComponent = SurfaceMeshComponent; /// \warning, WIP /// \todo doc. @@ -217,6 +219,13 @@ SurfaceMeshComponent::SurfaceMeshComponent( generateMesh( data ); } +template <> +RA_ENGINE_API SurfaceMeshComponent::SurfaceMeshComponent( + const std::string& name, + Entity* entity, + Ra::Core::Geometry::MultiIndexedGeometry&& mesh, + Core::Asset::MaterialData* mat ); + template SurfaceMeshComponent::SurfaceMeshComponent( const std::string& name, Entity* entity, @@ -248,6 +257,10 @@ void SurfaceMeshComponent::generateMesh( const Ra::Core::Asset::Ge data->getFrame() ); } +template <> +RA_ENGINE_API void SurfaceMeshComponent::generateMesh( + const Ra::Core::Asset::GeometryData* data ); + template void SurfaceMeshComponent::finalizeROFromGeometry( const Core::Asset::MaterialData* data, @@ -311,6 +324,12 @@ void SurfaceMeshComponent::setupIO( const std::string& id ) { cm->registerOutput( getEntity(), this, id, cbOut ); cm->registerReadWrite( getEntity(), this, id, cbRw ); + if ( std::is_convertible() && + !std::is_same() ) { + + cm->registerOutput( getEntity(), this, id, cbOut ); + cm->registerReadWrite( getEntity(), this, id, cbRw ); + } base::setupIO( id ); } diff --git a/src/Engine/Scene/GeometrySystem.cpp b/src/Engine/Scene/GeometrySystem.cpp index 78517d084d6..0d757e39406 100644 --- a/src/Engine/Scene/GeometrySystem.cpp +++ b/src/Engine/Scene/GeometrySystem.cpp @@ -30,16 +30,10 @@ void GeometrySystem::handleAssetLoading( Entity* entity, comp = new PointCloudComponent( componentName, entity, data ); break; case Ra::Core::Asset::GeometryData::LINE_MESH: - // comp = new LineMeshComponent( componentName, entity, data ); - // break; - case Ra::Core::Asset::GeometryData::TRI_MESH: - comp = new TriangleMeshComponent( componentName, entity, data ); - break; case Ra::Core::Asset::GeometryData::QUAD_MESH: - comp = new QuadMeshComponent( componentName, entity, data ); - break; + case Ra::Core::Asset::GeometryData::TRI_MESH: case Ra::Core::Asset::GeometryData::POLY_MESH: - comp = new PolyMeshComponent( componentName, entity, data ); + comp = new GeometryDisplayableComponent( componentName, entity, data ); break; case Ra::Core::Asset::GeometryData::TETRA_MESH: case Ra::Core::Asset::GeometryData::HEX_MESH: diff --git a/src/Engine/Scene/SkinningComponent.cpp b/src/Engine/Scene/SkinningComponent.cpp index 4646cc3eee4..dcd256405d8 100644 --- a/src/Engine/Scene/SkinningComponent.cpp +++ b/src/Engine/Scene/SkinningComponent.cpp @@ -1,11 +1,11 @@ -#include +#include "Core/Geometry/TriangleMesh.hpp" +#include #include #include #include #include -#include #include #include #include @@ -22,9 +22,6 @@ using namespace Ra::Core; using Geometry::AttribArrayGeometry; -using Geometry::PolyMesh; -using Geometry::QuadMesh; -using Geometry::TriangleMesh; using namespace Animation; using SpaceType = HandleArray::SpaceType; @@ -40,73 +37,21 @@ static const std::string tangentName = static const std::string bitangentName = Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_BITANGENT ); -TriangleMesh triangulate( const PolyMesh& polyMesh ) { - TriangleMesh res; - res.setVertices( polyMesh.vertices() ); - res.setNormals( polyMesh.normals() ); - res.copyAllAttributes( polyMesh ); - VectorArray indices; - // using the same triangulation as in Ra::Engine::GeneralMesh::triangulate - for ( const auto& face : polyMesh.getIndices() ) { - if ( face.size() == 3 ) { indices.push_back( face ); } - else { - int minus { int( face.size() ) - 1 }; - int plus { 0 }; - while ( plus + 1 < minus ) { - if ( ( plus - minus ) % 2 ) { - indices.emplace_back( face[plus], face[plus + 1], face[minus] ); - ++plus; - } - else { - indices.emplace_back( face[minus], face[plus], face[minus - 1] ); - --minus; - } - } - } - } - res.setIndices( std::move( indices ) ); - return res; -} - -TriangleMesh triangulate( const QuadMesh& quadMesh ) { - TriangleMesh res; - res.setVertices( quadMesh.vertices() ); - res.setNormals( quadMesh.normals() ); - res.copyAllAttributes( quadMesh ); - VectorArray indices; - // using the same triangulation as in Ra::Engine::GeneralMesh::triangulate - for ( const auto& face : quadMesh.getIndices() ) { - indices.emplace_back( face[0], face[1], face[2] ); - indices.emplace_back( face[0], face[2], face[3] ); - } - res.setIndices( std::move( indices ) ); - return res; -} - void SkinningComponent::initialize() { auto compMsg = ComponentMessenger::getInstance(); // get the current animation data. bool hasSkel = compMsg->canGet( getEntity(), m_skelName ); bool hasRefPose = compMsg->canGet( getEntity(), m_skelName ); - bool hasTriMesh = compMsg->canGet( getEntity(), m_meshName ); - m_meshIsPoly = compMsg->canGet( getEntity(), m_meshName ); - m_meshIsQuad = compMsg->canGet( getEntity(), m_meshName ); - - if ( hasSkel && hasRefPose && ( hasTriMesh || m_meshIsPoly || m_meshIsQuad ) ) { + bool hasGeom = compMsg->canGet( getEntity(), m_meshName ); + if ( hasSkel && hasRefPose && hasGeom ) { m_renderObjectReader = compMsg->getterCallback( getEntity(), m_meshName ); m_skeletonGetter = compMsg->getterCallback( getEntity(), m_skelName ); - if ( hasTriMesh ) { - m_triMeshWriter = compMsg->rwCallback( getEntity(), m_meshName ); - } - else if ( m_meshIsQuad ) { - m_quadMeshWriter = compMsg->rwCallback( getEntity(), m_meshName ); - } - else { m_polyMeshWriter = compMsg->rwCallback( getEntity(), m_meshName ); } + + m_geomWriter = compMsg->rwCallback( getEntity(), m_meshName ); // copy mesh triangles and find duplicates for normal computation. - if ( hasTriMesh ) { m_refData.m_referenceMesh = *m_triMeshWriter(); } - else if ( m_meshIsQuad ) { m_refData.m_referenceMesh = triangulate( *m_quadMeshWriter() ); } - else { m_refData.m_referenceMesh = triangulate( *m_polyMeshWriter() ); } + m_refData.m_referenceMesh = *m_geomWriter(); + /// TODO : use the tangent computation algorithms from Core as soon as it is available. if ( !m_refData.m_referenceMesh.hasAttrib( tangentName ) && !m_refData.m_referenceMesh.hasAttrib( bitangentName ) ) { @@ -143,8 +88,6 @@ void SkinningComponent::initialize() { m_refData.m_referenceMesh.addAttrib( bitangentName, std::move( bitangents ) ); } - m_topoMesh = Ra::Core::Geometry::TopologicalMesh { m_refData.m_referenceMesh }; - auto ro = getRoMgr()->getRenderObject( *m_renderObjectReader() ); // get other data m_refData.m_meshTransformInverse = ro->getLocalTransform().inverse(); @@ -171,13 +114,11 @@ void SkinningComponent::initialize() { m_baseMaterial = ro->getMaterial(); // prepare UV - auto attrUV = Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_TEXCOORD ); + AttribArrayGeometry* geom; - if ( hasTriMesh ) { geom = const_cast( m_triMeshWriter() ); } - else { - if ( m_meshIsPoly ) { geom = const_cast( m_polyMeshWriter() ); } - else { geom = const_cast( m_quadMeshWriter() ); } - } + geom = m_geomWriter(); + + auto attrUV = Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_TEXCOORD ); if ( geom->hasAttrib( attrUV ) ) { auto handle = geom->getAttribHandle( attrUV ); m_baseUV = geom->getAttrib( handle ).data(); @@ -231,7 +172,8 @@ void SkinningComponent::skin() { break; } case COR: { - centerOfRotationSkinning( m_refData, tangents, bitangents, m_frameData ); + ///\todo centerOfRotationSkinning( m_refData, tangents, bitangents, + /// m_frameData ); break; } case LBS: @@ -240,29 +182,13 @@ void SkinningComponent::skin() { break; } } - - if ( m_normalSkinning == GEOMETRIC ) { - m_topoMesh.updatePositions( m_frameData.m_currentPosition ); - m_topoMesh.updateWedgeNormals(); - m_topoMesh.updateTriangleMeshNormals( m_frameData.m_currentNormal ); -#pragma omp parallel for - for ( int i = 0; i < int( m_frameData.m_currentNormal.size() ); ++i ) { - Core::Math::getOrthogonalVectors( m_frameData.m_currentNormal[i], - m_frameData.m_currentTangent[i], - m_frameData.m_currentBitangent[i] ); - } - } } } void SkinningComponent::endSkinning() { if ( m_frameData.m_doSkinning ) { AttribArrayGeometry* geom; - if ( !m_meshIsPoly ) { - if ( !m_meshIsQuad ) { geom = const_cast( m_triMeshWriter() ); } - else { geom = const_cast( m_quadMeshWriter() ); } - } - else { geom = const_cast( m_polyMeshWriter() ); } + geom = m_geomWriter(); geom->setVertices( m_frameData.m_currentPosition ); geom->setNormals( m_frameData.m_currentNormal ); @@ -346,7 +272,8 @@ void SkinningComponent::setSkinningType( SkinningType type ) { if ( m_isReady ) { // compute the per-vertex center of rotation only if required. // FIXME: takes time, would be nice to store them in a file and reload. - if ( m_skinningType == COR && m_refData.m_CoR.empty() ) { computeCoR( m_refData ); } + ///\todo if ( m_skinningType == COR && m_refData.m_CoR.empty() ) { computeCoR( + /// m_refData ); } m_forceUpdate = true; } } @@ -370,12 +297,7 @@ void SkinningComponent::showWeights( bool on ) { auto attrUV = Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_TEXCOORD ); AttribHandle handle; - AttribArrayGeometry* geom; - if ( !m_meshIsPoly ) { - if ( !m_meshIsQuad ) { geom = const_cast( m_triMeshWriter() ); } - else { geom = const_cast( m_quadMeshWriter() ); } - } - else { geom = const_cast( m_polyMeshWriter() ); } + AttribArrayGeometry* geom = m_geomWriter(); if ( m_showingWeights ) { // update the displayed weights diff --git a/src/Engine/Scene/SkinningComponent.hpp b/src/Engine/Scene/SkinningComponent.hpp index 27b07092559..9f9608c6a45 100644 --- a/src/Engine/Scene/SkinningComponent.hpp +++ b/src/Engine/Scene/SkinningComponent.hpp @@ -197,23 +197,8 @@ class RA_ENGINE_API SkinningComponent : public Component /// Read FMC's RO idx. Getter m_renderObjectReader; - /// Whether the skinned mesh is a TriangleMesh or a PolyMesh. - bool m_meshIsPoly { false }; - - /// Whether the skinned mesh is a TriangleMesh or a PolyMesh. - bool m_meshIsQuad { false }; - - /// Getter/Setter to the skinned mesh, in case it is a TriangleMesh. - ReadWrite m_triMeshWriter; - - /// Getter/Setter to the skinned mesh, in case it is a PolyMesh. - ReadWrite m_polyMeshWriter; - - /// Getter/Setter to the skinned mesh, in case it is a QuadMesh. - ReadWrite m_quadMeshWriter; - - /// The Topological mesh used to geometrically recompute the normals. - Core::Geometry::TopologicalMesh m_topoMesh; + /// Getter/Setter to the skinned geom + ReadWrite m_geomWriter; /// The per-bone skinning weights. /// \note These are stored this way because we cannot build the weight matrix