2012-05-18 09:45:23 +02:00
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2012 Ceetron AS
//
// This library 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 3 of the License, or
// (at your option) any later version.
//
// This library 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 at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
//##################################################################################################
#include "cvfBase.h"
#include "cvfDrawableText.h"
#include "cvfOpenGL.h"
#include "cvfOpenGLResourceManager.h"
#include "cvfBufferObjectManaged.h"
#include "cvfDrawableGeo.h"
#include "cvfQuat.h"
#include "cvfUniform.h"
#include "cvfRay.h"
#include "cvfFont.h"
#include "cvfGlyph.h"
#include "cvfCamera.h"
#include "cvfShaderProgram.h"
#include "cvfViewport.h"
#include "cvfGeometryUtils.h"
#include "cvfMatrixState.h"
2012-10-02 10:17:52 +02:00
#include "cvfRenderStatePoint.h"
#include "cvfRenderStateDepth.h"
#include "cvfRenderStatePolygonOffset.h"
#include "cvfRenderStateBlending.h"
2012-05-18 09:45:23 +02:00
#ifndef CVF_OPENGL_ES
#include "cvfRenderState_FF.h"
#endif
namespace cvf {
//==================================================================================================
///
/// \class cvf::DrawableText
/// \ingroup Render
///
/// DrawableText implements drawing of text strings in 2D space.
///
//==================================================================================================
//--------------------------------------------------------------------------------------------------
/// Constructor
//--------------------------------------------------------------------------------------------------
DrawableText :: DrawableText ()
: m_verticalAlignment ( TextDrawer :: BASELINE ),
m_textColor ( Color3 :: BLACK ),
m_backgroundColor ( 1.0f , 1.0f , 0.8f ),
m_borderColor ( Color3 :: BLACK ),
m_drawBackground ( true ),
m_drawBorder ( true ),
2013-02-28 11:37:32 +01:00
m_checkPosVisible ( true ),
m_useDepthBuffer ( false )
2012-05-18 09:45:23 +02:00
{
}
//--------------------------------------------------------------------------------------------------
/// Destructor
//--------------------------------------------------------------------------------------------------
DrawableText ::~ DrawableText ()
{
}
//--------------------------------------------------------------------------------------------------
/// Set font to be used for drawing text
//--------------------------------------------------------------------------------------------------
void DrawableText :: setFont ( Font * font )
{
m_font = font ;
}
//--------------------------------------------------------------------------------------------------
/// Get font used to draw text
//--------------------------------------------------------------------------------------------------
ref < Font > DrawableText :: font () const
{
return m_font ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: setVerticalAlignment ( TextDrawer :: Alignment alignment )
{
m_verticalAlignment = alignment ;
}
//--------------------------------------------------------------------------------------------------
/// Set color of the text to be rendered
//--------------------------------------------------------------------------------------------------
void DrawableText :: setTextColor ( const Color3f & color )
{
m_textColor = color ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: setBackgroundColor ( const Color3f & color )
{
m_backgroundColor = color ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: setBorderColor ( const Color3f & color )
{
m_borderColor = color ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: setDrawBackground ( bool drawBackground )
{
m_drawBackground = drawBackground ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: setDrawBorder ( bool drawBorder )
{
m_drawBorder = drawBorder ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: setCheckPosVisible ( bool checkpos )
{
m_checkPosVisible = checkpos ;
}
2013-02-28 11:37:32 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: setUseDepthBuffer ( bool useDepthBuffer )
{
m_useDepthBuffer = useDepthBuffer ;
}
2012-05-18 09:45:23 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: addText ( const String & text , const Vec3f & position )
{
m_positions . push_back ( position );
m_texts . push_back ( text );
m_boundingBox . add ( position );
}
//--------------------------------------------------------------------------------------------------
/// Main shader based rendering path for the geometry
//--------------------------------------------------------------------------------------------------
void DrawableText :: render ( OpenGLContext * oglContext , ShaderProgram * shaderProgram , const MatrixState & matrixState )
{
renderText ( oglContext , shaderProgram , matrixState );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: renderSoftware ( OpenGLContext * oglContext , const MatrixState & matrixState )
{
renderText ( oglContext , NULL , matrixState );
}
//--------------------------------------------------------------------------------------------------
/// Get total number of vertices for all texts
//--------------------------------------------------------------------------------------------------
size_t DrawableText :: vertexCount () const
{
return faceCount () * 4 ; // Four vertices per face/quad
}
//--------------------------------------------------------------------------------------------------
/// Get total number of triangles for all texts
//--------------------------------------------------------------------------------------------------
size_t DrawableText :: triangleCount () const
{
size_t numCharacters = 0 ;
size_t numTexts = m_texts . size ();
size_t i ;
for ( i = 0 ; i < numTexts ; i ++ )
{
numCharacters += m_texts [ i ]. size ();
}
return faceCount () * 2 ; // Two triangles per face/quad
}
//--------------------------------------------------------------------------------------------------
/// Get total number of faces for all texts
//--------------------------------------------------------------------------------------------------
size_t DrawableText :: faceCount () const
{
size_t numCharacters = 0 ;
size_t numTexts = m_texts . size ();
size_t i ;
for ( i = 0 ; i < numTexts ; i ++ )
{
numCharacters += m_texts [ i ]. size ();
}
return numCharacters ; // One face/quad per character/glyph
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
BoundingBox DrawableText :: boundingBox () const
{
return m_boundingBox ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool DrawableText :: rayIntersectCreateDetail ( const Ray & ray , Vec3d * intersectionPoint , ref < HitDetail >* hitDetail ) const
{
CVF_UNUSED ( ray );
CVF_UNUSED ( intersectionPoint );
CVF_UNUSED ( hitDetail );
return false ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void DrawableText :: renderText ( OpenGLContext * oglContext , ShaderProgram * shaderProgram , const MatrixState & matrixState )
{
CVF_ASSERT ( m_font . notNull ());
CVF_ASSERT ( ! m_font -> isEmpty ());
CVF_ASSERT ( oglContext );
CVF_ASSERT ( ! shaderProgram || ShaderProgram :: supportedOpenGL ( oglContext ));
if ( m_texts . size () == 0 ) return ;
CVF_ASSERT ( m_positions . size () == m_texts . size ());
if ( m_checkPosVisible )
{
if ( shaderProgram )
{
ref < ShaderProgram > nudgeShader = oglContext -> resourceManager () -> getLinkedNudgeShaderProgram ( oglContext );
nudgeShader -> useProgram ( oglContext );
nudgeShader -> clearUniformApplyTracking ();
nudgeShader -> applyFixedUniforms ( oglContext , matrixState );
2012-10-02 10:17:52 +02:00
RenderStatePoint point ( RenderStatePoint :: PROGRAM_SIZE );
2012-05-18 09:45:23 +02:00
point . applyOpenGL ( oglContext );
}
else
{
if ( ShaderProgram :: supportedOpenGL ( oglContext ))
{
ShaderProgram :: useNoProgram ( oglContext );
}
#ifndef CVF_OPENGL_ES
2012-10-02 10:17:52 +02:00
RenderStateMaterial_FF mat ;
2012-05-18 09:45:23 +02:00
mat . enableColorMaterial ( true );
2012-10-02 10:17:52 +02:00
RenderStateLighting_FF noLight ( false );
2012-05-18 09:45:23 +02:00
noLight . applyOpenGL ( oglContext );
#endif
}
2012-10-02 10:17:52 +02:00
RenderStateDepth visibleCheckDepthRS ( true , RenderStateDepth :: LEQUAL , false );
2012-05-18 09:45:23 +02:00
visibleCheckDepthRS . applyOpenGL ( oglContext );
2012-10-02 10:17:52 +02:00
RenderStatePolygonOffset po ;
2012-05-18 09:45:23 +02:00
po . enablePointMode ( true );
po . setFactor ( - 3.0f );
po . setUnits ( - 3.0f );
po . applyOpenGL ( oglContext );
2012-10-02 10:17:52 +02:00
RenderStateBlending addBlend ;
2012-05-18 09:45:23 +02:00
addBlend . enableBlending ( true );
2012-10-02 10:17:52 +02:00
addBlend . setFunction ( RenderStateBlending :: ONE , RenderStateBlending :: ONE );
addBlend . setEquation ( RenderStateBlending :: FUNC_ADD );
2012-05-18 09:45:23 +02:00
addBlend . applyOpenGL ( oglContext );
}
Mat4d modelViewProjectionMatrix = Mat4d ( matrixState . modelViewProjectionMatrix ());
std :: vector < Vec3f > projCoords ;
std :: vector < String > textsToDraw ; // Text strings to be drawn
size_t pos ;
for ( pos = 0 ; pos < m_positions . size (); pos ++ )
{
Vec3d proj ;
GeometryUtils :: project ( modelViewProjectionMatrix , matrixState . viewportPosition (), matrixState . viewportSize (), Vec3d ( m_positions [ pos ]), & proj );
if ( ! m_checkPosVisible || labelAnchorVisible ( oglContext , proj , m_positions [ pos ], shaderProgram == NULL ))
{
// Note: Need to adjust for the current viewport, as the coords returned from project are in global windows coordinates
2013-02-28 11:37:32 +01:00
projCoords . push_back ( Vec3f ( static_cast < float > ( proj . x () - matrixState . viewportPosition (). x ()), static_cast < float > ( proj . y () - matrixState . viewportPosition (). y ()), static_cast < float > ( 1.0 - 2.0 * proj . z ()))); // Map z into 1 .. -1
2012-05-18 09:45:23 +02:00
textsToDraw . push_back ( m_texts [ pos ]);
}
}
if ( m_checkPosVisible )
{
2012-10-02 10:17:52 +02:00
RenderStatePolygonOffset resetPO ;
2012-05-18 09:45:23 +02:00
resetPO . applyOpenGL ( oglContext );
2012-10-02 10:17:52 +02:00
RenderStateBlending resetBlend ;
2012-05-18 09:45:23 +02:00
resetBlend . applyOpenGL ( oglContext );
#ifndef CVF_OPENGL_ES
if ( ! shaderProgram )
{
2012-10-02 10:17:52 +02:00
RenderStateMaterial_FF mat ;
2012-05-18 09:45:23 +02:00
mat . applyOpenGL ( oglContext );
2012-10-02 10:17:52 +02:00
RenderStateLighting_FF light ;
2012-05-18 09:45:23 +02:00
light . applyOpenGL ( oglContext );
}
#endif
}
TextDrawer drawer ( m_font . p ());
drawer . setVerticalAlignment ( m_verticalAlignment );
drawer . setTextColor ( m_textColor );
drawer . setBackgroundColor ( m_backgroundColor );
drawer . setBorderColor ( m_borderColor );
drawer . setDrawBorder ( m_drawBorder );
drawer . setDrawBackground ( m_drawBackground );
2013-02-28 11:37:32 +01:00
drawer . setUseDepthBuffer ( m_useDepthBuffer );
2012-05-18 09:45:23 +02:00
size_t i ;
for ( i = 0 ; i < textsToDraw . size (); i ++ )
{
Vec3f pos = projCoords [ i ];
2013-02-28 11:37:32 +01:00
drawer . addText ( textsToDraw [ i ], pos );
2012-05-18 09:45:23 +02:00
}
if ( shaderProgram )
{
drawer . render ( oglContext , matrixState );
// Keep current program in use when we exit
shaderProgram -> useProgram ( oglContext );
}
else
{
drawer . renderSoftware ( oglContext , matrixState );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool DrawableText :: labelAnchorVisible ( OpenGLContext * oglContext , const Vec3d winCoord , const Vec3f & worldCoord , bool softwareRendering ) const
{
CVF_CALLSITE_OPENGL ( oglContext );
CVF_UNUSED ( worldCoord );
Vec2i coord ( static_cast < GLint > ( winCoord . x ()), static_cast < GLint > ( winCoord . y ()));
// Read out current z buffer contents
GLubyte bufferBefore [ 9 * 4 ];
glReadPixels ( coord . x () - 1 , coord . y () - 1 , 3 , 3 , GL_RGBA , GL_UNSIGNED_BYTE , bufferBefore );
if ( softwareRendering )
{
#ifndef CVF_OPENGL_ES
glPointSize ( 3 );
glColor3f ( 0.02f , 0.02f , 0.02f );
glBegin ( GL_POINTS );
glVertex3fv ( worldCoord . ptr ());
glEnd ();
#endif
}
else
{
glEnableVertexAttribArray ( ShaderProgram :: VERTEX );
glVertexAttribPointer ( ShaderProgram :: VERTEX , 3 , GL_FLOAT , GL_FALSE , 0 , worldCoord . ptr ());
glDrawArrays ( GL_POINTS , 0 , 1 );
}
// Now compare with previous z buffer contents to see if marker is visible
GLubyte bufferAfter [ 9 * 4 ];
glReadPixels ( coord . x () - 1 , coord . y () - 1 , 3 , 3 , GL_RGBA , GL_UNSIGNED_BYTE , bufferAfter );
size_t j ;
for ( j = 0 ; j < 9 * 4 ; j ++ )
{
if ( bufferBefore [ j ] != bufferAfter [ j ]) return true ;
}
return false ;
}
2013-02-28 11:37:32 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool DrawableText :: rayIntersect ( const Ray & ray , const Camera & camera , Vec3d * intersectionPoint )
{
Vec3d pickCoord2d ;
camera . project ( ray . origin (), & pickCoord2d );
size_t i ;
for ( i = 0 ; i < m_positions . size (); i ++ )
{
Vec3d proj ;
camera . project ( Vec3d ( m_positions [ i ]), & proj );
Vec3f posTextDrawer = Vec3f ( static_cast < float > ( proj . x () - camera . viewport () -> x ()), static_cast < float > ( proj . y () - camera . viewport () -> y ()), static_cast < float > ( 1.0 - 2.0 * proj . z ())); // Map z into 1 .. -1
if ( TextDrawer :: pickText ( Vec3f ( pickCoord2d ), m_texts [ i ], posTextDrawer , m_font . p ()))
{
if ( m_useDepthBuffer )
{
* intersectionPoint = Vec3d ( m_positions [ i ]);
}
else
{
* intersectionPoint = ray . origin ();
}
return true ;
}
}
return false ;
}
2012-05-18 09:45:23 +02:00
} // namespace cvf