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
It looks great!!
ReplyDeleteI'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);
Hi Koala, not sure I got a comment about a bug. Could you elaborate ?
ReplyDeleteHi 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:
ReplyDeletemyViewer = new V3d_Viewer(
ReplyDeletetheGraphicDevice,
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();
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) ?
ReplyDeleteIf OCC folks read this and have a comment, we would be interested to know.
Don't worry. It is only a typing error. You confused aWeight by aWidth.
ReplyDeleteDespite of that, I do not get working even with Svetlozar settings. Is required a specific version of OCC (Using 6.2.0)?
I simplified your codes easy for newer
ReplyDeletevoid 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();
}
2 Koala:
ReplyDeleteNope, 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.
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!
ReplyDeleteI added the code fragment to my aplication generated with the mfc appwizard. But cannot see the gradient, only the default flat color.
ReplyDeleteSharjith N.
Hmmm, I just took MFC samples shipped with OCC and modified OCC_3dView::OnModifyChangeBackground() as follows:
ReplyDeletevoid 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.
Svetlozar, N. Sharjith:
ReplyDeleteDon't call this function in OnInitialUpdate. Call it later, after initialization finish and it will work!
Koala, when to call if not in OnInitialUpdate()?
ReplyDeleteYes, it just worked after I pasted the same fragment in the View3D OnInitialUpdate instead of the View's.
ReplyDeleteJust after the base class View OnInitialUpdate is called from the View3D class. Great!
Thanks..
Wishing all a Merry Christmas!
N. Sharjith
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 :(
ReplyDeleteHi Ceniza,
ReplyDeleteWell, 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 ;-).
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.
ReplyDeleteThanks for your reply.
Hi,
ReplyDeleteThank 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
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...
ReplyDeleteHi Roman,
ReplyDeleteI 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.
Hi Venu,
ReplyDeleteThanks 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
Hi Roman, I tried reading something about drawing text on layers on OCC forums.
ReplyDeletehttp://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
Hi, Roman:
ReplyDeleteThis doesn't work with OCCT 6.5 anymore, any ieda?
Hi James!
ReplyDelete6.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
Hi, Roman:
ReplyDeleteThank 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
Hi guys
ReplyDeleteI'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