ColoredLines.cpp

Display a set of lines as specified by the Edge skeleton of a grid. Refinement levels and fragmented fields not implemented.

#include <ocean/plankton/VCreator.hpp>
#include <ocean/plankton/VPipeline.hpp>


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

#include <ocean/eagle/PhysicalSpace.hpp>

#include <ocean/shrimp/VEnum.hpp>
#include <ocean/shrimp/TimeDependent.hpp>
#include <ocean/shrimp/VObjectStatus.hpp>

#include <ocean/shrimp/EaglePhysicalSpaceTVector.hpp>

#include <GL/FieldBuffer.hpp>
#include <GL/LineSetRenderer.hpp>

#include <bone/GridActor.hpp>
#include <bone/GridObject.hpp>
#include <bone/FishField.hpp>

#include <bundle/BundleProperty.hpp>
#include <grid/types/LineSet.hpp>

#include <baseop/ExpandBBox.hpp>
#include <fiberop/Range.hpp>

#include <field/ArrayRef.hpp>

#include <memcore/Chunk.hpp>

//#define VERBOSE

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

namespace
{

class   RampTexture : public GLTexture1D
{
        typedef FixedArray<float,3> rgb;
        

        rgba_float_t color1;
        rgba_float_t color2;

public: 
        RampTexture(const int tex_unit, const rgba_float_t&c1, const rgba_float_t&c2)
        : GLTexture1D(tex_unit)
        , color1(c1)
        , color2(c2)    
        {}

        void enlighten()
        {
        rgb     texture[512];
        rgb     col;
        double  p;

                for(int i = 0; i < 512 ; i++)
                {
                        p = (double)i/512;
                        col[0] = (1-p)*color1[0] + p*color2[0];
                        col[1] = (1-p)*color1[1] + p*color2[1];
                                col[2] = (1-p)*color1[2] + p*color2[2];
                                texture[i] = col;
                }

                bind();
                GL::TexImage1D( texture, 512, GL_RGB);
                glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
                glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        }
        
        void prerender()
        {
                enable(); 
                //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //not really nesseccasy ? 
                bind();  // should do corresponding glActiveTexture(GL_TEXTURE1);  ?? automatically ??
        }
        
};


class   GlossColorMapLines : public virtual VGLRenderObject, public TimeDependent
{

        struct  FieldState : State
        {
                WeakPtr<Grid>      theGridOfInterest;
                vector<RefPtr<FragmentID> > fragments;
        };

        override RefPtr<State> newState() const
        {
                return new FieldState();
        }


        struct  FragIt : FieldFragmentIterator
        {
                RefPtr<FieldState> S;

                FragIt( RefPtr<FieldState> & SP )
                : S( SP )
                {
                        S->fragments.clear();
                }

                bool apply(const RefPtr<FragmentID>&f,
                           const MemCore::StrongPtr<Fiber::CreativeArrayBase>&DC )
                {
                        if(!f)
                                return false; 

                        S->fragments.push_back( f ); 
                        return true;
                }
        } ; 
        

public:
        GlossyTexture::Parameters  GlossyParameters;

        TypedSlot<Grid>         MyLinesGrid;
        TypedSlot<Field>        InputField;

        TypedSlot<double>       LineWidth;
        TypedSlot<rgba_float_t> ColStart, ColEnd;

//      TypedSlot<Enum>         AutoLenScale;
        TypedSlot<Enum>         GhostyLines;

        TypedSlot<Range>        InputRange;
        TypedSlot<VColormap>    Colormap;

//      VOutput<Range>          LengthRange;

        enum    { NumberOfInputFields = 1 };
//      typedef LIST<double, LIST<float> > InputTypes;
        typedef LIST<double>            InputTypes;


