gkv311 / occt-samples-qopenglwidget

Open CASCADE Technology sample - 3D Viewer within Qt Widgets window
Other
54 stars 16 forks source link

[Information] Build on linux #2

Closed grotius-cnc closed 1 year ago

grotius-cnc commented 2 years ago

Hi,

To build on linux i changed the source at a few points.

  1. Solved errors when qt headers are not loaded first.
  2. Added a few linux include paths.
  3. Modified a line to : myView->SetWindow (aWindow, (Aspect_RenderingContext) glXGetCurrentContext());
  4. Tested on opencascade .deb installer : https://github.com/grotius-cnc/oce/releases/tag/1.0.1

The archive of this repository to build on linux : occt-samples-qopenglwidget.tar.gz

gkv311 commented 2 years ago

Thanks for sharing.

Fill free to create a Pull Request to be able to integrate fixes (if they don't cause other problems).

grotius-cnc commented 2 years ago

Hi,

Ok i will work it out. Are you ok with a second directory containing a qt gui app with graphical designer layout based on your code? That is a usefull setup when building more complex programs. Then users can choose either your parametric coded example or choose a grapical design example.

I have a tiny request for you. I know you are master kenobi of opencascade. I am a kind of stuck on this problem.

In a cnc program i have 1.200.000+ gcode lines of a pruca slicer nc code. Up to 60.000 gcode lines can be processed by opencascade->make wire etc. No problem.

https://user-images.githubusercontent.com/44880102/150699178-48e660f0-0369-4772-9843-d500ff9c4fbc.jpg

But loading 1.200.000+ edges -> to wire looks like a impossible task for opencascade. 10 minutes +.

Can you give a few one liners to directly call opengl functions.

I would be happy if something like this is working in your example gui: if(aGlCtx->core11 != NULL){ aGlCtx->core20->glCreateProgram(); aGlCtx.get()->core20->glColor3d(1,1,1); aGlCtx.get()->core20->glBegin(GL_LINE); aGlCtx.get()->core20->glVertex3d(0,0,0); aGlCtx.get()->core20->glVertex3d(1000,1000,1000); aGlCtx.get()->core20->glVertex3d(-1000,-1000,-1000); aGlCtx.get()->core20->glEnd(); }

Earlyer this day i posted the same question over here : https://github.com/tpaviot/oce/issues/746

gkv311 commented 2 years ago

Are you ok with a second directory containing a qt gui app with graphical designer layout based on your code?

Let me first describe my thoughts behind the current sample. I see two common types of samples:

You may find tutorial-alike samples based on MFC and Qt Widgets frameworks within OCCT. Such samples have many benefits, as they demonstrate many features in a single place, avoid code duplication, and their content allows adding new similar features to application even for users not knowing (well) particular GUI framework by a copy-paste of similar button / menu / dialog. The key drawback of such samples is they are large in code size, tangled in structure (additional hierarchy of classes, shared functionality, multi-project building environment), over-complication (like implementing Multi-Document Interface, or MDI, when your application works only with single document), features are difficult to extract without copying a plenty of dependencies, and others.

In contrast, Hello-World sample (I don't know if there is a better term for that, like "unit-sample") is focused on a single task and should be clean from the burden of unrelated stuff as much as possible. Both sample styles (tutorial and hello) might be helpful to learn new framework and to develop application, so that one may complement another.

As you may see, this repository implements a Hello-World sample, so that I've intentionally tried to simplify project structure (minimal number of source files, no external dependencies) and reduce amounts of unrelated functionality (like importing STEP files or modeling complex geometry) - as sample purpose is to show 3D Viewer initialization using Qt OpenGL widget.

In this concept, I find UI files redundant and over-complicating, as this is only one of the way to use Qt Widgets. But I must admit. that potential user might look at the sample and say - "OK, I see 3D Viewer, but how to put it into UI file that our Qt GUI experts relies on?". So such a sample would be indeed helpful.

I don't like an idea of replacing existing sample with another one using UI files, and have doubts that putting ifdef-blocks for sharing common code wouldn't spoil sample clarity. So I like an idea of putting several samples into dedicated sub-folders with self-sustained list of files - even if this obviously leads to code duplication. So that we would have one sample using QOpenGLWidget in a plain C++ (occt-qopenglwidget), another using UI files (occt-qopenglwidget-ui), and maybe one more sample will implement a conventional viewer integration into a general QWidget.

gkv311 commented 2 years ago

But loading 1.200.000+ edges -> to wire looks like a impossible task for opencascade. 10 minutes +.

If you have a plenty of line segments or polylines to display, then you are probably doing it in a non-efficient way. You are probably constructing Geom_Curve + TopoDS_Edge out of line segments and display them using presentations like AIS_Shape, but that would bring a lot of overhead on memory and performance.

Instead, TopoDS_Edge could be created out of Poly_Polygon3D - a tessellated representation of a curve without analytical Geom_Curve. You wouldn't be able to use such geometry in modeling algorithms, but it should work for visualization purposes. Or you may sub-class AIS_InteractiveObject and construct / display Graphic3d_ArrayOfSegments directly without additional overhead of dealing with B-Rep definition.

Graphic3d_ArrayOfPrimitives is pretty close to interfaces of low-level graphic libraries - it is mapped into VBO (Vertex Buffer Object) in case of OpenGL, and not much you can do here to improve performance further by using OpenGL directly if you have to display all these segments. The main thing to avoid is creation of many presentations (AIS_InteractiveObject) or many distinct arrays of primitives - better combining them into larger array(s). Using obsolete OpenGL functionality like immediate rendering (glBegin()/glVertex3d()/glEnd()) would certainly kill performance instead of improving it.

grotius-cnc commented 2 years ago

Hi,

Thanks for your fast responce and idea's. I will try out your suggestions about improving. I hope i can bring good news later on this day.

Your "hello world" example is a precious code because of the working qwidget overlay. Also the refined mouse zoom in, zoom out is nice.

I will work out a example, coded as a widget. This is in my opinion the preferred way newbies need to go for.

grotius-cnc commented 2 years ago

preview

The progress so far. The overlay works nice.

grotius-cnc commented 2 years ago

Hi,

I think i am ready with the example. I see you are from Russia. Perfect !! I like that country. One of my cnc tube cutter's lives there.

https://github.com/grotius-cnc/occt-samples-qopenglwidget/blob/master/occ_gui_app/

grotius-cnc commented 2 years ago

Hi,

I tested loading 1.2 milion lines followed by your Poly_Polygon3D suggestion and it worked. It load the preview in about 1 minute.

    //! Basic impementation example:
    TopoDS_Shape ResultShape;
    TColgp_Array1OfPnt array (1,5); // sizing array
    array.SetValue(1,gp_Pnt(100,0,0));
    array.SetValue(2,gp_Pnt(1000,0,0));
    array.SetValue(3,gp_Pnt(1000,500,0));
    array.SetValue(4,gp_Pnt(100,500,0));
    array.SetValue(5,gp_Pnt(100,0,0));
    Handle(Poly_Polygon3D) aPolygon = new Poly_Polygon3D(array);
    BRep_Builder Brep;
    TopoDS_Edge& E = TopoDS::Edge(ResultShape);
    Brep.MakeEdge(E,aPolygon);
    Handle(AIS_Shape) Ashape=new AIS_Shape(ResultShape);
    m_context->Display(Ashape,Standard_False);

After the shape preview, i tried to free as much as possible memory, and it worked.

    array.IsDeletable();
    aPolygon->Delete();
    aPolygon.Nullify();
    E.Nullify();

The program was running ok, i let it run for about 25 minutes. The opencascade responce stayed perfect.

NEXT EXAMPLE

One thing i am still interested in the Handle(Graphic3d_ArrayOfPoints) example you give me, but i have problems finishing the code. After a while i could let it work. In this page there is a nice example https://dev.opencascade.org/doc/overview/html/occt_user_guides__visualization.html

Ok for me i have some standard thing in the header file.

things inside the opencascade header file:
private:
    void m_initialize_context();
    Handle(AIS_InteractiveContext) m_context;
    Handle(V3d_Viewer) m_viewer;
    Handle(V3d_View) m_view;
    Handle(Graphic3d_GraphicDriver) m_graphic_driver;
    Handle(AIS_InteractiveObject) m_aisViewCube;
//! Then in the cpp file i did : 
#include <Graphic3d_ArrayOfPrimitives.hxx>
#include <Graphic3d_ArrayOfSegments.hxx>
#include <Graphic3d_ArrayOfPoints.hxx>
#include <AIS_PointCloud.hxx>
#include <Graphic3d_NameOfMaterial.hxx>

    //! Create a structure in this Viewer.
    Handle(Graphic3d_Structure) aStruct = new Graphic3d_Structure (m_viewer->StructureManager());
    aStruct->SetVisual (Graphic3d_TOS_SHADING); // Type of structure

    //! Create a group of primitives  in this structure.
    Handle(Graphic3d_Group) aPrsGroup = aStruct->NewGroup();

    //! Ammount of points.
    int size=;1200000;
    Handle(Graphic3d_ArrayOfPoints) aPointArray=new Graphic3d_ArrayOfPoints(size,Graphic3d_ArrayFlags_None);

    //! Fill the point array with points.
    for(unsigned int i=0; i<size; i++){
    //! Add the x,y,z points.
        aPointArray->AddVertex({x,y,z});
    }
    //! Check if point array result is ok.
    std::cout<<"shapearray.isvalid:"<<aPointArray->IsValid()<<std::endl; 

    //! This is optional.
    Handle(Graphic3d_AspectFillArea3d) anAspects = new Graphic3d_AspectFillArea3d (Aspect_IS_SOLID, 
        Quantity_NOC_RED,
        Quantity_NOC_RED, Aspect_TOL_SOLID, 1.0f,
        Graphic3d_NOM_ALUMINIUM,Graphic3d_NOM_ALUMINIUM);
    aPrsGroup->SetGroupPrimitivesAspect (anAspects);

    //! Add the pointarray o the group.
    aPrsGroup->AddPrimitiveArray (aPointArray);

    // Display presentation in this View
    aStruct->Display();

    //! This is all done without any line like     m_context->Display(Ashape,Standard_False);
gkv311 commented 2 years ago

This is all done without any line like m_context->Display(Ashape,Standard_False);

V3d_View provides a low-level interface for displaying presentations - so that you may create Graphic3d_Structure and display it straight ahead. AIS_InteractiveContext fulfills other parts like picking, selection, interaction with the objects. And even if object is not intended to be interactive, it still looks useful to place all objects together in context.

grotius-cnc commented 2 years ago

Hi,

Thanks. I think the struct is nice to implement. If you have a vector struct, it can manage data on the fly. One of my software users, has a problem loading a stepfile of ~100mb. I think above code example's can solve part's of this problem.

I'm still interested how a eventual opengl api call should look like from the occt lib. Just to expand my knowlegde. Maybe you can give just one tiny howto example? Only when you have time off course.

Currently coding a third'h occt widget example. The yesterday example was with a mainwindow app. Now coding it just as a widget. I have made in total 3 classes to get the widget functional and separate the opengl stuff with the help off independent classes. Somewhere i saw a comment of you, suggesting to separate opengl includes from qt and occt. And indeed, this is the only solution to get it work.

In this example i am planning to include a sub-directory with some of my occt draw functions.

Have a good night ! And thanks for the help so far master kenobi !

gkv311 commented 2 years ago

I'm still interested how a eventual opengl api call should look like from the occt lib.

The sample comes with OCCT - see VUserDrawObj::Render() for VUserDrawObj sample class within OpenGlTest_Commands.cxx

grotius-cnc commented 2 years ago

Hi, Sorry to bother you again.

The following code loads a preview with a black background. But no items are showing up. Did you ever get something like this working? I have tried it for several hours, but it seems very difficult.

`

Handle(Aspect_DisplayConnection) a_display_donnection = new Aspect_DisplayConnection();

Handle(OpenGl_GraphicDriver) myglGraphicDriver = new OpenGl_GraphicDriver(a_display_donnection);

WId window_handle = (WId) winId();

Handle(Xw_Window)  a_wind = new Xw_Window(a_display_donnection, (Window) window_handle);

Handle(OpenGl_Window) myglWindow = myglGraphicDriver->CreateRenderWindow(a_wind , (Aspect_RenderingContext)0);

Handle(OpenGl_Caps) myglCaps = &myglGraphicDriver->ChangeOptions();

Handle(Graphic3d_StructureManager) myG3dStructureCanager = new Graphic3d_StructureManager(myglGraphicDriver);

OpenGl_StateCounter* myglStateCounter = new OpenGl_StateCounter();

OpenGl_View *myglView = new OpenGl_View(myG3dStructureCanager, myglGraphicDriver, myglCaps, myglStateCounter);

Handle(OpenGl_Workspace) myglWorkSpace = new OpenGl_Workspace(myglView, myglWindow);

Handle(OpenGl_Context) myglContext = myglWorkSpace->GetGlContext();

myglContext->ShaderManager()->BindLineProgram(Handle(OpenGl_TextureSet)(), Aspect_TOL_SOLID, Graphic3d_TOSM_UNLIT, Graphic3d_AlphaMode_Opaque, false, Handle(OpenGl_ShaderProgram)());

OpenGl_Vec4 col(2);
myglContext->SetColor4fv(col);

const OpenGl_Vec3 aVertArray[4] =

{

OpenGl_Vec3(-10.0,-20.0,-30.0),

OpenGl_Vec3(10.0, 20.0, -30.0),

OpenGl_Vec3(10.0, 20.0, 30.0),

OpenGl_Vec3(-10.0,-20.0,30.0),

};

Handle(OpenGl_VertexBuffer) aVertBuffer = new OpenGl_VertexBuffer();

aVertBuffer->Init(myglContext, 3, 4, aVertArray[0].GetData());

aVertBuffer->BindAttribute(myglContext, Graphic3d_TOA_POS);

glDrawArrays(GL_LINE_LOOP, 0, aVertBuffer->GetElemsNb());

aVertBuffer->UnbindAttribute(myglContext, Graphic3d_TOA_POS);

aVertBuffer->Release(myglContext.get());

`

grotius-cnc commented 2 years ago

Hi,

After some more day's of investegating i have finally found a solution to use the opengl api with opencascade and get a working display result.

I did not really need a direct opengl request. But i just wanted to know how to do this from inside the opencascade, in case of a future scenario.

I was lost for several day's trying out example after example, without any succes.

Then today i opened the file Occt.pro in qt. This is a qt project file inside the opencascace source code. ~ /oce-upstream-V7_5_0beta/adm/qmake/Occt.pro

It has some comments about how to build the opencascade project in qt. I followed that, and after a while it was building. I had to solve a stupid build error. And it worked out of the box.

Then i had to think about a strategy to get done what i wanted. I focussed on the myview and myviewer files. I was looking for how is OpenGl_View.cxx connected to myview and myviewer... It's quite confusing to work trough the code. But after a while i got a spot.

OpenGl_View_Redraw.cxx, in this file i pasted some OpenGl core code. My screen output was ok. I reqeusted 2 square's.

boxooct

It drawed what i want. It worked at the renderStructs and the blitBuffers functions.

//! line 1248 OpenGL_View_Redraw_cxx renderStructs //! line 1509 ,, blitBuffers

hackintothesource

So now this works, i can do some extra reverse engineering, clean it up, and make a permanent OpenGL core code user interface.

My conclusion. Don't give up the first day.

gkv311 commented 2 years ago

Then today i opened the file Occt.pro in qt.

I'm not a fan of Qt Creator and it's clumsy bindings to Qt Framework, but so far I haven't seen something more reliable for Linux platform, where I have to debug OCCT from time to time (previously I've used Code::Blocks for that purpose). And since I hate CMake even more than qmake (that I have to use in some other cross-platform project), and bindings to CMake in Qt Creator are something weird - I've decided to write this funny Occt.pro project for it that is able to parse FILES / PACKAGES / EXTERNLIB and automatically generate projects. Initially I was hoped to make it completely self-sustained, but after some fighting with qmake limitations, decided to keep using genproj for generating header files in inc folder and for configuring paths to dependencies.

I was looking for how is OpenGl_View.cxx connected to myview and myviewer...

There are several ways for combining OCCT 3D Viewer and other OpenGL renderer:

In which method to put the code would depend on what your engine actually needs to draw. Modern 3D graphic renderers are complex beasts, so you cannot put an arbitrary immediate-render OpenGL code anywhere and expect it to work properly - you have to understand how existing 3D engine renders scene and embed your code into proper place initializing and leaving OpenGL context state in some valid state. Article TKOpenGl Frame Render Graph might be helpful to oversee basic steps in it.

You cannot find deeper samples of OpenGL user drawing in OCCT for a simple reason - it is an alien and a misconception for OCCT itself. It defines an Graphic3d_GraphicDriver interface for a good reason - to provide an abstraction level between 3D graphics and it's rendering using low-level OpenGL library. OpenGL is a quite good cross-platform library combining feature-completeness and relative simplicity (compared to overcomplicated Vulkan) that doesn't yet have a better alternative (in all aspects), but in the future there are might be more Graphic3d_GraphicDriver implementations based on Vulkan, WebGPU, Metal, Direct3D or whatever... And having a custom OpenGL rendering code at application side would prevent straightforward switching to another driver implementation.