PolychromeSurface.cpp

Display a triangular surface with a color field. Builds on MonochromeSurface.cpp .

Colors need to be specified as an array of rgb_float_t data for each vertex. If no such field is defined, then this surface module creates something artificial, which maps each vertex into its own specific color.

#include <ocean/plankton/VCreator.hpp>

#include <ocean/GLvish/VGLRenderObject.hpp>
#include <ocean/GLvish/BoundingBox.hpp>
#include <ocean/GLvish/ArrayTypes.hpp>
#include <ocean/GLvish/colors.hpp>

#include <ocean/shrimp/VObjectStatus.hpp>

#include <ocean/eagle/PhysicalSpace.hpp>

#include <field/Cell.hpp>
#include <GL/fiberGL.hpp>

#include <baseop/ExpandBBox.hpp>
#include <eye/retina/VSkeletonRenderObject.hpp>
#include <grid/types/TriangularSurface.hpp>
#include <bundle/BundleProperty.hpp>
#include <bone/GridActor.hpp>


#include <bone/FishField.hpp>

using namespace Wizt;
using namespace Fiber;
using namespace Eagle;

namespace
{


/*
  Render a triangular surface transparently.
 */
class   PolychromeSurface : public VSkeletonRenderObject
{
public:

        struct MyState : State, TriangularSurface
        {
                string  ColorFieldName;

                using TriangularSurface::operator=;
        };
        
        override RefPtr<State> newState() const
        {
                return new MyState();
        }

        TypedSlot<Field>        SurfaceColorField;

        TypedSlot<rgba_float_t> SurfaceColor;
        TypedSlot<double>       ScaleFactor;

        PolychromeSurface(const string&name, int p, const RefPtr<VCreationPreferences>&VP)
        : VGLRenderObject(name, p, VP)
        , Fish<VObject>(this)
        , VSkeletonRenderObject(name, p, VP)
        , SurfaceColorField(this, "colorfield" )
        , SurfaceColor(this, "color", makeColor(.8,.5,.1, 1.0),0)
        , ScaleFactor(this, "colorization", 0.0, 1)
        {}

        override void render(VGLRenderContext&Context) const;
        override bool update(VRequest&R, double precision);

static string createChildname(const string&parent_name)
        {
                return "PolychromeSurface(" + parent_name + ")";
        }

};


bool PolychromeSurface::update(VRequest&Context, double precision)
{
RefPtr<MyState> S = myState(Context);

RefPtr<Grid>  G = findMostRecentGrid( Context );
        if (!G)
        {
                removeState(Context);
                return setStatusError(Context, "No Grid found.");
        } 

        /*
          Assign grid pointer to TriangularSurface, which investigates
          the Grid for properties conforming to a TriangularSurface.
          If the Grid is not a TriangularSurface, then the following
          status check witll return false.
         */
        *S = G;

        if (!*S)
                return setStatusError(Context, "No surface available."); 

FieldSelector FS; 
        SurfaceColorField << Context >> FS; 
        S->ColorFieldName = FS();

        setBoundingBall(Context, getBoundingBox( S->CoordField ) );

        return setStatusInfo(Context, "Surface ready to render.");
}

struct  TriangleRenderer : VBO::Renderer
{
        RefPtr<TriangularSurface::CellArray_t>  Cells;
        
        TriangleRenderer(const RefPtr<TriangularSurface::CellArray_t>&TriangleCells)
        : Cells(TriangleCells)
        {}

        override void prefix()
        {
                glEnable(GL_NORMALIZE); 
                glDisable( GL_BLEND );
        }

        override bool draw() 
        {
        MultiArray<1, TriangleCell> Triangles = *Cells;
                GL::DrawElements( Triangles ); 
                return true;
        }

};

void PolychromeSurface::render(VGLRenderContext&Context) const
{
RefPtr<MyState> myState = getState(Context); 
        if (!myState)
                return;

TriangularSurface&Surface = *myState; 
        if (!Surface)
                return;

        if (!Surface.CoordField)
        {
                printf("Did not find coordinates :( \n");
                return;
        } 

        if (!Surface.CellField)
        {
                printf("Did not find cells :(  %s\n",
                       Typename( Surface.CellField->getFieldStorageType() ).c_str() ); 
                return;
        } 
/*
        if (Surface.CellField->getData()->nElements() < 1)
        {
                puts("PolychromeSurface: No triangles.");
                return;
        }
*/


// 
// Generic OpenGL section, these settings affect all VBO's rendered later 
//
        glEnable(GL_LIGHTING);

        glMaterialf( GL_FRONT, GL_SHININESS,  3 );
        glMaterialf( GL_BACK , GL_SHININESS, 50 ); 

        glMaterial( GL::FRONT_AND_BACK, GL::AMBIENT , makeColor( 0,0,.01,0) );
        glMaterial( GL::FRONT_AND_BACK, GL::SPECULAR, makeColor( 0.5,0.5,.53, 0) );
        glMaterial( GL::FRONT_AND_BACK, GL::EMISSION, makeColor( 0., 0.,0., 0) );
        glColor4f(1,0,0, 0.5); 

        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); 
        glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE);
        glEnable( GL_COLOR_MATERIAL );

