Skip to content

Commit

Permalink
[engine] Prepare API of GeometryDisplayable
Browse files Browse the repository at this point in the history
  • Loading branch information
nmellado committed Jul 19, 2021
1 parent 4af8f8e commit 5a16cbe
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 114 deletions.
178 changes: 176 additions & 2 deletions src/Engine/Data/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ using namespace Ra::Core::Utils;
// we have no data to send to the gpu.
AttribArrayDisplayable::AttribArrayDisplayable( const std::string& name,
MeshRenderMode renderMode ) :
Displayable( name ), m_renderMode {renderMode} {
Displayable( name ), m_renderMode { renderMode } {
CORE_ASSERT( m_renderMode == RM_POINTS || m_renderMode == RM_LINES ||
m_renderMode == RM_LINE_LOOP || m_renderMode == RM_LINE_STRIP ||
m_renderMode == RM_TRIANGLES || m_renderMode == RM_TRIANGLE_STRIP ||
Expand Down Expand Up @@ -73,7 +73,7 @@ void Mesh::loadGeometry( const Core::Vector3Array& vertices, const std::vector<u
{
// We store all indices in order. This means that for lines we have
// (L00, L01, L10), (L11, L20, L21) etc. We fill the missing by wrapping around indices.
mindices.push_back( {indices[i], indices[( i + 1 ) % nIdx], indices[( i + 2 ) % nIdx]} );
mindices.push_back( { indices[i], indices[( i + 1 ) % nIdx], indices[( i + 2 ) % nIdx] } );
}

mesh.setIndices( std::move( mindices ) );
Expand Down Expand Up @@ -156,6 +156,180 @@ void AttribArrayDisplayable::setDirty( const AttribArrayDisplayable::MeshData& t

m_isDirty = true;
}
//////////////// MultiIndexedGeometry ///////////////////////////////

GeometryDisplayable::GeometryDisplayable( const std::string& name,
typename Core::Geometry::MultiIndexedGeometry&& geom,
typename base::MeshRenderMode renderMode ) :
base( name, renderMode ), m_geom( std::move( geom ) ) {}

GeometryDisplayable::~GeometryDisplayable() {}

void GeometryDisplayable::loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh ) {
m_geomLayers.clear();
m_geom = std::move( mesh );
setupCoreMeshObservers();
}

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 ) {
auto it = m_translationTableMeshToShader.find( name );
if ( it == m_translationTableMeshToShader.end() )
{
m_translationTableMeshToShader[name] = name;
m_translationTableShaderToMesh[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();

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::setAttribNameCorrespondence( const std::string& meshAttribName,
const std::string& shaderAttribName ) {

// clean previously set translation

auto it1 = std::find_if( m_translationTableShaderToMesh.begin(),
m_translationTableShaderToMesh.end(),
[&meshAttribName]( const TranslationTable::value_type& p ) {
return p.second == meshAttribName;
} );

if ( it1 != m_translationTableShaderToMesh.end() ) m_translationTableShaderToMesh.erase( it1 );

auto it2 = std::find_if( m_translationTableMeshToShader.begin(),
m_translationTableMeshToShader.end(),
[&shaderAttribName]( const TranslationTable::value_type& p ) {
return p.second == shaderAttribName;
} );

if ( it2 != m_translationTableMeshToShader.end() ) m_translationTableMeshToShader.erase( it2 );

m_translationTableShaderToMesh[shaderAttribName] = meshAttribName;
m_translationTableMeshToShader[meshAttribName] = shaderAttribName;
}

bool GeometryDisplayable::addRenderLayer( LayerKeyType key ) {
if ( !m_geom.containsLayer( key ) ) return false;
auto it = m_geomLayers.find( key );
if ( it == m_geomLayers.end() ) return false;

VaoIndices* vao = new VaoIndices;
auto& geomLayer = m_geom.getLayerWithLock( key );
int observerId = geomLayer.attach( VaoIndices::IndicesObserver( vao ) );
m_geom.unlockLayer( key );

m_geomLayers[key] = { observerId, vao };

return false;
}
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.first );
m_geom.unlockLayer( key );
}
delete ( it->second.second );
m_geomLayers.erase( it );

return true;
}

void GeometryDisplayable::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<gl::GLsizeiptr>( 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 !
}

void GeometryDisplayable::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<GLenum>( base::m_renderMode ),
// GLsizei( m_numElements ),
// GL_UNSIGNED_INT,
// nullptr );
// GL_CHECK_ERROR;
// base::m_vao->unbind();
// GL_CHECK_ERROR;
// }
/// \todo implement !
}

void PointCloud::render( const ShaderProgram* prog ) {
if ( m_vao )
Expand Down
110 changes: 70 additions & 40 deletions src/Engine/Data/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class RA_ENGINE_API AttribArrayDisplayable : public Displayable
protected:
std::unique_ptr<globjects::VertexArray> m_vao;

MeshRenderMode m_renderMode {MeshRenderMode::RM_TRIANGLES};
MeshRenderMode m_renderMode { MeshRenderMode::RM_TRIANGLES };

// m_vbos and m_dataDirty have the same size and are indexed thru m_handleToBuffer[attribName]
std::vector<std::unique_ptr<globjects::Buffer>> m_vbos;
Expand All @@ -174,7 +174,7 @@ class RA_ENGINE_API AttribArrayDisplayable : public Displayable

/// General dirty bit of the mesh. Must be equivalent of the "or" of the other dirty flags.
/// an empty mesh is not dirty
bool m_isDirty {false};
bool m_isDirty { false };
};

/// Concept class to ensure consistent naming of VaoIndices accross derived classes.
Expand All @@ -189,20 +189,20 @@ class RA_ENGINE_API VaoIndices
{
public:
/// not tested
explicit IndicesObserver( VaoIndices* displayable ) : m_displayable {displayable} {}
explicit IndicesObserver( VaoIndices* displayable ) : m_displayable { displayable } {}
/// not tested
void operator()() { m_displayable->m_indicesDirty = true; }

private:
VaoIndices* m_displayable;
VaoIndices* m_displayable { nullptr };
};

protected:
std::unique_ptr<globjects::Buffer> m_indices {nullptr};
bool m_indicesDirty {true};
std::unique_ptr<globjects::Buffer> m_indices { nullptr };
bool m_indicesDirty { true };
/// number of elements to draw (i.e number of indices to use)
/// automatically set by updateGL(), not meaningfull if m_indicesDirty.
size_t m_numElements {0};
size_t m_numElements { 0 };
};

