summaryrefslogtreecommitdiff
path: root/common/gal/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'common/gal/opengl')
-rw-r--r--common/gal/opengl/cached_container.cpp547
-rw-r--r--common/gal/opengl/gpu_manager.cpp302
-rw-r--r--common/gal/opengl/noncached_container.cpp89
-rw-r--r--common/gal/opengl/opengl_compositor.cpp291
-rw-r--r--common/gal/opengl/opengl_gal.cpp1204
-rw-r--r--common/gal/opengl/shader.cpp272
-rw-r--r--common/gal/opengl/shader.frag75
-rw-r--r--common/gal/opengl/shader.vert95
-rw-r--r--common/gal/opengl/vertex_container.cpp60
-rw-r--r--common/gal/opengl/vertex_item.cpp53
-rw-r--r--common/gal/opengl/vertex_manager.cpp234
11 files changed, 3222 insertions, 0 deletions
diff --git a/common/gal/opengl/cached_container.cpp b/common/gal/opengl/cached_container.cpp
new file mode 100644
index 0000000..8cf3513
--- /dev/null
+++ b/common/gal/opengl/cached_container.cpp
@@ -0,0 +1,547 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file cached_container.cpp
+ * @brief Class to store instances of VERTEX with caching. It allows storing VERTEX objects and
+ * associates them with VERTEX_ITEMs. This leads to a possibility of caching vertices data in the
+ * GPU memory and a fast reuse of that data.
+ */
+
+#include <gal/opengl/cached_container.h>
+#include <gal/opengl/vertex_manager.h>
+#include <gal/opengl/vertex_item.h>
+#include <gal/opengl/shader.h>
+#include <confirm.h>
+#include <wx/log.h>
+#include <list>
+#ifdef __WXDEBUG__
+#include <profile.h>
+#endif /* __WXDEBUG__ */
+
+using namespace KIGFX;
+
+CACHED_CONTAINER::CACHED_CONTAINER( unsigned int aSize ) :
+ VERTEX_CONTAINER( aSize ), m_item( NULL )
+{
+ // In the beginning there is only free space
+ m_freeChunks.insert( CHUNK( aSize, 0 ) );
+
+ // Do not have uninitialized members:
+ m_chunkSize = 0;
+ m_chunkOffset = 0;
+ m_itemSize = 0;
+}
+
+
+void CACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem )
+{
+ wxASSERT( aItem != NULL );
+
+ m_item = aItem;
+ m_itemSize = m_item->GetSize();
+ m_chunkSize = m_itemSize;
+
+ if( m_itemSize == 0 )
+ m_items.insert( m_item ); // The item was not stored before
+ else
+ m_chunkOffset = m_item->GetOffset();
+
+#if CACHED_CONTAINER_TEST > 1
+ wxLogDebug( wxT( "Adding/editing item 0x%08lx (size %d)" ), (long) m_item, m_itemSize );
+#endif
+}
+
+
+void CACHED_CONTAINER::FinishItem()
+{
+ wxASSERT( m_item != NULL );
+ wxASSERT( m_item->GetSize() == m_itemSize );
+
+ // Finishing the previously edited item
+ if( m_itemSize < m_chunkSize )
+ {
+ // There is some not used but reserved memory left, so we should return it to the pool
+ int itemOffset = m_item->GetOffset();
+
+ // Add the not used memory back to the pool
+ m_freeChunks.insert( CHUNK( m_chunkSize - m_itemSize, itemOffset + m_itemSize ) );
+ m_freeSpace += ( m_chunkSize - m_itemSize );
+ // mergeFreeChunks(); // veery slow and buggy
+ }
+
+#if CACHED_CONTAINER_TEST > 1
+ wxLogDebug( wxT( "Finishing item 0x%08lx (size %d)" ), (long) m_item, m_itemSize );
+ test();
+ m_item = NULL; // electric fence
+#endif
+}
+
+
+VERTEX* CACHED_CONTAINER::Allocate( unsigned int aSize )
+{
+ wxASSERT( m_item != NULL );
+
+ if( m_failed )
+ return NULL;
+
+ if( m_itemSize + aSize > m_chunkSize )
+ {
+ // There is not enough space in the currently reserved chunk, so we have to resize it
+
+ // Reserve a bigger memory chunk for the current item and
+ // make it multiple of 3 to store triangles
+ m_chunkSize = ( 2 * m_itemSize ) + aSize + ( 3 - aSize % 3 );
+ // Save the current size before reallocating
+ m_chunkOffset = reallocate( m_chunkSize );
+
+ if( m_chunkOffset > m_currentSize )
+ {
+ m_failed = true;
+ return NULL;
+ }
+ }
+
+ VERTEX* reserved = &m_vertices[m_chunkOffset + m_itemSize];
+ m_itemSize += aSize;
+ // Now the item officially possesses the memory chunk
+ m_item->setSize( m_itemSize );
+
+ // The content has to be updated
+ m_dirty = true;
+
+#if CACHED_CONTAINER_TEST > 1
+ test();
+#endif
+#if CACHED_CONTAINER_TEST > 2
+ showFreeChunks();
+ showReservedChunks();
+#endif
+
+ return reserved;
+}
+
+
+void CACHED_CONTAINER::Delete( VERTEX_ITEM* aItem )
+{
+ wxASSERT( aItem != NULL );
+ wxASSERT( m_items.find( aItem ) != m_items.end() );
+
+ int size = aItem->GetSize();
+ int offset = aItem->GetOffset();
+
+#if CACHED_CONTAINER_TEST > 1
+ wxLogDebug( wxT( "Removing 0x%08lx (size %d offset %d)" ), (long) aItem, size, offset );
+#endif
+
+ // Insert a free memory chunk entry in the place where item was stored
+ if( size > 0 )
+ {
+ m_freeChunks.insert( CHUNK( size, offset ) );
+ m_freeSpace += size;
+ // Indicate that the item is not stored in the container anymore
+ aItem->setSize( 0 );
+ }
+
+ m_items.erase( aItem );
+
+#if CACHED_CONTAINER_TEST > 1
+ test();
+#endif
+
+ // Dynamic memory freeing, there is no point in holding
+ // a large amount of memory when there is no use for it
+ if( m_freeSpace > ( 0.75 * m_currentSize ) && m_currentSize > m_initialSize )
+ {
+ resizeContainer( 0.5 * m_currentSize );
+ }
+}
+
+
+void CACHED_CONTAINER::Clear()
+{
+ // Change size to the default one
+ m_vertices = static_cast<VERTEX*>( realloc( m_vertices,
+ m_initialSize * sizeof( VERTEX ) ) );
+
+ // Reset state variables
+ m_freeSpace = m_initialSize;
+ m_currentSize = m_initialSize;
+ m_failed = false;
+
+ // Set the size of all the stored VERTEX_ITEMs to 0, so it is clear that they are not held
+ // in the container anymore
+ ITEMS::iterator it;
+
+ for( it = m_items.begin(); it != m_items.end(); ++it )
+ {
+ ( *it )->setSize( 0 );
+ }
+
+ m_items.clear();
+
+
+ // Now there is only free space left
+ m_freeChunks.clear();
+ m_freeChunks.insert( CHUNK( m_freeSpace, 0 ) );
+}
+
+
+unsigned int CACHED_CONTAINER::reallocate( unsigned int aSize )
+{
+ wxASSERT( aSize > 0 );
+
+#if CACHED_CONTAINER_TEST > 2
+ wxLogDebug( wxT( "Resize 0x%08lx from %d to %d" ), (long) m_item, m_itemSize, aSize );
+#endif
+
+ // Is there enough space to store vertices?
+ if( m_freeSpace < aSize )
+ {
+ bool result;
+
+ // Would it be enough to double the current space?
+ if( aSize < m_freeSpace + m_currentSize )
+ {
+ // Yes: exponential growing
+ result = resizeContainer( m_currentSize * 2 );
+ }
+ else
+ {
+ // No: grow to the nearest greater power of 2
+ result = resizeContainer( pow( 2, ceil( log2( m_currentSize * 2 + aSize ) ) ) );
+ }
+
+ if( !result )
+ return UINT_MAX;
+ }
+
+ // Look for the free space chunk of at least given size
+ FREE_CHUNK_MAP::iterator newChunk = m_freeChunks.lower_bound( aSize );
+
+ if( newChunk == m_freeChunks.end() )
+ {
+ // In the case when there is enough space to store the vertices,
+ // but the free space is not continous we should defragment the container
+ if( !defragment() )
+ return UINT_MAX;
+
+ // Update the current offset
+ m_chunkOffset = m_item->GetOffset();
+
+ // We can take the first free chunk, as there is only one after defragmentation
+ // and we can be sure that it provides enough space to store the object
+ newChunk = m_freeChunks.begin();
+ }
+
+ // Parameters of the allocated cuhnk
+ unsigned int chunkSize = newChunk->first;
+ unsigned int chunkOffset = newChunk->second;
+
+ wxASSERT( chunkSize >= aSize );
+ wxASSERT( chunkOffset < m_currentSize );
+
+ // Check if the item was previously stored in the container
+ if( m_itemSize > 0 )
+ {
+#if CACHED_CONTAINER_TEST > 3
+ wxLogDebug( wxT( "Moving 0x%08x from 0x%08x to 0x%08x" ),
+ (int) m_item, oldChunkOffset, chunkOffset );
+#endif
+ // The item was reallocated, so we have to copy all the old data to the new place
+ memcpy( &m_vertices[chunkOffset], &m_vertices[m_chunkOffset], m_itemSize * VertexSize );
+
+ // Free the space previously used by the chunk
+ wxASSERT( m_itemSize > 0 );
+ m_freeChunks.insert( CHUNK( m_itemSize, m_chunkOffset ) );
+ m_freeSpace += m_itemSize;
+ }
+
+ // Remove the allocated chunk from the free space pool
+ m_freeChunks.erase( newChunk );
+
+ // If there is some space left, return it to the pool - add an entry for it
+ if( chunkSize > aSize )
+ {
+ m_freeChunks.insert( CHUNK( chunkSize - aSize, chunkOffset + aSize ) );
+ }
+
+ m_freeSpace -= aSize;
+ // mergeFreeChunks(); // veery slow and buggy
+
+ m_item->setOffset( chunkOffset );
+
+ return chunkOffset;
+}
+
+
+bool CACHED_CONTAINER::defragment( VERTEX* aTarget )
+{
+#if CACHED_CONTAINER_TEST > 0
+ wxLogDebug( wxT( "Defragmenting" ) );
+
+ prof_counter totalTime;
+ prof_start( &totalTime );
+#endif
+
+ if( aTarget == NULL )
+ {
+ // No target was specified, so we have to reallocate our own space
+ int size = m_currentSize * sizeof( VERTEX );
+ aTarget = static_cast<VERTEX*>( malloc( size ) );
+
+ if( aTarget == NULL )
+ {
+ DisplayError( NULL, wxString::Format(
+ wxT( "CACHED_CONTAINER::defragment: Run out of memory (malloc %d bytes)" ),
+ size ) );
+ return false;
+ }
+ }
+
+ int newOffset = 0;
+ ITEMS::iterator it, it_end;
+
+ for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
+ {
+ VERTEX_ITEM* item = *it;
+ int itemOffset = item->GetOffset();
+ int itemSize = item->GetSize();
+
+ // Move an item to the new container
+ memcpy( &aTarget[newOffset], &m_vertices[itemOffset], itemSize * VertexSize );
+
+ // Update new offset
+ item->setOffset( newOffset );
+
+ // Move to the next free space
+ newOffset += itemSize;
+ }
+
+ free( m_vertices );
+ m_vertices = aTarget;
+
+ // Now there is only one big chunk of free memory
+ m_freeChunks.clear();
+ wxASSERT( m_freeSpace > 0 );
+ m_freeChunks.insert( CHUNK( m_freeSpace, m_currentSize - m_freeSpace ) );
+
+#if CACHED_CONTAINER_TEST > 0
+ prof_end( &totalTime );
+
+ wxLogDebug( wxT( "Defragmented the container storing %d vertices / %.1f ms" ),
+ m_currentSize - m_freeSpace, totalTime.msecs() );
+#endif
+
+ return true;
+}
+
+
+void CACHED_CONTAINER::mergeFreeChunks()
+{
+ if( m_freeChunks.size() <= 1 ) // There are no chunks that can be merged
+ return;
+
+#if CACHED_CONTAINER_TEST > 0
+ prof_counter totalTime;
+ prof_start( &totalTime );
+#endif
+
+ // Reversed free chunks map - this one stores chunk size with its offset as the key
+ std::list<CHUNK> freeChunks;
+
+ FREE_CHUNK_MAP::const_iterator it, it_end;
+
+ for( it = m_freeChunks.begin(), it_end = m_freeChunks.end(); it != it_end; ++it )
+ {
+ freeChunks.push_back( std::make_pair( it->second, it->first ) );
+ }
+
+ m_freeChunks.clear();
+ freeChunks.sort();
+
+ std::list<CHUNK>::const_iterator itf, itf_end;
+ unsigned int offset = freeChunks.front().first;
+ unsigned int size = freeChunks.front().second;
+ freeChunks.pop_front();
+
+ for( itf = freeChunks.begin(), itf_end = freeChunks.end(); itf != itf_end; ++itf )
+ {
+ if( itf->first == offset + size )
+ {
+ // These chunks can be merged, so just increase the current chunk size and go on
+ size += itf->second;
+ }
+ else
+ {
+ // These chunks cannot be merged
+ // So store the previous one
+ m_freeChunks.insert( std::make_pair( size, offset ) );
+ // and let's check the next chunk
+ offset = itf->first;
+ size = itf->second;
+
+ }
+ }
+
+ // Add the last one
+ m_freeChunks.insert( std::make_pair( size, offset ) );
+
+#if CACHED_CONTAINER_TEST > 0
+ prof_end( &totalTime );
+
+ wxLogDebug( wxT( "Merged free chunks / %.1f ms" ), totalTime.msecs() );
+#endif
+
+ test();
+}
+
+
+bool CACHED_CONTAINER::resizeContainer( unsigned int aNewSize )
+{
+ wxASSERT( aNewSize != m_currentSize );
+
+#if CACHED_CONTAINER_TEST > 0
+ wxLogDebug( wxT( "Resizing container from %d to %d" ), m_currentSize, aNewSize );
+#endif
+
+ VERTEX* newContainer;
+
+ if( aNewSize < m_currentSize )
+ {
+ // Shrinking container
+ // Sanity check, no shrinking if we cannot fit all the data
+ if( reservedSpace() > aNewSize )
+ return false;
+
+ int size = aNewSize * sizeof( VERTEX );
+ newContainer = static_cast<VERTEX*>( malloc( size ) );
+
+ if( newContainer == NULL )
+ {
+ DisplayError( NULL, wxString::Format(
+ wxT( "CACHED_CONTAINER::resizeContainer:\n"
+ "Run out of memory (malloc %d bytes)" ),
+ size ) );
+ return false;
+ }
+
+ // Defragment directly to the new, smaller container
+ defragment( newContainer );
+
+ // We have to correct freeChunks after defragmentation
+ m_freeChunks.clear();
+ wxASSERT( aNewSize - reservedSpace() > 0 );
+ m_freeChunks.insert( CHUNK( aNewSize - reservedSpace(), reservedSpace() ) );
+ }
+ else
+ {
+ // Enlarging container
+ int size = aNewSize * sizeof( VERTEX );
+ newContainer = static_cast<VERTEX*>( realloc( m_vertices, size ) );
+
+ if( newContainer == NULL )
+ {
+ DisplayError( NULL, wxString::Format(
+ wxT( "CACHED_CONTAINER::resizeContainer:\n"
+ "Run out of memory (realloc from %d to %d bytes)" ),
+ m_currentSize * sizeof( VERTEX ), size ) );
+ return false;
+ }
+
+ // Add an entry for the new memory chunk at the end of the container
+ m_freeChunks.insert( CHUNK( aNewSize - m_currentSize, m_currentSize ) );
+ }
+
+ m_vertices = newContainer;
+
+ m_freeSpace += ( aNewSize - m_currentSize );
+ m_currentSize = aNewSize;
+
+ return true;
+}
+
+
+#ifdef CACHED_CONTAINER_TEST
+void CACHED_CONTAINER::showFreeChunks()
+{
+ FREE_CHUNK_MAP::iterator it;
+
+ wxLogDebug( wxT( "Free chunks:" ) );
+
+ for( it = m_freeChunks.begin(); it != m_freeChunks.end(); ++it )
+ {
+ unsigned int offset = getChunkOffset( *it );
+ unsigned int size = getChunkSize( *it );
+ wxASSERT( size > 0 );
+
+ wxLogDebug( wxT( "[0x%08x-0x%08x] (size %d)" ),
+ offset, offset + size - 1, size );
+ }
+}
+
+
+void CACHED_CONTAINER::showReservedChunks()
+{
+ ITEMS::iterator it;
+
+ wxLogDebug( wxT( "Reserved chunks:" ) );
+
+ for( it = m_items.begin(); it != m_items.end(); ++it )
+ {
+ VERTEX_ITEM* item = *it;
+ unsigned int offset = item->GetOffset();
+ unsigned int size = item->GetSize();
+ wxASSERT( size > 0 );
+
+ wxLogDebug( wxT( "[0x%08x-0x%08x] @ 0x%08lx (size %d)" ),
+ offset, offset + size - 1, (long) item, size );
+ }
+}
+
+
+void CACHED_CONTAINER::test()
+{
+ // Free space check
+ unsigned int freeSpace = 0;
+ FREE_CHUNK_MAP::iterator itf;
+
+ for( itf = m_freeChunks.begin(); itf != m_freeChunks.end(); ++itf )
+ freeSpace += getChunkSize( *itf );
+
+ wxASSERT( freeSpace == m_freeSpace );
+
+ // Reserved space check
+ /*unsigned int reservedSpace = 0;
+ ITEMS::iterator itr;
+ for( itr = m_items.begin(); itr != m_items.end(); ++itr )
+ reservedSpace += ( *itr )->GetSize();
+ reservedSpace += m_itemSize; // Add the current chunk size
+
+ wxASSERT( ( freeSpace + reservedSpace ) == m_currentSize );*/
+
+ // Overlapping check TBD
+}
+
+#endif /* CACHED_CONTAINER_TEST */
diff --git a/common/gal/opengl/gpu_manager.cpp b/common/gal/opengl/gpu_manager.cpp
new file mode 100644
index 0000000..be34a56
--- /dev/null
+++ b/common/gal/opengl/gpu_manager.cpp
@@ -0,0 +1,302 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file gpu_manager.cpp
+ * @brief Class to handle uploading vertices and indices to GPU in drawing purposes.
+ */
+
+#include <gal/opengl/gpu_manager.h>
+#include <gal/opengl/cached_container.h>
+#include <gal/opengl/noncached_container.h>
+#include <gal/opengl/shader.h>
+#include <typeinfo>
+#include <wx/msgdlg.h>
+#include <confirm.h>
+#ifdef PROFILE
+#include <profile.h>
+#include <wx/debug.h>
+#include <wx/log.h>
+#endif /* PROFILE */
+
+using namespace KIGFX;
+
+GPU_MANAGER* GPU_MANAGER::MakeManager( VERTEX_CONTAINER* aContainer )
+{
+ if( typeid( *aContainer ) == typeid( CACHED_CONTAINER ) )
+ return new GPU_CACHED_MANAGER( aContainer );
+ else if( typeid( *aContainer ) == typeid( NONCACHED_CONTAINER ) )
+ return new GPU_NONCACHED_MANAGER( aContainer );
+
+ wxASSERT_MSG( false, wxT( "Not handled container type" ) );
+ return NULL;
+}
+
+
+GPU_MANAGER::GPU_MANAGER( VERTEX_CONTAINER* aContainer ) :
+ m_isDrawing( false ), m_container( aContainer ), m_shader( NULL ), m_shaderAttrib( 0 )
+{
+}
+
+
+GPU_MANAGER::~GPU_MANAGER()
+{
+}
+
+
+void GPU_MANAGER::SetShader( SHADER& aShader )
+{
+ m_shader = &aShader;
+
+ m_shaderAttrib = m_shader->GetAttribute( "attrShaderParams" );
+
+ if( m_shaderAttrib == -1 )
+ {
+ DisplayError( NULL, wxT( "Could not get the shader attribute location" ) );
+ }
+}
+
+
+// Cached manager
+GPU_CACHED_MANAGER::GPU_CACHED_MANAGER( VERTEX_CONTAINER* aContainer ) :
+ GPU_MANAGER( aContainer ), m_buffersInitialized( false ), m_indicesPtr( NULL ),
+ m_verticesBuffer( 0 ), m_indicesBuffer( 0 ), m_indicesSize( 0 ), m_indicesCapacity( 0 )
+{
+ // Allocate the biggest possible buffer for indices
+ resizeIndices( aContainer->GetSize() );
+}
+
+
+GPU_CACHED_MANAGER::~GPU_CACHED_MANAGER()
+{
+ if( m_buffersInitialized )
+ {
+ glBindBuffer( GL_ARRAY_BUFFER, 0 );
+ glDeleteBuffers( 1, &m_verticesBuffer );
+ glDeleteBuffers( 1, &m_indicesBuffer );
+ }
+}
+
+
+void GPU_CACHED_MANAGER::Initialize()
+{
+ wxASSERT( !m_buffersInitialized );
+
+ if( !m_buffersInitialized )
+ {
+ glGenBuffers( 1, &m_verticesBuffer );
+ glGenBuffers( 1, &m_indicesBuffer );
+ m_buffersInitialized = true;
+ }
+}
+
+
+void GPU_CACHED_MANAGER::BeginDrawing()
+{
+ wxASSERT( !m_isDrawing );
+
+ if( m_container->IsDirty() )
+ uploadToGpu();
+
+ // Number of vertices to be drawn in the EndDrawing()
+ m_indicesSize = 0;
+ // Set the indices pointer to the beginning of the indices-to-draw buffer
+ m_indicesPtr = m_indices.get();
+
+ m_isDrawing = true;
+}
+
+
+void GPU_CACHED_MANAGER::DrawIndices( unsigned int aOffset, unsigned int aSize )
+{
+ wxASSERT( m_isDrawing );
+
+ // Copy indices of items that should be drawn to GPU memory
+ for( unsigned int i = aOffset; i < aOffset + aSize; *m_indicesPtr++ = i++ );
+
+ m_indicesSize += aSize;
+}
+
+
+void GPU_CACHED_MANAGER::DrawAll()
+{
+ wxASSERT( m_isDrawing );
+
+ m_indicesSize = m_container->GetSize();
+ for( unsigned int i = 0; i < m_indicesSize; *m_indicesPtr++ = i++ );
+}
+
+
+void GPU_CACHED_MANAGER::EndDrawing()
+{
+ wxASSERT( m_isDrawing );
+
+ // Prepare buffers
+ glEnableClientState( GL_VERTEX_ARRAY );
+ glEnableClientState( GL_COLOR_ARRAY );
+
+ // Bind vertices data buffers
+ glBindBuffer( GL_ARRAY_BUFFER, m_verticesBuffer );
+ glVertexPointer( CoordStride, GL_FLOAT, VertexSize, 0 );
+ glColorPointer( ColorStride, GL_UNSIGNED_BYTE, VertexSize, (GLvoid*) ColorOffset );
+
+ if( m_shader != NULL ) // Use shader if applicable
+ {
+ m_shader->Use();
+ glEnableVertexAttribArray( m_shaderAttrib );
+ glVertexAttribPointer( m_shaderAttrib, ShaderStride, GL_FLOAT, GL_FALSE,
+ VertexSize, (GLvoid*) ShaderOffset );
+ }
+
+ glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indicesBuffer );
+ glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_indicesSize * sizeof(int), (GLvoid*) m_indices.get(), GL_DYNAMIC_DRAW );
+
+ glDrawElements( GL_TRIANGLES, m_indicesSize, GL_UNSIGNED_INT, 0 );
+
+ glBindBuffer( GL_ARRAY_BUFFER, 0 );
+ glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
+
+ // Deactivate vertex array
+ glDisableClientState( GL_COLOR_ARRAY );
+ glDisableClientState( GL_VERTEX_ARRAY );
+
+ if( m_shader != NULL )
+ {
+ glDisableVertexAttribArray( m_shaderAttrib );
+ m_shader->Deactivate();
+ }
+
+ m_isDrawing = false;
+}
+
+
+void GPU_CACHED_MANAGER::uploadToGpu()
+{
+#ifdef PROFILE
+ prof_counter totalTime;
+ prof_start( &totalTime );
+#endif /* PROFILE */
+
+ if( !m_buffersInitialized )
+ Initialize();
+
+ int bufferSize = m_container->GetSize();
+ GLfloat* vertices = (GLfloat*) m_container->GetAllVertices();
+
+ // Upload vertices coordinates and shader types to GPU memory
+ glBindBuffer( GL_ARRAY_BUFFER, m_verticesBuffer );
+ glBufferData( GL_ARRAY_BUFFER, bufferSize * VertexSize, vertices, GL_STATIC_DRAW );
+ glBindBuffer( GL_ARRAY_BUFFER, 0 );
+
+ // Allocate the biggest possible buffer for indices
+ resizeIndices( bufferSize );
+
+ if( glGetError() != GL_NO_ERROR )
+ DisplayError( NULL, wxT( "Error during data upload to the GPU memory" ) );
+
+#ifdef PROFILE
+ prof_end( &totalTime );
+
+ wxLogDebug( wxT( "Uploading %d vertices to GPU / %.1f ms" ), bufferSize, totalTime.msecs() );
+#endif /* PROFILE */
+}
+
+
+void GPU_CACHED_MANAGER::resizeIndices( unsigned int aNewSize )
+{
+ if( aNewSize > m_indicesCapacity )
+ {
+ m_indicesCapacity = aNewSize;
+ m_indices.reset( new GLuint[m_indicesCapacity] );
+ }
+}
+
+
+// Noncached manager
+GPU_NONCACHED_MANAGER::GPU_NONCACHED_MANAGER( VERTEX_CONTAINER* aContainer ) :
+ GPU_MANAGER( aContainer )
+{
+}
+
+
+void GPU_NONCACHED_MANAGER::Initialize()
+{
+ // Nothing has to be intialized
+}
+
+
+void GPU_NONCACHED_MANAGER::BeginDrawing()
+{
+ // Nothing has to be prepared
+}
+
+
+void GPU_NONCACHED_MANAGER::DrawIndices( unsigned int aOffset, unsigned int aSize )
+{
+ wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
+}
+
+
+void GPU_NONCACHED_MANAGER::DrawAll()
+{
+ // This is the default use case, nothing has to be done
+ // The real rendering takes place in the EndDrawing() function
+}
+
+
+void GPU_NONCACHED_MANAGER::EndDrawing()
+{
+ VERTEX* vertices = m_container->GetAllVertices();
+ GLfloat* coordinates = (GLfloat*) ( vertices );
+ GLubyte* colors = (GLubyte*) ( vertices ) + ColorOffset;
+
+ // Prepare buffers
+ glEnableClientState( GL_VERTEX_ARRAY );
+ glEnableClientState( GL_COLOR_ARRAY );
+
+ glVertexPointer( CoordStride, GL_FLOAT, VertexSize, coordinates );
+ glColorPointer( ColorStride, GL_UNSIGNED_BYTE, VertexSize, colors );
+
+ if( m_shader != NULL ) // Use shader if applicable
+ {
+ GLfloat* shaders = (GLfloat*) ( vertices ) + ShaderOffset / sizeof(GLfloat);
+
+ m_shader->Use();
+ glEnableVertexAttribArray( m_shaderAttrib );
+ glVertexAttribPointer( m_shaderAttrib, ShaderStride, GL_FLOAT, GL_FALSE,
+ VertexSize, shaders );
+ }
+
+ glDrawArrays( GL_TRIANGLES, 0, m_container->GetSize() );
+
+ // Deactivate vertex array
+ glDisableClientState( GL_COLOR_ARRAY );
+ glDisableClientState( GL_VERTEX_ARRAY );
+
+ if( m_shader != NULL )
+ {
+ glDisableVertexAttribArray( m_shaderAttrib );
+ m_shader->Deactivate();
+ }
+}
diff --git a/common/gal/opengl/noncached_container.cpp b/common/gal/opengl/noncached_container.cpp
new file mode 100644
index 0000000..c902c47
--- /dev/null
+++ b/common/gal/opengl/noncached_container.cpp
@@ -0,0 +1,89 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file noncached_container.cpp
+ * @brief Class to store instances of VERTEX without caching. It allows a fast one-frame drawing
+ * and then clearing the buffer and starting from scratch.
+ */
+
+#include <gal/opengl/noncached_container.h>
+#include <cstdlib>
+
+using namespace KIGFX;
+
+NONCACHED_CONTAINER::NONCACHED_CONTAINER( unsigned int aSize ) :
+ VERTEX_CONTAINER( aSize ), m_freePtr( 0 )
+{
+}
+
+
+NONCACHED_CONTAINER::~NONCACHED_CONTAINER()
+{
+}
+
+
+void NONCACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem )
+{
+ // Nothing has to be done, as the noncached container
+ // does not care about VERTEX_ITEMs ownership
+}
+
+
+VERTEX* NONCACHED_CONTAINER::Allocate( unsigned int aSize )
+{
+ if( m_freeSpace < aSize )
+ {
+ // Double the space
+ VERTEX* newVertices = static_cast<VERTEX*>( realloc( m_vertices,
+ m_currentSize * 2 *
+ sizeof(VERTEX) ) );
+
+ if( newVertices != NULL )
+ {
+ m_vertices = newVertices;
+ m_freeSpace += m_currentSize;
+ m_currentSize *= 2;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ VERTEX* freeVertex = &m_vertices[m_freePtr];
+
+ // Move to the next free chunk
+ m_freePtr += aSize;
+ m_freeSpace -= aSize;
+
+ return freeVertex;
+}
+
+
+void NONCACHED_CONTAINER::Clear()
+{
+ m_freePtr = 0;
+ m_freeSpace = m_currentSize;
+}
diff --git a/common/gal/opengl/opengl_compositor.cpp b/common/gal/opengl/opengl_compositor.cpp
new file mode 100644
index 0000000..9f35d9e
--- /dev/null
+++ b/common/gal/opengl/opengl_compositor.cpp
@@ -0,0 +1,291 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2015 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file opengl_compositor.cpp
+ * @brief Class that handles multitarget rendering (i.e. to different textures/surfaces) and
+ * later compositing into a single image (OpenGL flavour).
+ */
+
+#include <gal/opengl/opengl_compositor.h>
+
+#include <stdexcept>
+#include <cassert>
+
+using namespace KIGFX;
+
+OPENGL_COMPOSITOR::OPENGL_COMPOSITOR() :
+ m_initialized( false ), m_current( 0 ), m_currentFbo( DIRECT_RENDERING )
+{
+ // Avoid not initialized members:
+ m_framebuffer = 0;
+ m_depthBuffer = 0;
+}
+
+
+OPENGL_COMPOSITOR::~OPENGL_COMPOSITOR()
+{
+ if( m_initialized )
+ clean();
+}
+
+
+void OPENGL_COMPOSITOR::Initialize()
+{
+ if( m_initialized )
+ return;
+
+ // We need framebuffer objects for drawing the screen contents
+ // Generate framebuffer and a depth buffer
+ glGenFramebuffersEXT( 1, &m_framebuffer );
+ glBindFramebufferEXT( GL_FRAMEBUFFER, m_framebuffer );
+ m_currentFbo = m_framebuffer;
+
+ // Allocate memory for the depth buffer
+ // Attach the depth buffer to the framebuffer
+ glGenRenderbuffersEXT( 1, &m_depthBuffer );
+ glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_depthBuffer );
+
+ // Use here a size of 24 bits for the depth buffer, 8 bits for the stencil buffer
+ // this is required later for anti-aliasing
+ glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, m_width, m_height );
+ glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER_EXT, m_depthBuffer );
+
+ // Unbind the framebuffer, so by default all the rendering goes directly to the display
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ m_initialized = true;
+}
+
+
+void OPENGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
+{
+ if( m_initialized )
+ clean();
+
+ m_width = aWidth;
+ m_height = aHeight;
+}
+
+
+unsigned int OPENGL_COMPOSITOR::CreateBuffer()
+{
+ assert( m_initialized );
+
+ unsigned int maxBuffers;
+
+ // Get the maximum number of buffers
+ glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &maxBuffers );
+
+ if( usedBuffers() >= maxBuffers )
+ {
+ throw std::runtime_error("Cannot create more framebuffers. OpenGL rendering "
+ "backend requires at least 3 framebuffers. You may try to update/change "
+ "your graphic drivers.");
+ }
+
+ // GL_COLOR_ATTACHMENTn are consecutive integers
+ GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers();
+ GLuint textureTarget;
+
+ // Generate the texture for the pixel storage
+ glGenTextures( 1, &textureTarget );
+ glBindTexture( GL_TEXTURE_2D, textureTarget );
+
+ // Set texture parameters
+ glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, NULL );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+
+ // Bind the texture to the specific attachment point, clear and rebind the screen
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_framebuffer );
+ m_currentFbo = m_framebuffer;
+ glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint,
+ GL_TEXTURE_2D, textureTarget, 0 );
+
+ // Check the status, exit if the framebuffer can't be created
+ GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
+
+ if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
+ {
+ switch( status )
+ {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+ throw std::runtime_error( "Cannot create the framebuffer." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
+ throw std::runtime_error( "The framebuffer attachment points are incomplete." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
+ throw std::runtime_error( "The framebuffer does not have at least one "
+ "image attached to it." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
+ throw std::runtime_error( "The framebuffer read buffer is incomplete." );
+ break;
+
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
+ throw std::runtime_error( "The combination of internal formats of the attached "
+ "images violates an implementation-dependent set of restrictions." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
+ throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for "
+ "all attached renderbuffers" );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
+ throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
+ break;
+
+ default:
+ throw std::runtime_error( "Cannot create the framebuffer." );
+ break;
+ }
+
+ return 0;
+ }
+
+ ClearBuffer();
+
+ // Return to direct rendering (we were asked only to create a buffer, not switch to one)
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ // Store the new buffer
+ OPENGL_BUFFER buffer = { textureTarget, attachmentPoint };
+ m_buffers.push_back( buffer );
+
+ return usedBuffers();
+}
+
+
+void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
+{
+ if( aBufferHandle > usedBuffers() )
+ return;
+
+ // Change the rendering destination to the selected attachment point
+ if( aBufferHandle == DIRECT_RENDERING )
+ {
+ m_currentFbo = DIRECT_RENDERING;
+ }
+ else if( m_currentFbo != m_framebuffer )
+ {
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_framebuffer );
+ m_currentFbo = m_framebuffer;
+ }
+
+ if( m_currentFbo != DIRECT_RENDERING )
+ {
+ m_current = aBufferHandle - 1;
+ glDrawBuffer( m_buffers[m_current].attachmentPoint );
+ }
+}
+
+
+void OPENGL_COMPOSITOR::ClearBuffer()
+{
+ assert( m_initialized );
+
+ glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+}
+
+
+void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
+{
+ assert( m_initialized );
+ assert( aBufferHandle != 0 && aBufferHandle <= usedBuffers() );
+
+ // Switch to the main framebuffer and blit the scene
+ glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ // Depth test has to be disabled to make transparency working
+ glDisable( GL_DEPTH_TEST );
+ glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
+
+ // Enable texturing and bind the main texture
+ glEnable( GL_TEXTURE_2D );
+ glBindTexture( GL_TEXTURE_2D, m_buffers[aBufferHandle - 1].textureTarget );
+
+ // Draw a full screen quad with the texture
+ glMatrixMode( GL_MODELVIEW );
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode( GL_PROJECTION );
+ glPushMatrix();
+ glLoadIdentity();
+
+ glBegin( GL_TRIANGLES );
+ glTexCoord2f( 0.0f, 1.0f );
+ glVertex2f( -1.0f, -1.0f );
+ glTexCoord2f( 1.0f, 1.0f );
+ glVertex2f( 1.0f, -1.0f );
+ glTexCoord2f( 1.0f, 0.0f );
+ glVertex2f( 1.0f, 1.0f );
+
+ glTexCoord2f( 0.0f, 1.0f );
+ glVertex2f( -1.0f, -1.0f );
+ glTexCoord2f( 1.0f, 0.0f );
+ glVertex2f( 1.0f, 1.0f );
+ glTexCoord2f( 0.0f, 0.0f );
+ glVertex2f( -1.0f, 1.0f );
+ glEnd();
+
+ glPopMatrix();
+ glMatrixMode( GL_MODELVIEW );
+ glPopMatrix();
+}
+
+
+void OPENGL_COMPOSITOR::clean()
+{
+ assert( m_initialized );
+
+ glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ OPENGL_BUFFERS::const_iterator it;
+
+ for( it = m_buffers.begin(); it != m_buffers.end(); ++it )
+ {
+ glDeleteTextures( 1, &it->textureTarget );
+ }
+
+ m_buffers.clear();
+
+ glDeleteFramebuffersEXT( 1, &m_framebuffer );
+ glDeleteRenderbuffersEXT( 1, &m_depthBuffer );
+
+ m_initialized = false;
+}
diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp
new file mode 100644
index 0000000..945161f
--- /dev/null
+++ b/common/gal/opengl/opengl_gal.cpp
@@ -0,0 +1,1204 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
+ * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors.
+ * Copyright (C) 2013-2016 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * Graphics Abstraction Layer (GAL) for OpenGL
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <gal/opengl/opengl_gal.h>
+#include <gal/definitions.h>
+
+#include <wx/log.h>
+#include <macros.h>
+#ifdef __WXDEBUG__
+#include <profile.h>
+#endif /* __WXDEBUG__ */
+
+#include <limits>
+#include <boost/bind.hpp>
+
+using namespace KIGFX;
+
+static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
+const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
+wxGLContext* OPENGL_GAL::glContext = NULL;
+
+OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
+ wxEvtHandler* aPaintListener, const wxString& aName ) :
+ wxGLCanvas( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize,
+ wxEXPAND, aName ),
+ mouseListener( aMouseListener ),
+ paintListener( aPaintListener ),
+ cachedManager( true ),
+ nonCachedManager( false ),
+ overlayManager( false )
+{
+ if( glContext == NULL )
+ glContext = new wxGLContext( this );
+
+ // Check if OpenGL requirements are met
+ runTest();
+
+ // Make VBOs use shaders
+ cachedManager.SetShader( shader );
+ nonCachedManager.SetShader( shader );
+ overlayManager.SetShader( shader );
+
+ // Initialize the flags
+ isFramebufferInitialized = false;
+ isGrouping = false;
+ groupCounter = 0;
+
+#ifdef RETINA_OPENGL_PATCH
+ SetViewWantsBestResolution( true );
+#endif
+
+ // Connecting the event handlers
+ Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) );
+
+ // Mouse events are skipped to the parent
+ Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+ Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+#ifdef USE_OSX_MAGNIFY_EVENT
+ Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+#endif
+#if defined _WIN32 || defined _WIN64
+ Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
+#endif
+
+ SetSize( aParent->GetSize() );
+ screenSize = VECTOR2I( aParent->GetSize() );
+
+ // Grid color settings are different in Cairo and OpenGL
+ SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );
+
+ // Tesselator initialization
+ tesselator = gluNewTess();
+ InitTesselatorCallbacks( tesselator );
+
+ if( tesselator == NULL )
+ throw std::runtime_error( "Could not create the tesselator" );
+
+ gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+ currentManager = &nonCachedManager;
+}
+
+
+OPENGL_GAL::~OPENGL_GAL()
+{
+ glFlush();
+
+ gluDeleteTess( tesselator );
+ ClearCache();
+}
+
+
+void OPENGL_GAL::BeginDrawing()
+{
+ if( !IsShownOnScreen() )
+ return;
+
+ SetCurrent( *glContext );
+ clientDC = new wxClientDC( this );
+
+#ifdef RETINA_OPENGL_PATCH
+ const float scaleFactor = GetBackingScaleFactor();
+#else
+ const float scaleFactor = 1.0f;
+#endif
+
+ // Set up the view port
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ glViewport( 0, 0, (GLsizei) screenSize.x * scaleFactor, (GLsizei) screenSize.y * scaleFactor );
+
+ // Create the screen transformation
+ glOrtho( 0, (GLint) screenSize.x, 0, (GLsizei) screenSize.y,
+ -depthRange.x, -depthRange.y );
+
+ if( !isFramebufferInitialized )
+ {
+ // Prepare rendering target buffers
+ compositor.Initialize();
+ mainBuffer = compositor.CreateBuffer();
+ overlayBuffer = compositor.CreateBuffer();
+
+ isFramebufferInitialized = true;
+ }
+
+ // Disable 2D Textures
+ glDisable( GL_TEXTURE_2D );
+
+ // Enable the depth buffer
+ glEnable( GL_DEPTH_TEST );
+ glDepthFunc( GL_LESS );
+
+ // Setup blending, required for transparent objects
+ glEnable( GL_BLEND );
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+ glMatrixMode( GL_MODELVIEW );
+
+ // Set up the world <-> screen transformation
+ ComputeWorldScreenMatrix();
+ GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+ matrixData[0] = worldScreenMatrix.m_data[0][0];
+ matrixData[1] = worldScreenMatrix.m_data[1][0];
+ matrixData[2] = worldScreenMatrix.m_data[2][0];
+ matrixData[4] = worldScreenMatrix.m_data[0][1];
+ matrixData[5] = worldScreenMatrix.m_data[1][1];
+ matrixData[6] = worldScreenMatrix.m_data[2][1];
+ matrixData[12] = worldScreenMatrix.m_data[0][2];
+ matrixData[13] = worldScreenMatrix.m_data[1][2];
+ matrixData[14] = worldScreenMatrix.m_data[2][2];
+ glLoadMatrixd( matrixData );
+
+ // Set defaults
+ SetFillColor( fillColor );
+ SetStrokeColor( strokeColor );
+
+ // Unbind buffers - set compositor for direct drawing
+ compositor.SetBuffer( OPENGL_COMPOSITOR::DIRECT_RENDERING );
+
+ // Remove all previously stored items
+ nonCachedManager.Clear();
+ overlayManager.Clear();
+
+ cachedManager.BeginDrawing();
+ nonCachedManager.BeginDrawing();
+ overlayManager.BeginDrawing();
+}
+
+
+void OPENGL_GAL::EndDrawing()
+{
+ // Cached & non-cached containers are rendered to the same buffer
+ compositor.SetBuffer( mainBuffer );
+ nonCachedManager.EndDrawing();
+ cachedManager.EndDrawing();
+
+ // Overlay container is rendered to a different buffer
+ compositor.SetBuffer( overlayBuffer );
+ overlayManager.EndDrawing();
+
+ // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
+ glColor4d( 1.0, 1.0, 1.0, 1.0 );
+
+ // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
+ compositor.DrawBuffer( mainBuffer );
+ compositor.DrawBuffer( overlayBuffer );
+ blitCursor();
+
+ glFlush();
+ SwapBuffers();
+
+ delete clientDC;
+}
+
+
+void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
+{
+ const VECTOR2D startEndVector = aEndPoint - aStartPoint;
+ double lineAngle = startEndVector.Angle();
+
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+
+ drawLineQuad( aStartPoint, aEndPoint );
+
+ // Line caps
+ if( lineWidth > 1.0 )
+ {
+ drawFilledSemiCircle( aStartPoint, lineWidth / 2, lineAngle + M_PI / 2 );
+ drawFilledSemiCircle( aEndPoint, lineWidth / 2, lineAngle - M_PI / 2 );
+ }
+}
+
+
+void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
+ double aWidth )
+{
+ VECTOR2D startEndVector = aEndPoint - aStartPoint;
+ double lineAngle = startEndVector.Angle();
+
+ if( isFillEnabled )
+ {
+ // Filled tracks
+ currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
+
+ SetLineWidth( aWidth );
+ drawLineQuad( aStartPoint, aEndPoint );
+
+ // Draw line caps
+ drawFilledSemiCircle( aStartPoint, aWidth / 2, lineAngle + M_PI / 2 );
+ drawFilledSemiCircle( aEndPoint, aWidth / 2, lineAngle - M_PI / 2 );
+ }
+ else
+ {
+ // Outlined tracks
+ double lineLength = startEndVector.EuclideanNorm();
+
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+
+ Save();
+
+ currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
+ currentManager->Rotate( lineAngle, 0.0f, 0.0f, 1.0f );
+
+ drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ),
+ VECTOR2D( lineLength, aWidth / 2.0 ) );
+
+ drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ),
+ VECTOR2D( lineLength, -aWidth / 2.0 ) );
+
+ // Draw line caps
+ drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2 );
+ drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2 );
+
+ Restore();
+ }
+}
+
+
+void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
+{
+ if( isFillEnabled )
+ {
+ currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
+
+ /* Draw a triangle that contains the circle, then shade it leaving only the circle.
+ * Parameters given to setShader are indices of the triangle's vertices
+ * (if you want to understand more, check the vertex shader source [shader.vert]).
+ * Shader uses this coordinates to determine if fragments are inside the circle or not.
+ * v2
+ * /\
+ * //\\
+ * v0 /_\/_\ v1
+ */
+ currentManager->Shader( SHADER_FILLED_CIRCLE, 1.0 );
+ currentManager->Vertex( aCenterPoint.x - aRadius * sqrt( 3.0f ), // v0
+ aCenterPoint.y - aRadius, layerDepth );
+
+ currentManager->Shader( SHADER_FILLED_CIRCLE, 2.0 );
+ currentManager->Vertex( aCenterPoint.x + aRadius * sqrt( 3.0f ), // v1
+ aCenterPoint.y - aRadius, layerDepth );
+
+ currentManager->Shader( SHADER_FILLED_CIRCLE, 3.0 );
+ currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, // v2
+ layerDepth );
+ }
+
+ if( isStrokeEnabled )
+ {
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+
+ /* Draw a triangle that contains the circle, then shade it leaving only the circle.
+ * Parameters given to setShader are indices of the triangle's vertices
+ * (if you want to understand more, check the vertex shader source [shader.vert]).
+ * and the line width. Shader uses this coordinates to determine if fragments are
+ * inside the circle or not.
+ * v2
+ * /\
+ * //\\
+ * v0 /_\/_\ v1
+ */
+ double outerRadius = aRadius + ( lineWidth / 2 );
+ currentManager->Shader( SHADER_STROKED_CIRCLE, 1.0, aRadius, lineWidth );
+ currentManager->Vertex( aCenterPoint.x - outerRadius * sqrt( 3.0f ), // v0
+ aCenterPoint.y - outerRadius, layerDepth );
+
+ currentManager->Shader( SHADER_STROKED_CIRCLE, 2.0, aRadius, lineWidth );
+ currentManager->Vertex( aCenterPoint.x + outerRadius * sqrt( 3.0f ), // v1
+ aCenterPoint.y - outerRadius, layerDepth );
+
+ currentManager->Shader( SHADER_STROKED_CIRCLE, 3.0, aRadius, lineWidth );
+ currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + outerRadius * 2.0f, // v2
+ layerDepth );
+ }
+}
+
+
+void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle,
+ double aEndAngle )
+{
+ if( aRadius <= 0 )
+ return;
+
+ // Swap the angles, if start angle is greater than end angle
+ SWAP( aStartAngle, >, aEndAngle );
+
+ Save();
+ currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
+
+ if( isStrokeEnabled )
+ {
+ const double alphaIncrement = 2.0 * M_PI / CIRCLE_POINTS;
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+
+ VECTOR2D p( cos( aStartAngle ) * aRadius, sin( aStartAngle ) * aRadius );
+ double alpha;
+
+ for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement )
+ {
+ VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
+ DrawLine( p, p_next );
+
+ p = p_next;
+ }
+
+ // Draw the last missing part
+ if( alpha != aEndAngle )
+ {
+ VECTOR2D p_last( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
+ DrawLine( p, p_last );
+ }
+ }
+
+ if( isFillEnabled )
+ {
+ const double alphaIncrement = 2 * M_PI / CIRCLE_POINTS;
+ double alpha;
+ currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
+ currentManager->Shader( SHADER_NONE );
+
+ // Triangle fan
+ for( alpha = aStartAngle; ( alpha + alphaIncrement ) < aEndAngle; )
+ {
+ currentManager->Vertex( 0.0, 0.0, 0.0 );
+ currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 );
+ alpha += alphaIncrement;
+ currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 );
+ }
+
+ // The last missing triangle
+ const VECTOR2D endPoint( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
+ currentManager->Vertex( 0.0, 0.0, 0.0 );
+ currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 );
+ currentManager->Vertex( endPoint.x, endPoint.y, 0.0 );
+ }
+
+ Restore();
+}
+
+
+void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
+{
+ // Compute the diagonal points of the rectangle
+ VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
+ VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
+
+ // Stroke the outline
+ if( isStrokeEnabled )
+ {
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+
+ std::deque<VECTOR2D> pointList;
+ pointList.push_back( aStartPoint );
+ pointList.push_back( diagonalPointA );
+ pointList.push_back( aEndPoint );
+ pointList.push_back( diagonalPointB );
+ pointList.push_back( aStartPoint );
+ DrawPolyline( pointList );
+ }
+
+ // Fill the rectangle
+ if( isFillEnabled )
+ {
+ currentManager->Shader( SHADER_NONE );
+ currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
+
+ currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
+ currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, layerDepth );
+ currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
+
+ currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
+ currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
+ currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, layerDepth );
+ }
+}
+
+
+void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
+{
+ if( aPointList.size() < 2 )
+ return;
+
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+
+ std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
+
+ // Start from the second point
+ for( ++it; it != aPointList.end(); ++it )
+ {
+ const VECTOR2D startEndVector = ( *it - *( it - 1 ) );
+ double lineAngle = startEndVector.Angle();
+
+ drawLineQuad( *( it - 1 ), *it );
+
+ // There is no need to draw line caps on both ends of polyline's segments
+ drawFilledSemiCircle( *( it - 1 ), lineWidth / 2, lineAngle + M_PI / 2 );
+ }
+
+ // ..and now - draw the ending cap
+ const VECTOR2D startEndVector = ( *( it - 1 ) - *( it - 2 ) );
+ double lineAngle = startEndVector.Angle();
+ drawFilledSemiCircle( *( it - 1 ), lineWidth / 2, lineAngle - M_PI / 2 );
+}
+
+
+void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize )
+{
+ if( aListSize < 2 )
+ return;
+
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+
+ // Start from the second point
+ for( int i = 1; i < aListSize; ++i )
+ {
+ const VECTOR2D startEndVector = ( aPointList[i] - aPointList[i - 1] );
+ double lineAngle = startEndVector.Angle();
+
+ drawLineQuad( aPointList[i - 1], aPointList[i] );
+
+ // There is no need to draw line caps on both ends of polyline's segments
+ drawFilledSemiCircle( aPointList[i - 1], lineWidth / 2, lineAngle + M_PI / 2 );
+ }
+
+ // ..and now - draw the ending cap
+ const VECTOR2D startEndVector = ( aPointList[aListSize - 1] - aPointList[aListSize - 2] );
+ double lineAngle = startEndVector.Angle();
+ drawFilledSemiCircle( aPointList[aListSize - 1], lineWidth / 2, lineAngle - M_PI / 2 );
+}
+
+
+void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
+{
+ currentManager->Shader( SHADER_NONE );
+ currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
+
+ // Any non convex polygon needs to be tesselated
+ // for this purpose the GLU standard functions are used
+ TessParams params = { currentManager, tessIntersects };
+ gluTessBeginPolygon( tesselator, &params );
+ gluTessBeginContour( tesselator );
+
+ boost::shared_array<GLdouble> points( new GLdouble[3 * aPointList.size()] );
+ int v = 0;
+
+ for( std::deque<VECTOR2D>::const_iterator it = aPointList.begin(); it != aPointList.end(); ++it )
+ {
+ points[v] = it->x;
+ points[v + 1] = it->y;
+ points[v + 2] = layerDepth;
+ gluTessVertex( tesselator, &points[v], &points[v] );
+ v += 3;
+ }
+
+ gluTessEndContour( tesselator );
+ gluTessEndPolygon( tesselator );
+
+ // Free allocated intersecting points
+ tessIntersects.clear();
+
+ // vertexList destroyed here
+}
+
+
+void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
+{
+ currentManager->Shader( SHADER_NONE );
+ currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
+
+ // Any non convex polygon needs to be tesselated
+ // for this purpose the GLU standard functions are used
+ TessParams params = { currentManager, tessIntersects };
+ gluTessBeginPolygon( tesselator, &params );
+ gluTessBeginContour( tesselator );
+
+ boost::shared_array<GLdouble> points( new GLdouble[3 * aListSize] );
+ int v = 0;
+ const VECTOR2D* ptr = aPointList;
+
+ for( int i = 0; i < aListSize; ++i )
+ {
+ points[v] = ptr->x;
+ points[v + 1] = ptr->y;
+ points[v + 2] = layerDepth;
+ gluTessVertex( tesselator, &points[v], &points[v] );
+ ++ptr;
+ v += 3;
+ }
+
+ gluTessEndContour( tesselator );
+ gluTessEndPolygon( tesselator );
+
+ // Free allocated intersecting points
+ tessIntersects.clear();
+
+ // vertexList destroyed here
+}
+
+
+void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
+ const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint )
+{
+ // FIXME The drawing quality needs to be improved
+ // FIXME Perhaps choose a quad/triangle strip instead?
+ // FIXME Brute force method, use a better (recursive?) algorithm
+
+ std::deque<VECTOR2D> pointList;
+
+ double t = 0.0;
+ double dt = 1.0 / (double) CURVE_POINTS;
+
+ for( int i = 0; i <= CURVE_POINTS; i++ )
+ {
+ double omt = 1.0 - t;
+ double omt2 = omt * omt;
+ double omt3 = omt * omt2;
+ double t2 = t * t;
+ double t3 = t * t2;
+
+ VECTOR2D vertex = omt3 * aStartPoint + 3.0 * t * omt2 * aControlPointA
+ + 3.0 * t2 * omt * aControlPointB + t3 * aEndPoint;
+
+ pointList.push_back( vertex );
+
+ t += dt;
+ }
+
+ DrawPolyline( pointList );
+}
+
+
+void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
+{
+ screenSize = VECTOR2I( aWidth, aHeight );
+
+#ifdef RETINA_OPENGL_PATCH
+ const float scaleFactor = GetBackingScaleFactor();
+#else
+ const float scaleFactor = 1.0f;
+#endif
+
+ // Resize framebuffers
+ compositor.Resize( aWidth * scaleFactor, aHeight * scaleFactor );
+ isFramebufferInitialized = false;
+
+ wxGLCanvas::SetSize( aWidth, aHeight );
+}
+
+
+bool OPENGL_GAL::Show( bool aShow )
+{
+ bool s = wxGLCanvas::Show( aShow );
+
+ if( aShow )
+ wxGLCanvas::Raise();
+
+ return s;
+}
+
+
+void OPENGL_GAL::Flush()
+{
+ glFlush();
+}
+
+
+void OPENGL_GAL::ClearScreen( const COLOR4D& aColor )
+{
+ // Clear screen
+ glClearColor( aColor.r, aColor.g, aColor.b, aColor.a );
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+}
+
+
+void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
+{
+ GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+
+ matrixData[0] = aTransformation.m_data[0][0];
+ matrixData[1] = aTransformation.m_data[1][0];
+ matrixData[2] = aTransformation.m_data[2][0];
+ matrixData[4] = aTransformation.m_data[0][1];
+ matrixData[5] = aTransformation.m_data[1][1];
+ matrixData[6] = aTransformation.m_data[2][1];
+ matrixData[12] = aTransformation.m_data[0][2];
+ matrixData[13] = aTransformation.m_data[1][2];
+ matrixData[14] = aTransformation.m_data[2][2];
+
+ glMultMatrixd( matrixData );
+}
+
+
+void OPENGL_GAL::Rotate( double aAngle )
+{
+ currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
+}
+
+
+void OPENGL_GAL::Translate( const VECTOR2D& aVector )
+{
+ currentManager->Translate( aVector.x, aVector.y, 0.0f );
+}
+
+
+void OPENGL_GAL::Scale( const VECTOR2D& aScale )
+{
+ currentManager->Scale( aScale.x, aScale.y, 0.0f );
+}
+
+
+void OPENGL_GAL::Save()
+{
+ currentManager->PushMatrix();
+}
+
+
+void OPENGL_GAL::Restore()
+{
+ currentManager->PopMatrix();
+}
+
+
+int OPENGL_GAL::BeginGroup()
+{
+ isGrouping = true;
+
+ boost::shared_ptr<VERTEX_ITEM> newItem( new VERTEX_ITEM( cachedManager ) );
+ int groupNumber = getNewGroupNumber();
+ groups.insert( std::make_pair( groupNumber, newItem ) );
+
+ return groupNumber;
+}
+
+
+void OPENGL_GAL::EndGroup()
+{
+ cachedManager.FinishItem();
+ isGrouping = false;
+}
+
+
+void OPENGL_GAL::DrawGroup( int aGroupNumber )
+{
+ cachedManager.DrawItem( *groups[aGroupNumber] );
+}
+
+
+void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
+{
+ cachedManager.ChangeItemColor( *groups[aGroupNumber], aNewColor );
+}
+
+
+void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
+{
+ cachedManager.ChangeItemDepth( *groups[aGroupNumber], aDepth );
+}
+
+
+void OPENGL_GAL::DeleteGroup( int aGroupNumber )
+{
+ // Frees memory in the container as well
+ groups.erase( aGroupNumber );
+}
+
+
+void OPENGL_GAL::ClearCache()
+{
+ groups.clear();
+ cachedManager.Clear();
+}
+
+
+void OPENGL_GAL::SaveScreen()
+{
+ wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
+}
+
+
+void OPENGL_GAL::RestoreScreen()
+{
+ wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
+}
+
+
+void OPENGL_GAL::SetTarget( RENDER_TARGET aTarget )
+{
+ switch( aTarget )
+ {
+ default:
+ case TARGET_CACHED:
+ currentManager = &cachedManager;
+ break;
+
+ case TARGET_NONCACHED:
+ currentManager = &nonCachedManager;
+ break;
+
+ case TARGET_OVERLAY:
+ currentManager = &overlayManager;
+ break;
+ }
+
+ currentTarget = aTarget;
+}
+
+
+RENDER_TARGET OPENGL_GAL::GetTarget() const
+{
+ return currentTarget;
+}
+
+
+void OPENGL_GAL::ClearTarget( RENDER_TARGET aTarget )
+{
+ // Save the current state
+ unsigned int oldTarget = compositor.GetBuffer();
+
+ switch( aTarget )
+ {
+ // Cached and noncached items are rendered to the same buffer
+ default:
+ case TARGET_CACHED:
+ case TARGET_NONCACHED:
+ compositor.SetBuffer( mainBuffer );
+ break;
+
+ case TARGET_OVERLAY:
+ compositor.SetBuffer( overlayBuffer );
+ break;
+ }
+
+ compositor.ClearBuffer();
+
+ // Restore the previous state
+ compositor.SetBuffer( oldTarget );
+}
+
+
+void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
+{
+ // Now we should only store the position of the mouse cursor
+ // The real drawing routines are in blitCursor()
+ VECTOR2D screenCursor = worldScreenMatrix * aCursorPosition;
+
+ cursorPosition = screenWorldMatrix * VECTOR2D( screenCursor.x, screenSize.y - screenCursor.y );
+}
+
+
+void OPENGL_GAL::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
+{
+ compositor.SetBuffer( mainBuffer );
+
+ // We do not need a very precise comparison here (the lineWidth is set by GAL::DrawGrid())
+ if( fabs( lineWidth - 2.0 * gridLineWidth / worldScale ) < 0.1 )
+ glLineWidth( 1.0 );
+ else
+ glLineWidth( 2.0 );
+
+ glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
+
+ glBegin( GL_LINES );
+ glVertex3d( aStartPoint.x, aStartPoint.y, layerDepth );
+ glVertex3d( aEndPoint.x, aEndPoint.y, layerDepth );
+ glEnd();
+
+ // Restore the default color, so textures will be drawn properly
+ glColor4d( 1.0, 1.0, 1.0, 1.0 );
+}
+
+
+void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
+{
+ /* Helper drawing: ____--- v3 ^
+ * ____---- ... \ \
+ * ____---- ... \ end \
+ * v1 ____---- ... ____---- \ width
+ * ---- ...___---- \ \
+ * \ ___...-- \ v
+ * \ ____----... ____---- v2
+ * ---- ... ____----
+ * start \ ... ____----
+ * \... ____----
+ * ----
+ * v0
+ * dots mark triangles' hypotenuses
+ */
+
+ VECTOR2D startEndVector = aEndPoint - aStartPoint;
+ double lineLength = startEndVector.EuclideanNorm();
+
+ if( lineLength <= 0.0 )
+ return;
+
+ double scale = 0.5 * lineWidth / lineLength;
+
+ // The perpendicular vector also needs transformations
+ glm::vec4 vector = currentManager->GetTransformation() *
+ glm::vec4( -startEndVector.y * scale, startEndVector.x * scale, 0.0, 0.0 );
+
+ // Line width is maintained by the vertex shader
+ currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth );
+ currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v0
+
+ currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth );
+ currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v1
+
+ currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth );
+ currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v3
+
+ currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth );
+ currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v0
+
+ currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth );
+ currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v3
+
+ currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth );
+ currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v2
+}
+
+
+void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
+{
+ if( isFillEnabled )
+ {
+ currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
+ drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
+ }
+
+ if( isStrokeEnabled )
+ {
+ currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
+ drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
+ }
+}
+
+
+void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
+ double aAngle )
+{
+ Save();
+ currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
+ currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
+
+ /* Draw a triangle that contains the semicircle, then shade it to leave only
+ * the semicircle. Parameters given to setShader are indices of the triangle's vertices
+ * (if you want to understand more, check the vertex shader source [shader.vert]).
+ * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
+ * v2
+ * /\
+ * /__\
+ * v0 //__\\ v1
+ */
+ currentManager->Shader( SHADER_FILLED_CIRCLE, 4.0f );
+ currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
+
+ currentManager->Shader( SHADER_FILLED_CIRCLE, 5.0f );
+ currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
+
+ currentManager->Shader( SHADER_FILLED_CIRCLE, 6.0f );
+ currentManager->Vertex( 0.0f, aRadius * 2.0f, layerDepth ); // v2
+
+ Restore();
+}
+
+
+void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
+ double aAngle )
+{
+ double outerRadius = aRadius + ( lineWidth / 2 );
+
+ Save();
+ currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
+ currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
+
+ /* Draw a triangle that contains the semicircle, then shade it to leave only
+ * the semicircle. Parameters given to setShader are indices of the triangle's vertices
+ * (if you want to understand more, check the vertex shader source [shader.vert]), the
+ * radius and the line width. Shader uses these coordinates to determine if fragments are
+ * inside the semicircle or not.
+ * v2
+ * /\
+ * /__\
+ * v0 //__\\ v1
+ */
+ currentManager->Shader( SHADER_STROKED_CIRCLE, 4.0f, aRadius, lineWidth );
+ currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
+
+ currentManager->Shader( SHADER_STROKED_CIRCLE, 5.0f, aRadius, lineWidth );
+ currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
+
+ currentManager->Shader( SHADER_STROKED_CIRCLE, 6.0f, aRadius, lineWidth );
+ currentManager->Vertex( 0.0f, outerRadius * 2.0f, layerDepth ); // v2
+
+ Restore();
+}
+
+
+void OPENGL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
+{
+ PostPaint();
+}
+
+
+void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
+{
+ // Post the mouse event to the event listener registered in constructor, if any
+ if( mouseListener )
+ wxPostEvent( mouseListener, aEvent );
+}
+
+
+void OPENGL_GAL::blitCursor()
+{
+ if( !isCursorEnabled )
+ return;
+
+ compositor.SetBuffer( OPENGL_COMPOSITOR::DIRECT_RENDERING );
+
+ VECTOR2D cursorBegin = cursorPosition - cursorSize / ( 2 * worldScale );
+ VECTOR2D cursorEnd = cursorPosition + cursorSize / ( 2 * worldScale );
+ VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
+
+ glDisable( GL_TEXTURE_2D );
+ glLineWidth( 1.0 );
+ glColor4d( cursorColor.r, cursorColor.g, cursorColor.b, cursorColor.a );
+
+ glBegin( GL_LINES );
+ glVertex2d( cursorCenter.x, cursorBegin.y );
+ glVertex2d( cursorCenter.x, cursorEnd.y );
+
+ glVertex2d( cursorBegin.x, cursorCenter.y );
+ glVertex2d( cursorEnd.x, cursorCenter.y );
+ glEnd();
+}
+
+
+unsigned int OPENGL_GAL::getNewGroupNumber()
+{
+ wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(),
+ wxT( "There are no free slots to store a group" ) );
+
+ while( groups.find( groupCounter ) != groups.end() )
+ {
+ groupCounter++;
+ }
+
+ return groupCounter++;
+}
+
+
+bool OPENGL_GAL::runTest()
+{
+ wxDialog dlgtest( GetParent(), -1, wxT( "opengl test" ), wxPoint( 50, 50 ),
+ wxDLG_UNIT( GetParent(), wxSize( 50, 50 ) ) );
+ OPENGL_TEST* test = new OPENGL_TEST( &dlgtest, this );
+
+ dlgtest.Raise(); // on Linux, on some windows managers (Unity for instance) this is needed to actually show the dialog
+ dlgtest.ShowModal();
+ bool result = test->IsOk();
+
+ if( !result )
+ throw std::runtime_error( test->GetError() );
+
+ return result;
+}
+
+
+OPENGL_GAL::OPENGL_TEST::OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal ) :
+ wxGLCanvas( aParent, wxID_ANY, glAttributes, wxDefaultPosition,
+ wxDefaultSize, 0, wxT( "GLCanvas" ) ),
+ m_parent( aParent ), m_gal( aGal ), m_tested( false ), m_result( false )
+{
+ m_timeoutTimer.SetOwner( this );
+ Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) );
+ Connect( wxEVT_TIMER, wxTimerEventHandler( OPENGL_GAL::OPENGL_TEST::OnTimeout ) );
+ m_parent->Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::OnDialogPaint ), NULL, this );
+}
+
+
+void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) )
+{
+ if( !m_tested )
+ {
+ if( !IsShownOnScreen() )
+ return;
+
+ m_timeoutTimer.Stop();
+ m_result = true; // Assume everything is fine, until proven otherwise
+
+ // One test is enough - close the testing dialog when the test is finished
+ Disconnect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) );
+ CallAfter( boost::bind( &wxDialog::EndModal, m_parent, wxID_NONE ) );
+
+ SetCurrent( *OPENGL_GAL::glContext );
+ GLenum err = glewInit();
+
+ if( GLEW_OK != err )
+ {
+ error( (const char*) glewGetErrorString( err ) );
+ return;
+ }
+ else
+ {
+ wxLogDebug( wxString( wxT( "Status: Using GLEW " ) ) +
+ FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
+ }
+
+ // Check the OpenGL version (minimum 2.1 is required)
+ if( GLEW_VERSION_2_1 )
+ {
+ wxLogInfo( wxT( "OpenGL 2.1 supported." ) );
+ }
+ else
+ {
+ error( "OpenGL 2.1 or higher is required!" );
+ return;
+ }
+
+ // Framebuffers have to be supported
+ if( !GLEW_EXT_framebuffer_object )
+ {
+ error( "Framebuffer objects are not supported!" );
+ return;
+ }
+
+ // Vertex buffer has to be supported
+ if( !GLEW_ARB_vertex_buffer_object )
+ {
+ error( "Vertex buffer objects are not supported!" );
+ return;
+ }
+
+ // Prepare shaders
+ if( !m_gal->shader.LoadBuiltinShader( 0, SHADER_TYPE_VERTEX ) )
+ {
+ error( "Cannot compile vertex shader!" );
+ return;
+ }
+
+ if( !m_gal->shader.LoadBuiltinShader( 1, SHADER_TYPE_FRAGMENT ) )
+ {
+ error( "Cannot compile fragment shader!" );
+ return;
+ }
+
+ if( !m_gal->shader.Link() )
+ {
+ error( "Cannot link the shaders!" );
+ return;
+ }
+
+ m_tested = true;
+ }
+}
+
+
+void OPENGL_GAL::OPENGL_TEST::OnTimeout( wxTimerEvent& aEvent )
+{
+ error( "Could not create OpenGL canvas" );
+ m_parent->EndModal( wxID_NONE );
+}
+
+
+void OPENGL_GAL::OPENGL_TEST::OnDialogPaint( wxPaintEvent& aEvent )
+{
+ // GL canvas may never appear on the screen (e.g. due to missing GL extensions), and the test
+ // will not be run. Therefore give at most a second to perform the test, otherwise we conclude
+ // it has failed.
+ // Also, wxWidgets OnShow event is triggered before a window is shown, therefore here we use
+ // OnPaint event, which is executed when a window is actually visible.
+ m_timeoutTimer.StartOnce( 1000 );
+}
+
+
+void OPENGL_GAL::OPENGL_TEST::error( const std::string& aError )
+{
+ m_timeoutTimer.Stop();
+ m_result = false;
+ m_tested = true;
+ m_error = aError;
+}
+
+// ------------------------------------- // Callback functions for the tesselator // ------------------------------------- // Compare Redbook Chapter 11
+void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
+{
+ GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
+ OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
+ VERTEX_MANAGER* vboManager = param->vboManager;
+
+ if( vboManager )
+ vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
+}
+
+
+void CALLBACK CombineCallback( GLdouble coords[3],
+ GLdouble* vertex_data[4],
+ GLfloat weight[4], GLdouble** dataOut, void* aData )
+{
+ GLdouble* vertex = new GLdouble[3];
+ OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
+
+ // Save the pointer so we can delete it later
+ param->intersectPoints.push_back( boost::shared_array<GLdouble>( vertex ) );
+
+ memcpy( vertex, coords, 3 * sizeof(GLdouble) );
+
+ *dataOut = vertex;
+}
+
+
+void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
+{
+ // This callback is needed to force GLU tesselator to use triangles only
+}
+
+
+void CALLBACK ErrorCallback( GLenum aErrorCode )
+{
+ //throw std::runtime_error( std::string( "Tessellation error: " ) +
+ //std::string( (const char*) gluErrorString( aErrorCode ) );
+}
+
+
+static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
+{
+ gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, ( void (CALLBACK*)() )VertexCallback );
+ gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback );
+ gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, ( void (CALLBACK*)() )EdgeCallback );
+ gluTessCallback( aTesselator, GLU_TESS_ERROR, ( void (CALLBACK*)() )ErrorCallback );
+}
diff --git a/common/gal/opengl/shader.cpp b/common/gal/opengl/shader.cpp
new file mode 100644
index 0000000..228d53d
--- /dev/null
+++ b/common/gal/opengl/shader.cpp
@@ -0,0 +1,272 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
+ * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors.
+ *
+ * Graphics Abstraction Layer (GAL) for OpenGL
+ *
+ * Shader class
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <iostream>
+#include <fstream>
+#include <stdexcept>
+
+#include <cstring>
+#include <cassert>
+
+#include <gal/opengl/shader.h>
+#include "shader_src.h"
+
+using namespace KIGFX;
+
+SHADER::SHADER() :
+ isProgramCreated( false ),
+ isShaderLinked( false ),
+ active( false ),
+ maximumVertices( 4 ),
+ geomInputType( GL_LINES ),
+ geomOutputType( GL_LINES )
+
+{
+ // Do not have uninitialized members:
+ programNumber = 0;
+}
+
+
+SHADER::~SHADER()
+{
+ if( isProgramCreated )
+ {
+ // Delete the shaders and the program
+ for( std::deque<GLuint>::iterator it = shaderNumbers.begin(); it != shaderNumbers.end();
+ ++it )
+ {
+ glDeleteShader( *it );
+ }
+
+ glDeleteProgram( programNumber );
+ }
+}
+
+
+bool SHADER::LoadBuiltinShader( unsigned int aShaderNumber, SHADER_TYPE aShaderType )
+{
+ if( aShaderNumber >= shaders_number )
+ return false;
+
+ return addSource( std::string( shaders_src[aShaderNumber] ), aShaderType );
+}
+
+
+bool SHADER::LoadShaderFromFile( const std::string& aShaderSourceName, SHADER_TYPE aShaderType )
+{
+ // Load shader sources
+ const std::string shaderSource = readSource( aShaderSourceName );
+
+ return addSource( shaderSource, aShaderType );
+}
+
+
+void SHADER::ConfigureGeometryShader( GLuint maxVertices, GLuint geometryInputType,
+ GLuint geometryOutputType )
+{
+ maximumVertices = maxVertices;
+ geomInputType = geometryInputType;
+ geomOutputType = geometryOutputType;
+}
+
+
+bool SHADER::Link()
+{
+ // Shader linking
+ glLinkProgram( programNumber );
+ programInfo( programNumber );
+
+ // Check the Link state
+ glGetObjectParameterivARB( programNumber, GL_OBJECT_LINK_STATUS_ARB,
+ (GLint*) &isShaderLinked );
+
+#ifdef DEBUG
+ if( !isShaderLinked )
+ {
+ int maxLength;
+ glGetProgramiv( programNumber, GL_INFO_LOG_LENGTH, &maxLength );
+ maxLength = maxLength + 1;
+ char* linkInfoLog = new char[maxLength];
+ glGetProgramInfoLog( programNumber, maxLength, &maxLength, linkInfoLog );
+ std::cerr << "Shader linking error:" << std::endl;
+ std::cerr << linkInfoLog;
+ delete[] linkInfoLog;
+ }
+#endif /* DEBUG */
+
+ return isShaderLinked;
+}
+
+
+int SHADER::AddParameter( const std::string& aParameterName )
+{
+ GLint location = glGetUniformLocation( programNumber, aParameterName.c_str() );
+
+ if( location != -1 )
+ parameterLocation.push_back( location );
+
+ return location;
+}
+
+
+void SHADER::SetParameter( int parameterNumber, float value ) const
+{
+ glUniform1f( parameterLocation[parameterNumber], value );
+}
+
+
+void SHADER::SetParameter( int parameterNumber, int value ) const
+{
+ glUniform1i( parameterLocation[parameterNumber], value );
+}
+
+
+int SHADER::GetAttribute( std::string aAttributeName ) const
+{
+ return glGetAttribLocation( programNumber, aAttributeName.c_str() );
+}
+
+
+void SHADER::programInfo( GLuint aProgram )
+{
+ GLint glInfoLogLength = 0;
+ GLint writtenChars = 0;
+
+ // Get the length of the info string
+ glGetProgramiv( aProgram, GL_INFO_LOG_LENGTH, &glInfoLogLength );
+
+ // Print the information
+ if( glInfoLogLength > 2 )
+ {
+ GLchar* glInfoLog = new GLchar[glInfoLogLength];
+ glGetProgramInfoLog( aProgram, glInfoLogLength, &writtenChars, glInfoLog );
+
+ std::cerr << glInfoLog << std::endl;
+
+ delete[] glInfoLog;
+ }
+}
+
+
+void SHADER::shaderInfo( GLuint aShader )
+{
+ GLint glInfoLogLength = 0;
+ GLint writtenChars = 0;
+
+ // Get the length of the info string
+ glGetShaderiv( aShader, GL_INFO_LOG_LENGTH, &glInfoLogLength );
+
+ // Print the information
+ if( glInfoLogLength > 2 )
+ {
+ GLchar* glInfoLog = new GLchar[glInfoLogLength];
+ glGetShaderInfoLog( aShader, glInfoLogLength, &writtenChars, glInfoLog );
+
+ std::cerr << glInfoLog << std::endl;
+
+ delete[] glInfoLog;
+ }
+}
+
+
+std::string SHADER::readSource( std::string aShaderSourceName )
+{
+ // Open the shader source for reading
+ std::ifstream inputFile( aShaderSourceName.c_str(), std::ifstream::in );
+ std::string shaderSource;
+
+ if( !inputFile )
+ throw std::runtime_error( "Can't read the shader source: " + aShaderSourceName );
+
+ std::string shaderSourceLine;
+
+ // Read all lines from the text file
+ while( getline( inputFile, shaderSourceLine ) )
+ {
+ shaderSource += shaderSourceLine;
+ shaderSource += "\n";
+ }
+
+ return shaderSource;
+}
+
+
+bool SHADER::addSource( const std::string& aShaderSource, SHADER_TYPE aShaderType )
+{
+ assert( !isShaderLinked );
+
+ // Create the program
+ if( !isProgramCreated )
+ {
+ programNumber = glCreateProgram();
+ isProgramCreated = true;
+ }
+
+ // Create a shader
+ GLuint shaderNumber = glCreateShader( aShaderType );
+ shaderNumbers.push_back( shaderNumber );
+
+ // Get the program info
+ programInfo( programNumber );
+
+ // Copy to char array
+ char* source = new char[aShaderSource.size() + 1];
+ strncpy( source, aShaderSource.c_str(), aShaderSource.size() + 1 );
+ const char** source_ = (const char**) ( &source );
+
+ // Attach the source
+ glShaderSource( shaderNumber, 1, source_, NULL );
+ programInfo( programNumber );
+
+ // Delete the allocated char array
+ delete[] source;
+
+ // Compile and attach shader to the program
+ glCompileShader( shaderNumber );
+ GLint status;
+ glGetShaderiv( shaderNumber, GL_COMPILE_STATUS, &status );
+
+ if( status != GL_TRUE )
+ {
+ shaderInfo( shaderNumber );
+ throw std::runtime_error( "Shader compilation error" );
+ }
+
+ glAttachShader( programNumber, shaderNumber );
+ programInfo( programNumber );
+
+ // Special handling for the geometry shader
+ if( aShaderType == SHADER_TYPE_GEOMETRY )
+ {
+ glProgramParameteriEXT( programNumber, GL_GEOMETRY_VERTICES_OUT_EXT, maximumVertices );
+ glProgramParameteriEXT( programNumber, GL_GEOMETRY_INPUT_TYPE_EXT, geomInputType );
+ glProgramParameteriEXT( programNumber, GL_GEOMETRY_OUTPUT_TYPE_EXT, geomOutputType );
+ }
+
+ return true;
+}
diff --git a/common/gal/opengl/shader.frag b/common/gal/opengl/shader.frag
new file mode 100644
index 0000000..7d7d96c
--- /dev/null
+++ b/common/gal/opengl/shader.frag
@@ -0,0 +1,75 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * Fragment shader
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#version 120
+
+// Shader types
+const float SHADER_LINE = 1.0;
+const float SHADER_FILLED_CIRCLE = 2.0;
+const float SHADER_STROKED_CIRCLE = 3.0;
+
+varying vec4 shaderParams;
+varying vec2 circleCoords;
+
+void filledCircle( vec2 aCoord )
+{
+ if( dot( aCoord, aCoord ) < 1.0 )
+ gl_FragColor = gl_Color;
+ else
+ discard;
+}
+
+
+void strokedCircle( vec2 aCoord, float aRadius, float aWidth )
+{
+ float outerRadius = aRadius + ( aWidth / 2 );
+ float innerRadius = aRadius - ( aWidth / 2 );
+ float relWidth = innerRadius / outerRadius;
+
+ if( ( dot( aCoord, aCoord ) < 1.0 ) &&
+ ( dot( aCoord, aCoord ) > relWidth * relWidth ) )
+ gl_FragColor = gl_Color;
+ else
+ discard;
+}
+
+
+void main()
+{
+ if( shaderParams[0] == SHADER_FILLED_CIRCLE )
+ {
+ filledCircle( circleCoords );
+ }
+ else if( shaderParams[0] == SHADER_STROKED_CIRCLE )
+ {
+ strokedCircle( circleCoords, shaderParams[2], shaderParams[3] );
+ }
+ else
+ {
+ // Simple pass-through
+ gl_FragColor = gl_Color;
+ }
+}
diff --git a/common/gal/opengl/shader.vert b/common/gal/opengl/shader.vert
new file mode 100644
index 0000000..24521a4
--- /dev/null
+++ b/common/gal/opengl/shader.vert
@@ -0,0 +1,95 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * Vertex shader
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#version 120
+
+// Shader types
+const float SHADER_LINE = 1.0;
+const float SHADER_FILLED_CIRCLE = 2.0;
+const float SHADER_STROKED_CIRCLE = 3.0;
+
+// Minimum line width
+const float MIN_WIDTH = 1.0;
+
+attribute vec4 attrShaderParams;
+varying vec4 shaderParams;
+varying vec2 circleCoords;
+
+void main()
+{
+ // Pass attributes to the fragment shader
+ shaderParams = attrShaderParams;
+
+ if( shaderParams[0] == SHADER_LINE )
+ {
+ float lineWidth = shaderParams[3];
+ float worldScale = gl_ModelViewMatrix[0][0];
+
+ // Make lines appear to be at least 1 pixel wide
+ if( worldScale * lineWidth < MIN_WIDTH )
+ gl_Position = gl_ModelViewProjectionMatrix *
+ ( gl_Vertex + vec4( shaderParams.yz * MIN_WIDTH / ( worldScale * lineWidth ), 0.0, 0.0 ) );
+ else
+ gl_Position = gl_ModelViewProjectionMatrix *
+ ( gl_Vertex + vec4( shaderParams.yz, 0.0, 0.0 ) );
+ }
+ else if( ( shaderParams[0] == SHADER_STROKED_CIRCLE ) ||
+ ( shaderParams[0] == SHADER_FILLED_CIRCLE ) )
+ {
+ // Compute relative circle coordinates basing on indices
+ // Circle
+ if( shaderParams[1] == 1.0 )
+ circleCoords = vec2( -sqrt( 3.0 ), -1.0 );
+ else if( shaderParams[1] == 2.0 )
+ circleCoords = vec2( sqrt( 3.0 ), -1.0 );
+ else if( shaderParams[1] == 3.0 )
+ circleCoords = vec2( 0.0, 2.0 );
+
+ // Semicircle
+ else if( shaderParams[1] == 4.0 )
+ circleCoords = vec2( -3.0 / sqrt( 3.0 ), 0.0 );
+ else if( shaderParams[1] == 5.0 )
+ circleCoords = vec2( 3.0 / sqrt( 3.0 ), 0.0 );
+ else if( shaderParams[1] == 6.0 )
+ circleCoords = vec2( 0.0, 2.0 );
+
+ // Make the line appear to be at least 1 pixel wide
+ float lineWidth = shaderParams[3];
+ float worldScale = gl_ModelViewMatrix[0][0];
+
+ if( worldScale * lineWidth < MIN_WIDTH )
+ shaderParams[3] = shaderParams[3] / ( worldScale * lineWidth );
+
+ gl_Position = ftransform();
+ }
+ else
+ {
+ // Pass through the coordinates like in the fixed pipeline
+ gl_Position = ftransform();
+ }
+
+ gl_FrontColor = gl_Color;
+}
diff --git a/common/gal/opengl/vertex_container.cpp b/common/gal/opengl/vertex_container.cpp
new file mode 100644
index 0000000..fa41ecb
--- /dev/null
+++ b/common/gal/opengl/vertex_container.cpp
@@ -0,0 +1,60 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file vertex_container.cpp
+ * @brief Class to store vertices and handle transfers between system memory and GPU memory.
+ */
+
+#include <gal/opengl/vertex_container.h>
+#include <gal/opengl/cached_container.h>
+#include <gal/opengl/noncached_container.h>
+#include <gal/opengl/shader.h>
+#include <cstdlib>
+#include <cstring>
+
+using namespace KIGFX;
+
+VERTEX_CONTAINER* VERTEX_CONTAINER::MakeContainer( bool aCached )
+{
+ if( aCached )
+ return new CACHED_CONTAINER;
+ else
+ return new NONCACHED_CONTAINER;
+}
+
+
+VERTEX_CONTAINER::VERTEX_CONTAINER( unsigned int aSize ) :
+ m_freeSpace( aSize ), m_currentSize( aSize ), m_initialSize( aSize ),
+ m_failed( false ), m_dirty( true )
+{
+ m_vertices = static_cast<VERTEX*>( malloc( aSize * sizeof( VERTEX ) ) );
+ memset( m_vertices, 0x00, aSize * sizeof( VERTEX ) );
+}
+
+
+VERTEX_CONTAINER::~VERTEX_CONTAINER()
+{
+ free( m_vertices );
+}
diff --git a/common/gal/opengl/vertex_item.cpp b/common/gal/opengl/vertex_item.cpp
new file mode 100644
index 0000000..a6fd352
--- /dev/null
+++ b/common/gal/opengl/vertex_item.cpp
@@ -0,0 +1,53 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file vertex_item.cpp
+ * @brief Class to handle an item held in a container.
+ */
+
+#include <gal/opengl/vertex_item.h>
+#include <gal/opengl/vertex_manager.h>
+#include <cstring>
+
+using namespace KIGFX;
+
+VERTEX_ITEM::VERTEX_ITEM( const VERTEX_MANAGER& aManager ) :
+ m_manager( aManager ), m_offset( 0 ), m_size( 0 )
+{
+ // As the item is created, we are going to modify it, so call to SetItem() is needed
+ m_manager.SetItem( *this );
+}
+
+
+VERTEX_ITEM::~VERTEX_ITEM()
+{
+ m_manager.FreeItem( *this );
+}
+
+
+VERTEX* VERTEX_ITEM::GetVertices() const
+{
+ return m_manager.GetVertices( *this );
+}
diff --git a/common/gal/opengl/vertex_manager.cpp b/common/gal/opengl/vertex_manager.cpp
new file mode 100644
index 0000000..d055b52
--- /dev/null
+++ b/common/gal/opengl/vertex_manager.cpp
@@ -0,0 +1,234 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file vertex_manager.cpp
+ * @brief Class to control vertex container and GPU with possibility of emulating old-style OpenGL
+ * 1.0 state machine using modern OpenGL methods.
+ */
+
+#include <gal/opengl/vertex_manager.h>
+#include <gal/opengl/cached_container.h>
+#include <gal/opengl/noncached_container.h>
+#include <gal/opengl/gpu_manager.h>
+#include <gal/opengl/vertex_item.h>
+#include <confirm.h>
+
+using namespace KIGFX;
+
+VERTEX_MANAGER::VERTEX_MANAGER( bool aCached ) :
+ m_noTransform( true ), m_transform( 1.0f )
+{
+ m_container.reset( VERTEX_CONTAINER::MakeContainer( aCached ) );
+ m_gpu.reset( GPU_MANAGER::MakeManager( m_container.get() ) );
+
+ // There is no shader used by default
+ for( unsigned int i = 0; i < ShaderStride; ++i )
+ m_shader[i] = 0.0f;
+}
+
+
+void VERTEX_MANAGER::Vertex( GLfloat aX, GLfloat aY, GLfloat aZ ) const
+{
+ // flag to avoid hanging by calling DisplayError too many times:
+ static bool show_err = true;
+
+ // Obtain the pointer to the vertex in the currently used container
+ VERTEX* newVertex = m_container->Allocate( 1 );
+
+ if( newVertex == NULL )
+ {
+ if( show_err )
+ {
+ DisplayError( NULL, wxT( "VERTEX_MANAGER::Vertex: Vertex allocation error" ) );
+ show_err = false;
+ }
+
+ return;
+ }
+
+ putVertex( *newVertex, aX, aY, aZ );
+}
+
+
+void VERTEX_MANAGER::Vertices( const VERTEX aVertices[], unsigned int aSize ) const
+{
+ // flag to avoid hanging by calling DisplayError too many times:
+ static bool show_err = true;
+
+ // Obtain pointer to the vertex in currently used container
+ VERTEX* newVertex = m_container->Allocate( aSize );
+
+ if( newVertex == NULL )
+ {
+ if( show_err )
+ {
+ DisplayError( NULL, wxT( "VERTEX_MANAGER::Vertices: Vertex allocation error" ) );
+ show_err = false;
+ }
+
+ return;
+ }
+
+ // Put vertices in already allocated memory chunk
+ for( unsigned int i = 0; i < aSize; ++i )
+ {
+ putVertex( newVertex[i], aVertices[i].x, aVertices[i].y, aVertices[i].z );
+ }
+}
+
+
+void VERTEX_MANAGER::SetItem( VERTEX_ITEM& aItem ) const
+{
+ m_container->SetItem( &aItem );
+}
+
+
+void VERTEX_MANAGER::FinishItem() const
+{
+ m_container->FinishItem();
+}
+
+
+void VERTEX_MANAGER::FreeItem( VERTEX_ITEM& aItem ) const
+{
+ m_container->Delete( &aItem );
+}
+
+
+void VERTEX_MANAGER::ChangeItemColor( const VERTEX_ITEM& aItem, const COLOR4D& aColor ) const
+{
+ unsigned int size = aItem.GetSize();
+ unsigned int offset = aItem.GetOffset();
+
+ VERTEX* vertex = m_container->GetVertices( offset );
+
+ for( unsigned int i = 0; i < size; ++i )
+ {
+ vertex->r = aColor.r * 255.0;
+ vertex->g = aColor.g * 255.0;
+ vertex->b = aColor.b * 255.0;
+ vertex->a = aColor.a * 255.0;
+ vertex++;
+ }
+
+ m_container->SetDirty();
+}
+
+
+void VERTEX_MANAGER::ChangeItemDepth( const VERTEX_ITEM& aItem, GLfloat aDepth ) const
+{
+ unsigned int size = aItem.GetSize();
+ unsigned int offset = aItem.GetOffset();
+
+ VERTEX* vertex = m_container->GetVertices( offset );
+
+ for( unsigned int i = 0; i < size; ++i )
+ {
+ vertex->z = aDepth;
+ vertex++;
+ }
+
+ m_container->SetDirty();
+}
+
+
+VERTEX* VERTEX_MANAGER::GetVertices( const VERTEX_ITEM& aItem ) const
+{
+ if( aItem.GetSize() == 0 )
+ return NULL; // The item is not stored in the container
+
+ return m_container->GetVertices( aItem.GetOffset() );
+}
+
+
+void VERTEX_MANAGER::SetShader( SHADER& aShader ) const
+{
+ m_gpu->SetShader( aShader );
+}
+
+
+void VERTEX_MANAGER::Clear() const
+{
+ m_container->Clear();
+}
+
+
+void VERTEX_MANAGER::BeginDrawing() const
+{
+ m_gpu->BeginDrawing();
+}
+
+
+void VERTEX_MANAGER::DrawItem( const VERTEX_ITEM& aItem ) const
+{
+ int size = aItem.GetSize();
+
+ if( size > 0 )
+ {
+ int offset = aItem.GetOffset();
+ m_gpu->DrawIndices( offset, size );
+ }
+}
+
+
+void VERTEX_MANAGER::EndDrawing() const
+{
+ m_gpu->EndDrawing();
+}
+
+
+void VERTEX_MANAGER::putVertex( VERTEX& aTarget, GLfloat aX, GLfloat aY, GLfloat aZ ) const
+{
+ // Modify the vertex according to the currently used transformations
+ if( m_noTransform )
+ {
+ // Simply copy coordinates, when the transform matrix is the identity matrix
+ aTarget.x = aX;
+ aTarget.y = aY;
+ aTarget.z = aZ;
+ }
+ else
+ {
+ // Apply transformations
+ glm::vec4 transVertex( aX, aY, aZ, 1.0f );
+ transVertex = m_transform * transVertex;
+
+ aTarget.x = transVertex.x;
+ aTarget.y = transVertex.y;
+ aTarget.z = transVertex.z;
+ }
+
+ // Apply currently used color
+ aTarget.r = m_color[0];
+ aTarget.g = m_color[1];
+ aTarget.b = m_color[2];
+ aTarget.a = m_color[3];
+
+ // Apply currently used shader
+ for( unsigned int j = 0; j < ShaderStride; ++j )
+ {
+ aTarget.shader[j] = m_shader[j];
+ }
+}