rgba_float_t    SurfColor( makeColor(1,0,0,0.5 ) );
        SurfaceColor << Context >> SurfColor; 



//      glEnable( GL_DEPTH_TEST ); 
        //       glEnable( GL_MULTISAMPLE_ARB ); 
        // glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB );

        glColor( SurfColor ); 
//      glColor4f( SurfColor[0],SurfColor[1],SurfColor[2],SurfColor[3]);


//      printf(">>>>>   PolychromeSurface: RENDER cells for t=%ld, %lu Triangles\n", 
//             myState->CellField->time_value(), Cells->nElements() );

double  colorscale = 1.0;
        ScaleFactor << Context >> colorscale;


// 
// Vertex Buffer Object (VBO) Access via GLCache,
// retrieve and re-use or generate new VBO 
// 
// The caching mechanism is part of the "Visualization Cascade" and 
// described in the article: 
//
// http://sciviz.cct.lsu.edu/papers/2009/VizCascade.pdf
// 

string VBOKey = "";     // This is an additional parameter for the OpenGL Cache. 
                        // It can be an arbitrary string, and this would be the 
                        // right place to use the name of a field fragment when 
                        // support for fragmented surfaces is added. For now, 
                        // such is not supported, and we can keep this key empty. 

RefPtr<ValueSet> V;     // This is a set of values for each of which there should 
                        // be one OpenGL cache entry generated. This means that if 
                        // a parameter is changed to a value that it already had 
                        // in the past, then the VBO that is associated with this 
                        // value will be re-used instead of newly generated. 
                        // Be *very* cautious about what values to use here, 
                        // since every cache entry eats up valueable memory at 
                        // the GPU. See documentation for class ValueSet on 
                        // how to assign values here. 

        // 
        // Check for a named color field. If none is found, then we 
        // use a field called "Colors".
        //
RefPtr<Field> Colors = (*Surface.CartesianVertices)( myState->ColorFieldName ); 
        if (Colors)
                VBOKey += myState->ColorFieldName ;
        else
                Colors = (*Surface.CartesianVertices)( "Colors" );

RefPtr<VBO> myVBO;
        try
        {
                // n-dimensional readonly cache indexing (see VizCascade paper)
                myVBO = Context( *Surface )( typeid(*this) )( V )(VERTEXBUFFER(), VBOKey);

                //
                // Call the VBO if it's there and all ready to go. 
                //
                if (myVBO
                    && !myVBO->isOlderThan( *Surface.CellField)
                    && !myVBO->isOlderThan( *Surface.CoordField) 
                    && (Colors && !myVBO->isOlderThan( *Colors) ) )
                {
                        if (RefPtr<TriangleRenderer> TR = myVBO->getRenderer() )
                        {
                                if (myVBO->call() )
                                {
                                        return;
                                }
                        }
                } 
                //  Cellfield is newer than surface view VBO, need to re-load VBO
        }
        catch(const GLCacheError&Err)
        {} 

        //
        // Create new VBO if it doesn't exist yet.
        //
        if (!myVBO)
                myVBO = Context[*Surface][ typeid(*this) ][ V ]( VERTEXBUFFER(), VBOKey); 

        assert( myVBO );
        myVBO->clear();

// 
// Loading fields as vertex arrays and append them to the VBO. 
// 
// Vertex arrays are loaded by appending objects that have been 
// derived from class BufferArray. 
//

using namespace Eagle::PhysicalSpace; 

        // Note: this approach does not support fragmented surfaces.

        myVBO->append( new TypedVertexArray<point3>( myState->getCoords()->myChunk() ) ); 

        if (RefPtr<TriangularSurface::NormalVectorArray_t> VertexNormals = Surface.getNormals() )
        {
                myVBO->append( new TypedNormalArray<bivector3>( VertexNormals->myChunk() ));
        } 


        if (Colors)
        {
                if (RefPtr<TypedArray<rgba_float_t> > colordata = Colors->getData() )
                {
                        myVBO->append( ColorArray::newArray( colordata->myChunk() ));
                }
        } 
        else // no color field found, set something artifical here
        {
        index_t nVertices = Surface.CartesianVertices->getPositions()->getData()->nElements();

        MemVector<rgba_float_t> SurfaceColors(nVertices); 

                for(index_t i=0; i<nVertices; i++)
                {
                // a floating point index, which ranges from 0.0 to 1.0
                double  fi = i/(nVertices-1.0); 

                // some simple and stupid, but colorful formula 
                // to map an index to colors.
                        SurfaceColors[i] = 1-fi, 1-fi, fi, 1.0;
                }

        RefPtr<TypedChunk<rgba_float_t> > colordata = SurfaceColors;
                myVBO->append( ColorArray::newArray( colordata ) );
        }

        myVBO->setRenderer( new TriangleRenderer(myState->CellField->getData() ) );
        myVBO->call();
}


//
// Object Creation
//

struct  GridInspector
{
static  SkeletonExistence InspectionProperty()
        {
                return SkeletonExistence( TriangularSurface::ID() );
        }
};

static  Ref<GridActor<GridInspector, PolychromeSurface> > 
        MyPolychromeSurfaceCreator("Display/PolychromeSurface",  ObjectQuality::DEMO);

} // anon namespace