/// This class handles an attrib array displayable on gpu only, without core
Expand Down Expand Up @@ -358,44 +358,76 @@ class IndexedGeometry : public CoreGeometryDisplayable<T>, public VaoIndices
};

/// An engine mesh owning a MultiIndexedCoreGeometry, with multiple indices layer.
/// \todo Work in progress.
template <typename T>
class MultiIndexedGeometry : public CoreGeometryDisplayable<T>
class GeometryDisplayable : public AttribArrayDisplayable
{
public:
using base = CoreGeometryDisplayable<T>;
using CoreGeometryDisplayable<T>::CoreGeometryDisplayable;
explicit MultiIndexedGeometry(
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,
typename base::CoreGeometry&& geom,
typename Core::Geometry::MultiIndexedGeometry&& geom,
typename base::MeshRenderMode renderMode = base::MeshRenderMode::RM_TRIANGLES );
virtual inline ~GeometryDisplayable();
void render( const ShaderProgram* prog ) override;

void loadGeometry( T&& mesh ) override;
inline Core::Geometry::MultiIndexedGeometry& getGeometry() { return m_geom; }
inline const Core::Geometry::MultiIndexedGeometry& getGeometry() 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 setAttribNameCorrespondence( const std::string& meshAttribName,
const std::string& shaderAttribName );

void loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh );
inline void loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh, LayerKeyType key ) {
loadGeometry( std::move( mesh ) );
addRenderLayer( key );
}
template <typename RangeOfLayerKeys>
inline void loadGeometry( Core::Geometry::MultiIndexedGeometry&& mesh,
const RangeOfLayerKeys& r ) {
loadGeometry( std::move( mesh ) );
for ( const auto& k : r )
addRenderLayer( k );
}
bool addRenderLayer( LayerKeyType key );
bool removeRenderLayer( LayerKeyType key );

protected:
void updateGL_specific_impl() override;
void updateGL_specific_impl();
void setupCoreMeshObservers();

using LayerSemanticCollection = Core::Utils::ObjectWithSemantic::SemanticNameCollection;
using LayerSemantic = Core::Utils::ObjectWithSemantic::SemanticName;
using LayerKeyType = std::pair<LayerSemanticCollection, std::string>;

using EntryType = std::pair<bool, VaoIndices*>;
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<std::string>( stream, "" ) );
std::string result = stream.str();
std::sort( result.begin(), result.end() );

// Combine with layer name hash
return std::hash<std::string> {}( result ) ^
( std::hash<std::string> {}( k.second ) << 1 );
}
};
std::unordered_map<LayerKeyType, EntryType, KeyHash> m_indices;
/// 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 );

// <observerId, vao>
using LayerEntryType = std::pair<int, VaoIndices*>; // std::pair<bool, VaoIndices*>;

using TranslationTable = std::map<std::string, std::string>;
TranslationTable m_translationTableMeshToShader;
TranslationTable m_translationTableShaderToMesh;

private:
Core::Geometry::MultiIndexedGeometry m_geom;
std::unordered_map<LayerKeyType, LayerEntryType, LayerKeyHash> m_geomLayers;
};

/// LineMesh, own a Core::Geometry::LineMesh
Expand Down Expand Up @@ -507,9 +539,7 @@ CoreMeshType createCoreMeshFromGeometryData( const Ra::Core::Asset::GeometryData
}

if ( data->hasColors() )
{
mesh.addAttrib( Data::Mesh::getAttribName( Data::Mesh::VERTEX_COLOR ), data->getColors() );
}
{ mesh.addAttrib( Data::Mesh::getAttribName( Data::Mesh::VERTEX_COLOR ), data->getColors() ); }

// add custom attribs
// only attributs not handled before are handled by data->getAttribManager()
Expand Down Expand Up @@ -553,7 +583,7 @@ createMeshFromGeometryData( const std::string& name, const Ra::Core::Asset::Geom

CoreMeshType mesh = createCoreMeshFromGeometryData<CoreMeshType>( data );

MeshType* ret = new MeshType {name};
MeshType* ret = new MeshType { name };
ret->loadGeometry( std::move( mesh ) );

return ret;
Expand Down
Loading

0 comments on commit 5a16cbe

Please sign in to comment.