2008-12-21

Sexy background

If you want to add a special touch to your application, here are a couple of hints on ‘personalizing’ your 3D view.

*Adding an image as background*
Use V3d_View::SetBackgroundImage() that accepts a filename to gif, bmp or xwd image and a placement option (center, stretched or tiled) defined by the Aspect_FillMethod enumeration. Calling its with Aspect_FM_NONE erases the image.
Here’s an original image and its use with the Aspect_FM_STRETCH option:





You may want to extend pre-built options, and for instance add an image to a bottom-right corner to add your company logo.

* Gradient background *
This is a frequently used background style in CAD applications. Open CASCADE does not offer a direct API to set it, but it can be implemented. The following is an excerpt from my extension of DRAWEXE:

static void UpdateGradientBackground (const Handle(Visual3d_Layer)& theLayer,
const Quantity_Color& theTopColor,
const Quantity_Color& theBottomColor)
{
int aWidth = ..., aHeight = ...; //e.g. QWidget::width() and height() for Qt-based apps
theLayer->Clear(); //make sure we draw on a clean layer
theLayer->Begin();
theLayer->SetViewport (aWeight, aHeight);
//now draw a polygon using top and bottom colors
//remember that default boundary is [-1,1;-1,1] and origin is in the left bottom corner

//check position for the middle color - if transition should be non-uniform then
//additional points should be inserted and techiques changes - 2 polygons instead of 1
theLayer->BeginPolygon();
theLayer->SetColor (theTopColor);
theLayer->AddVertex (-1,1);
theLayer->AddVertex (1,1);

theLayer->SetColor (theBottomColor);
theLayer->AddVertex (1,-1);
theLayer->AddVertex (-1,-1);
theLayer->ClosePrimitive();
theLayer->End();
}

static int VSetBgColor (Draw_Interpretor& di, Standard_Integer argc, const char** argv)
{
Handle(V3d_View) V3dView = ViewerTest::CurrentView();
if ( V3dView.IsNull() ) return 1;

static Handle(Visual3d_Layer) aLayer;

if (argc == 4) {
if (!aLayer.IsNull()) {
//switch to a single color mode
aLayer->Destroy(); //explicit destruction is required as destructor
// will not be called (one reference remains in Visual3d_ViewManager)
aLayer.Nullify();
}
V3dView->SetBackgroundColor (Quantity_Color (atof(argv[1]), atof(argv[2]), atof(argv[3]), Quantity_TOC_RGB));
} else if (argc == 7) {
Quantity_Color aTopColor (atof(argv[1]), atof(argv[2]), atof(argv[3]), Quantity_TOC_RGB);
Quantity_Color aBottomColor (atof(argv[4]), atof(argv[5]), atof(argv[6]), Quantity_TOC_RGB);
if (aLayer.IsNull()) {
Standard_Boolean aSizeDependant = Standard_True; //each window to have particular mapped layer?
aLayer = new Visual3d_Layer (V3dView->Viewer()->Viewer(),
Aspect_TOL_UNDERLAY, aSizeDependant);
}
UpdateGradientBackground(aLayer, aTopColor, aBottomColor);
} else {
di << "Usage : " << argv[0] << " {color(R G B) | top_color(R G B) bottom_color(R G B)} \n";
return 1;
}

return 0;
}

Here’s an example view:


Please rate this post using the voting buttons below.