        GlossColorMapLines(const string&name, int p, const RefPtr<VCreationPreferences>&VP)
        : VGLRenderObject(name, TRANSPARENT_OBJECT-10, VP)      
        , TimeDependent(this)
        , GlossyParameters(this)
        , MyLinesGrid(this, "grid", GridSelector(), 2)
        , InputField(this, "field" )
        , LineWidth(this, "linewidth", 2.0, 1)
        , ColStart(this, "col_start", makeColor(0.0, 1.0, 0.0, 1.0),2)
        , ColEnd(this, "col_end", makeColor(1.0, 0.0, 0.0, 1.0),2)
//      , AutoLenScale(this, "autoscale", Enum(1,"on", "off"), 0)
        , GhostyLines(this, "ghostylines", Enum("off", "on"),3 )
        , InputRange(this, "range", Range(0,1), 0)
        , Colormap(this, "colormap", NullPtr() )
//      , LengthRange( self(), "fieldrange", Range(0.0, 1.0) )
        {
                LineWidth.setProperty( "max", 20.0);
        }

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

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

//
// Update function, is called whenever some input is changed
// It extracts the fields required for rendering and deposits them
// in the context-relative state object. This state object is
// available then during rendering.
//
bool GlossColorMapLines::update(VRequest&Context, double precision)
{
/*
        {
        VEnum   als; 
                AutoLenScale << Context >> als; 
                if (als()==0) setExpertLevel(InputRange, 10); 
                else          setExpertLevel(InputRange, 0);
        }
*/

FieldSelector FS;
        InputField << Context >> FS;

Info<Skeleton> SI;
        {
        GridSelector    GS; 
                MyLinesGrid << Context >> GS; 
                SI = GS.getRefinementLevel( getTime(Context), 0, 0 );
        }
double time = SI.getTime();

        if (!SI.getGrid() )
        {
                SI = FS.getRefinementLevel( getTime(Context), 0, 0 ); 
                time = SI.getTime();
                if (!SI.getGrid() )
                        return setStatusError(Context,"No grid object found at T=" + String(time) ); 
        }

        setBoundingBall( Context, getBoundingBox( *SI.getGrid() ) );

RefPtr<FieldState> S = myState(Context);

LineSet LS = SI.getGrid();
        if (!LS)
        {
                return setStatusError(Context,"No line grid found at T=" + String(time) ); 
        }
        if (!LS.CartesianVertices)
        {
                return setStatusError(Context,"No grid without cartesian vertices at T=" + String(time) ); 
        }
        S->theGridOfInterest = SI.getGrid();

        LS.setupStandardFields( SI.getGrid() ); 

// collect fragment IDs in state
FragIt myIt(S);

        if( LS.LineIndices )
                LS.LineIndices->iterate(myIt); 
        else
                setStatusError(Context,"No LineIndices Field fount at T=" + String(time) ); 

#ifdef VERBOSE
        for(size_t i = 0; i < S->fragments.size(); i++ )
                cout << "Found LineSet-Fragment: " << S->fragments[i]->Name() << endl; 
#endif

// if no fragments push one NullPtr on the vector, so the for loop in the rendering 
// can be used for unfragmented linesets as well
        if( S->fragments.empty() )
                S->fragments.push_back( NullPtr() );    

//RefPtr<Field> F = (*LS.CartesianVertices)( FS.getFieldName() ); 

//      if (!F) F = (*LS.CartesianVertices)( LineSet::ArcLengthFieldName ); 

//      if (F)
        {
/*
                if (RefPtr<DataRange<double> > DR = getRange(*F) )
                {
                Range   FieldRange(DR->Min(), DR->Max() ); 
                        LengthRange << Context << FieldRange;
                }
*/
//              S->TextureField = F; 
//              S->Fieldname = FS.getFieldName(); 

                return setStatusInfo(Context, "Lines ready, inspecting " + FS.getFieldName() ); 
        } 
//      else
        {
//              S->TextureField = NullPtr(); 
//              return setStatusInfo(Context, "Lines ready, no field available.");
        }
}


static
RefPtr<MemBase> ApplyRange(const Range&R, const RefPtr<MemArray<1, double> >&Data)
{
const MultiArray<1, double>&Input = *Data;

ArrayRef<double> Output(Input.Size() ); 

        for(index_t i=0; i<Output.size(); i++)
        {
                Output[i] = R(Input[ i ]);
        }

        return Output;
}


/*
  The renderer object for the VBO. It uses the information
  about line segments to issue glDrawElements() commands.

  It is implemented as a callback iterator object for
  LineSet's.

  @todo Investigate this construction whether it not creates a
        circular reference keeping the involved Grid object
        alive by itself. Probably it does not, but that has
        to be verified by investing some more brain power.
        Update: yes it did. Must not derive from LineSet here.
        This is an absolute NO.
        Previous code die
        @code
        struct  MyRenderer : DrawArrays, LineSet
        {
        };
        @endcode
        which was a very bad idea...
 */


/*
  The render routine. Takes care of vertex buffer objects
  and calls its render routines.
 */
void GlossColorMapLines::render(VGLRenderContext&Context) const
{
//      puts("GlossColorMapLines::render()");fflush(stdout);

RefPtr<FieldState> S = getState(Context);
        if (!S) return; 

        if (!S->theGridOfInterest)
                return; 

LineSet LS(S->theGridOfInterest);

        if (!LS.LineIndices)
                return; 


FieldSelector FS, FS_off; 
        InputField << Context >> FS; 
string  Fieldname = FS.getFieldName(); 

        //
        // Setup glossy texture on unit 0 
        //
        glActiveTexture(GL_TEXTURE0);
RefPtr<GlossyTexture> LineTexture;
        {
        RefPtr<ValueSet> GlossyValues = new ValueSet();
                try
                {
                        LineTexture = Context( *S )( this ) ( GlossyValues ) ( TEXTURE() ); 
                }
                catch( const GLError&)
                {} 

                if (!LineTexture)
                {
                        LineTexture = new GlossyTexture(0); 
                        LineTexture->enlighten();
                        LineTexture->enlighten(GlossyParameters, Context); 
                        GLCHECK( LineTexture->enlighten ); 

                        Context[ *S ][ this ][ GlossyValues ] ( TEXTURE() ) = LineTexture; 
                }
        } 

        //
        // Setup coloration texture on unit 1
        // 
        {
RefPtr<ValueSet> CmapValues = new ValueSet(Colormap(Context) );
TextureCreator&TC = Context[ *LS.LineIndices ][ this ][ CmapValues ]( TEXTURE() ); 

RefPtr<RampTexture> LineTexture2;

VColormap Cmap; 
        Colormap << Context >> Cmap;
        if (Cmap)
        {
//      RefPtr<GLTexture1D> CmapTexture =
                Cmap->Enable(TC, 1, true); 
        }
        else
        {
        rgba_float_t c1, c2;
                ColStart << Context >> c1;
                ColEnd   << Context >> c2;

                LineTexture2 = new RampTexture(1, c1, c2); 
                LineTexture2->enlighten();
                LineTexture2->prerender();
        } 
        }

        //
        // 
        // OpenGL prefix 
        // 
        //
double  width = 1.0;
        LineWidth << Context >> width; 
        if( width < 1e-4)
                width = 0.01; 

        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_POINT_SMOOTH);
        glDisable(GL_LIGHTING);

