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