26 comments:

  1. It looks great!!

    I've been trying to use in my app and gradient background is not working for me (Maybe because of width and heigth?)
    Now I am using flat color.

    Ah! A little bug, that instruction relies on the weight, xD
    theLayer->SetViewport (aWeight, aHeight);

    ReplyDelete
  2. Hi Koala, not sure I got a comment about a bug. Could you elaborate ?

    ReplyDelete
  3. Svetlozar KostadinovDecember 23, 2008 at 3:27 AM

    Hi Roman, do you know what's the reason for flickering on each mouse move? The gradient is drawn rightm but it seems that underlayer and the middle layer (grey by default) are somehow rotated which causes nasty flickering. My viewer is created with these attributes:

    ReplyDelete
  4. Svetlozar KostadinovDecember 23, 2008 at 3:30 AM

    myViewer = new V3d_Viewer(
    theGraphicDevice,
    a3DName.ToExtString(),
    "",
    1000.0,
    V3d_XposYnegZpos,
    Quantity_NOC_GRAY30,
    V3d_ZBUFFER,
    V3d_GOURAUD,
    V3d_WAIT,
    Standard_True,
    Standard_False);

    myViewer->SetDefaultLights();
    myViewer->SetLightOn();
    myViewer->SetZBufferManagment(Standard_True);

    The View attributes are:
    myView->SetSurfaceDetail(V3d_TEX_ALL);
    myView->SetTransparency(Standard_True);
    myView->SetAntialiasingOff();

    ReplyDelete
  5. Hi Svetlozar, no immediate answer. On my laptop with the Radeon card flickering also appears with a flat background color, so it seems it's not a specific issue of gradient filling. I can be wrong but I can't remember flickering in the older versions of OCC on Nvidia. Can this be connected with a video card (e.g. the 3D performance meter crashes on my laptop when using text) ?
    If OCC folks read this and have a comment, we would be interested to know.

    ReplyDelete
  6. Don't worry. It is only a typing error. You confused aWeight by aWidth.

    Despite of that, I do not get working even with Svetlozar settings. Is required a specific version of OCC (Using 6.2.0)?

    ReplyDelete
  7. I simplified your codes easy for newer

    void CMyoccView::OnBackground()
    {
    static Handle(Visual3d_Layer) aLayer;
    Quantity_TOC_RGB);
    Quantity_Color aTopColor (1, 1, 1, Quantity_TOC_RGB);
    Quantity_Color aBottomColor (1, 0, 1, Quantity_TOC_RGB);
    Standard_Boolean aSizeDependant = Standard_True;
    aLayer = new Visual3d_Layer (myView->Viewer()->Viewer(),
    Aspect_TOL_UNDERLAY, aSizeDependant);

    int aWidth =10, aHeight = 10;
    aLayer->Clear();
    aLayer->Begin();
    aLayer->SetViewport (aWidth, aHeight);

    aLayer->BeginPolygon();
    aLayer->SetColor (aTopColor);
    aLayer->AddVertex (-1,1);
    aLayer->AddVertex (1,1);

    aLayer->SetColor (aBottomColor);
    aLayer->AddVertex (1,-1);
    aLayer->AddVertex (-1,-1);
    aLayer->ClosePrimitive();
    aLayer->End();

    }

    ReplyDelete
  8. 2 Koala:
    Nope, I developed this code yet for 5.x and checked before posting on 6.3.0.

    2 Wangzan:
    Then you better store aLayer in a member field, not in a static variable. You can use it later if you want to switch to a flat color, for example.
    I had to use static var in DRAWEXE as it's rather a set of functions not a class.

    ReplyDelete
  9. Svetlozar KostadinovDecember 23, 2008 at 3:26 PM

    Roman, I am using laptop with Intel GMA 3100 video. I'll test the solution on a desktop machine with nVidia ASAP. As I remeber sometimes the flicker was with a flat color, but with other colors that I tested it was OK. However I use now a gradient strip bmp with 1 pixel width and Aspect_FM_TILED option. This gives the same result. Thank you!

    ReplyDelete
  10. I added the code fragment to my aplication generated with the mfc appwizard. But cannot see the gradient, only the default flat color.

    Sharjith N.

    ReplyDelete
  11. Hmmm, I just took MFC samples shipped with OCC and modified OCC_3dView::OnModifyChangeBackground() as follows:
    void OCC_3dView::OnModifyChangeBackground()
    {
    Standard_Real R1;
    Standard_Real G1;
    Standard_Real B1;
    myView->BackgroundColor(Quantity_TOC_RGB,R1,G1,B1);
    COLORREF m_clr ;
    m_clr = RGB(R1*255,G1*255,B1*255);

    CColorDialog dlgColor(m_clr);
    if (dlgColor.DoModal() == IDOK)
    {
    m_clr = dlgColor.GetColor();
    R1 = GetRValue(m_clr)/255.;
    G1 = GetGValue(m_clr)/255.;
    B1 = GetBValue(m_clr)/255.;
    //myView->SetBackgroundColor(Quantity_TOC_RGB,R1,G1,B1);
    if (myLayer.IsNull()) {
    Standard_Boolean aSizeDependant = Standard_True; //each window to have particular mapped layer?
    myLayer = new Visual3d_Layer (myView->Viewer()->Viewer(),
    Aspect_TOL_UNDERLAY, aSizeDependant);
    }
    UpdateGradientBackground(myLayer, Quantity_Color (R1, G1, B1, Quantity_TOC_RGB), Quantity_NOC_WHITE, myWidth, myHeight);

    }
    myView->Redraw();
    }
    and added myLayer and into the class (OCC_3dView).
    All worked just fine.

    ReplyDelete
  12. Svetlozar, N. Sharjith:

    Don't call this function in OnInitialUpdate. Call it later, after initialization finish and it will work!

    ReplyDelete
  13. Svetlozar KostadinovDecember 24, 2008 at 6:43 PM

    Koala, when to call if not in OnInitialUpdate()?

    ReplyDelete
  14. Yes, it just worked after I pasted the same fragment in the View3D OnInitialUpdate instead of the View's.
    Just after the base class View OnInitialUpdate is called from the View3D class. Great!
    Thanks..
    Wishing all a Merry Christmas!

    N. Sharjith

    ReplyDelete
  15. Pretty nice trick, Roman. Too bad you did not elaborate on how to set an image (like a logo) somewhere else in the view. "extend pre-build options" doesn't really say anything to me :(

    ReplyDelete
  16. Hi Ceniza,
    Well, by pre-built options I meant those available via the Aspect_FillMethod enumeration. So today, for example, you cannot add an image into a bottom-right corner. I encouraged to look into the OCC code and add more options (at least 4 - for corners). Hope this did not sound too frightening ;-).

    ReplyDelete
  17. I was afraid it would imply looking into the code, and that's exactly what you meant. Good thing it's not a priority for me to do such a thing.
    Thanks for your reply.

    ReplyDelete
  18. Hi,

    Thank you very much Roman !

    This is good working but if I activate a cut plane in my view, the background is also cut.

    Is it a bug ?

    Regards,

    Mathieu

    ReplyDelete
  19. MaT, have no clue upfront, sorry. Perhaps persistence of the background plan depends on the depth coordinate at which it is drawn. But I am not sure if this is a user-defined parameter. As such, this may sound like a limitation, not really as a bug...

    ReplyDelete
  20. Hi Roman,

    I tried your code on Linux machine. I modified the qt tutorial that comes with occ. I modified the onBackground() function and surprisingly, the app seg faults at myView->Redraw() function. My modified onBackground function is as follows :

    void View::onBackground()
    {
    QColor aColor ;
    Standard_Real R1;
    Standard_Real G1;
    Standard_Real B1;
    myView->BackgroundColor(Quantity_TOC_RGB,R1,G1,B1);
    aColor.setRgb(R1*255,G1*255,B1*255);

    QColor aRetColor = QColorDialog::getColor(aColor);

    if( aRetColor.isValid() )
    {
    R1 = aRetColor.red()/255.;
    G1 = aRetColor.green()/255.;
    B1 = aRetColor.blue()/255.;
    //myView->SetBackgroundColor(Quantity_TOC_RGB,R1,G1,B1);
    if(myLayer.IsNull())
    myLayer = new Visual3d_Layer (myView->Viewer()->Viewer(),
    Aspect_TOL_UNDERLAY, Standard_True); //
    }

    ::updateGradientBackground(myLayer,
    Quantity_Color (R1, G1, B1, Quantity_TOC_RGB),
    Quantity_NOC_WHITE);

    myView->Redraw();
    }
    ---
    On top on your head do you know a reason why?

    Just wanted to express my gratitude for all the lessons and education you are providing.

    Thank you.

    Venu
    ps: I tried commenting myView->Redraw() but no use.

    ReplyDelete
  21. Hi Venu,

    Thanks for a comment and posting a work-around. Nope, no idea upfront. I'm sitting on the Windows box and CAD Exchanger GUI is Windows-only (though underlying SDK is cross-platform). If/when it gets Linux port (though not sure if it ever be done) I'll get back to the issue you are mentioning.

    Roman

    ReplyDelete
  22. Hi Roman, I tried reading something about drawing text on layers on OCC forums.
    http://www.opencascade.org/org/forum/thread_15849/

    Even though it is not exactly about background color gradient, I was able to stabilize my layering code from breaking. I instead of calling updateGradientBackground() from onBackgroundColor(). I called it from View::init(). For some reason that helped. Now to experiment with your gradient code.

    Regards,
    Venu

    ReplyDelete
  23. Hi, Roman:

    This doesn't work with OCCT 6.5 anymore, any ieda?

    ReplyDelete
  24. Hi James!

    6.5 comes with redesigned layer mechanism, so it may invalidate prior API. On the other hand, there is already a built-in support for gradient background. See in DRAWEXE:
    pload VISUALIZATION
    vinit
    vsetgradientbg 128 128 255 255 255 255 1

    To see the source file where the command is defined use:
    getsource vsetgradientbg
    vsetgradientbg .\..\..\src\ViewerTest\ViewerTest_ViewerCommands.cxx

    ReplyDelete
  25. Hi, Roman:

    Thank you so much, it works !!!!!!
    Without your help, this would have took me a lot of time (if not forever). OCCT is huge, I'm still struggling to find the info I need from the source code and documentation.

    Thanks again.
    James

    ReplyDelete
  26. Hi guys
    I'm new to OCC and I want to work on MFC MDI project ...can any body provide me with a code to set my 3D_Viewer ????
    any help would be appreciated

    Thanx in advance

    ReplyDelete