        glEnable( GL_DEPTH_TEST );
        glLineWidth(width);

        // blending, experiment here
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
        glBlendFunc(GL_ONE, GL_ONE); 

Enum    GhostL;
        GhostyLines << Context >> GhostL; 

        if (GhostL("on") )
                glEnable( GL_BLEND ); 
        else
                glDisable( GL_BLEND ); 


        glEnable ( GL_COLOR_MATERIAL ); 

//Enum  Auto; 
//      AutoLenScale << Context >> Auto; 

        {
                glActiveTexture(GL_TEXTURE0);
                assert(LineTexture);
        GlossyTexture::Render LINERENDER( *LineTexture, Context.CameraSettings );
        Range   ValueRange(0.0, 1.0);

/*
                switch( Auto() )
                {
                case 1:
*/
                        InputRange  << Context >> ValueRange; 
/*
                        break;

                default:
                case 0:
                        LengthRange << Context >> ValueRange;
                }
*/
        const int ColormapTextureUnit = 1;
#if 0
                // 
                // Do scaling of colormap values via texture matrix on unit 1 
                // which is much faster than rescaling the input values of the 
                // provided scalar field.
                //

                glActiveTexture(GL_TEXTURE1);
                glMatrixMode(GL_TEXTURE); 

                glTexCoord4d(0.0, 0.0, 0.0, 1.0);

                /*
                  m * (t,0,0,0) = t( m[0] + m[1] + m[2] + m[3] )
                 */ 

/*
        double  m[16] = { ValueRange.inv_scale(), 0,0, ValueRange.inv_bias(),
                          0,1,0,0,
                          0,0,1,0,
                          0,0,0,1 };
*/ 

//      double  m[16] = { ValueRange.inv_scale(), 0,0, ValueRange.bias(),

        double  m[16] = { 1.0, 0,0, ValueRange.bias(),
                          0,1,0,0,
                          0,0,1,0,
                          0,0,0,1 }; 

                printf("TEXTURE MAPPING: inv[scale=%lg, bias=%lg]   [scale=%lg, bias=%lg], f(0)=%lg f(1)=%lg\n",
                       ValueRange.inv_scale(), ValueRange.inv_bias(),
                       ValueRange.scale(), ValueRange.bias(),
                       m[3], m[0] + m[3]
                        );

                glLoadMatrixd( m );
                glMatrixMode(GL_MODELVIEW);
#endif

        RefPtr<ValueSet> myCacheableValues = new ValueSet(); 
                // for each field make a new VBO
                *myCacheableValues <<= InputField(Context); 
//              printf("CACHE VALUE: [%s]\n", myCacheableValues->ValueState().c_str() ); 
//              WeakPtr<VValueBase> VB = InputField(Context);
//              printf("INPUT FIELD: [%s]\n", VB->Text().c_str() ); 

                // TODO sort by kdtree 
                for( size_t frag_i = 0; frag_i < S->fragments.size(); frag_i++  ) 
                {
                string  VBOKey =""; 

                        if( S->fragments[frag_i] )
                                VBOKey  = S->fragments[frag_i]->Name(); 
#ifdef VERBOSE
                        cout << "VBKey: " << VBOKey << endl;
#endif

                RefPtr<VBO> myVBO; 
                Intercube&CacheObject = *S->theGridOfInterest; 

                        try
                        {
                                myVBO = Context( CacheObject )( typeid(*this) )( myCacheableValues )( VERTEXBUFFER(), VBOKey ); 
                        }
                        catch(...){} // could investigate reasons here why cache is not available. 

                        RefPtr<Field> TextureField = (*LS.CartesianVertices)( Fieldname ); 

#if     1 
                        if (!myVBO || !myVBO->getRenderer() ||
                            myVBO->isOlderThan( ConnectionAge() ) ||
                            (LS.Coords && myVBO->isOlderThan( *LS.Coords) ) ||
                            (LS.LineIndices && myVBO->isOlderThan( *LS.LineIndices) ) ||
                            (TextureField && myVBO->isOlderThan( *TextureField) ) ||
                            InputRange.age( Context ).isNewerThan( *S ) ||
                            InputRange.age( Context ).isNewerThan( *myVBO ) )
                                
#else
                        if (true)
#endif
                        {
                        RefPtr<MemBase> Pts = LS.Coords->getData( S->fragments[frag_i] ); 

                        //std::vector<point> vertices = Pts; 
                        RefPtr<MemArray<1, point> > TA = Pts; 
                        if(!TA)
                        {
                                puts("No coords array retrieved"); 
                                return;
                        } 
                std::vector<point> VertexVector = TA->myChunk()->get_vector(); 

                        myVBO = Context[ CacheObject ][ typeid(*this) ][ myCacheableValues ] ( VERTEXBUFFER(), VBOKey ); 
                        myVBO->clear(); 

                        if (RefPtr<LineSet::TangentialVector_t> Tangents = LS.getTangentialVectors( S->fragments[frag_i] ) )
                        {
                                MultiIndex<1>mi; 
#ifdef  VERBOSE                         
                                for(mi[0] = 0; mi[0] < 3; mi[0]++)
                                        std::cout << (*Tangents)[mi] << std::endl;
#endif          
                                
                        const   int TextureUnit = 0;
                        RefPtr<TexCoordArray> TCA =
                                GL::FieldBuffer<TypedTexCoordArray<Eagle::tvector3> >::createParam( TextureUnit, Tangents, false, NullPtr() ); 
                                myVBO->append( TCA ); 
#ifdef  VERBOSE
                                cout << "ColoredLines::render() appended tangents: # " << TCA->BufferArray::NumberOfElements() << endl;
#endif          
                        }
                        else
                        {
                                LS.getLineset().Speak("********************LineSet EDGE Sets - WRONG EDGE SET DATA TYPE?"); 
                                return;
                        } 
#ifdef  VERBOSE
                        puts("Load a texture coordinate field, maybe?"); 
#endif
                        if (TextureField)
                        {
                                if (RefPtr<MemArray<1, double> > TextureData = TextureField->getData( S->fragments[frag_i] ) )
                                {
#ifdef  VERBOSE
                                        puts("Load a texture coordinate field"); 
#endif
                                RefPtr<MemArray<1, double> > LoadData = ApplyRange(~ValueRange, TextureData);

                                RefPtr<TexCoordArray> TCA =
                                                GL::FieldBuffer<TypedTexCoordArray<double> >::createParam( ColormapTextureUnit, 
                                                                                                           LoadData, false, NullPtr() ); 
                                        myVBO->append( TCA ); 

#ifdef  VERBOSE
                                        cout << "ColoredLines::render() appended texture: # " << TCA->BufferArray::NumberOfElements() << endl; 
#endif
                                } 
                                else
                                {
                                        puts("NOTE: CANNOT get texture field data?!");
                                }
                        } 
                        else
                        {
                                puts("NOTE: NO texture field!");
                        } 
                        RefPtr<TypedVertexArray<point> > VA = new TypedVertexArray<point>(); 
                        VA->load(VertexVector); 
                        myVBO->append( VA ); 
#ifdef  VERBOSE
                        cout << "ColoredLines::render() appended vertices: # " << VA->BufferArray::NumberOfElements() << endl; 
#endif
                        myVBO->setRenderer( new GL::LineSetRenderer( S->theGridOfInterest, GL::LineSetRenderer::AsLineStrip, S->fragments[frag_i] ) ); 

//#define VERBOSE
#ifdef  VERBOSE
                        puts("CREATING NEW LINE RENDER VBO"); 

                        if (TextureField && myVBO->isOlderThan( *TextureField) )
                        {
                                puts("  BECAUSE this VBO is older than the associated texture field"); 
                                TextureField.speak("ASSOC TEXTURE FIELD"); 
                                {
                                FieldSelector FS; 
                                        InputField << Context >> FS; 

                                        printf("    FIELDNAME: %s\n", FS.getFieldName().c_str() );
                                }
                        } 
                        else
                        {
                                puts("  BUT texture field has not changed");
                        }
#endif
                }
#ifdef  VERBOSE
                else 
                        puts("USING CACHED LINE RENDER VBO");
#endif 
#ifdef  VERBOSE
                puts("call1");fflush(stdout);
#endif
                myVBO->call(); 

                glActiveTexture(GL_TEXTURE1);
                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
                glMatrixMode(GL_MODELVIEW);
                } 
        }
}

//
// Object Creation
//

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

static  Ref<GridActor<GridInspector, GlossColorMapLines> > 
        MyGlossyLinesCreator( "Display/ColoredLines", ObjectQuality::MATURE);

static  Ref<TypedFieldInputCreator<GlossColorMapLines> >
        MyTransparentColoredSurfaceCreator("Display/OnColoredLines",  ObjectQuality::MATURE);


} // anon namespace