General transformation for PrsMgr_PresentableObject

PrsMgr_PresentableObject transformation defined by TopLoc_Datum3D, which is essentially just a gp_Trsf.
gp_Trsf supports only uniform scales. Such limitations are understandable for shapes, but for meshes, they impose a serious limitation on rendering certain real 3D models.
For sure, transformation can be applied to each vertex and normal when the mesh is built, but that's fine if the transformation doesn't change often. If any animation is involved, it forces the mesh to be rebuilt.
I even tried to provide a custom ModelToWorld matrix via a shader program, but with little success, since near/far clipping occurs: these planes are calculated from the geometry data without the matrix.

How to solve that? Maybe a good idea for the V8 improvement?

Dmitrii Pasukhin's picture

At the moment, non-uniform scaling is not supported directly. There is no direct way of use it in the clear way.

Upgrading the location to the matrix is aggressive rework and it will impacted BRep a lot. Originally OCCT is BRep kernel. Mesh is additional representation, which is not a direct interest of algorighms.

You can apply transformation on CPU using the BRepBuilderAPI_GTransform (more specific BRepTools_GTrsfModification::NewTriangulation) or you can apply similar logic into custom shader.

At the moment, updating to gp_GTrsf or similar logic is planned for the long term, not for 8.0.0, and It will be not just accepting different diagnosis of matrix, it is also will accept more operations on BRep. Which will be a huge redesign. So, unfortunately, the changes will be not soon.

Best regards, Dmitrii.

Ivan Poberezhnyk's picture

Thanks for the answer. I do understand limitations with BReps. But it seems that the TopoDS_Shape transformation (TopLoc_Location) and the PresentableObject transformation are not closely related. The shape's transformation is applied for the shape's triangulation. And that's ok if only gp_Trsf can be used here. But the PresentableObject's transformation is not affected by that (if I see the picture correctly).

gkv311 n's picture

gp_Trsf within AIS_InteractiveObject is an unfortunate consequence of desire to reuse existing TopLoc_Datum3D, with overseen use case of non-uniform scaling (rarely needed).

If we will take a look at gp_Trsf, we will find that all its methods process the 3x3 matrix to fit definition of uniform-only scale factor (stored as a dedicated class field, hence 3x3 matrix is only for rotation), and all its fields are private by design.

All, but one - gp_Trsf::InitFromJson() lacks validation layer (by mistake of course, not by design), so it can be used to initialize a 'broken' transformation. If we do that to create a matrix with non-uniform scale, we can see objects displayed with this scale. However, the selection of the object will work incorrectly.

One of the reasons is that PrsMgr_PresentableObject::UpdateTransformation() computes inversed transformation out of gp_Trsf::Inversed(). We may further hack PrsMgr_PresentableObject::InversedTransformation() to assign a properly initialized inversed matrix to it.

#include <AIS_Shape.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeCone.hxx>
...
const Handle(AIS_InteractiveContext)& theCtx = ...;

TopoDS_Shape aBox  = BRepPrimAPI_MakeBox(gp_Pnt(50.0, 0.0, 0.0), 100.0, 100.0, 100.0);
TopoDS_Shape aCone = BRepPrimAPI_MakeCone(100.0, 20.0, 100.0);

gp_Trsf aTrsf;
{
  // This code abuses the fact that gp_Trsf::InitFromJson() lacks validation layer.
  // gp_Other = 8
  std::stringstream aStream;
  aStream << "\"Location\": [-50, 0, 0], \"Matrix\": [3, 0, 0, 0, 3, 0, 0, 0, 1], \"shape\": 8";
  int aPos = 1;
  aTrsf.InitFromJson(aStream, aPos);
}
Handle(AIS_Shape) aPrsBox = new AIS_Shape(aBox);
aPrsBox->SetLocalTransformation(aTrsf); // for visualization
const_cast<gp_GTrsf&>(aPrsBox->InversedTransformation()) =
  gp_GTrsf(aTrsf).Inverted(); // for selection

Handle(AIS_Shape) aPrsCone = new AIS_Shape(aCone);
aPrsCone->SetLocalTransformation(aTrsf); // for visualization
const_cast<gp_GTrsf&>(aPrsBox->InversedTransformation()) =
  gp_GTrsf(aTrsf).Inverted();

theCtx->Display(aPrsBox, false);
theCtx->Display(aPrsCone, true);

Alternatively one may declare gp_Trsf copy with access to private fields and apply modifications via memory aliasing instead of serializing string for gp_Trsf::InitFromJson(). Of course, this is just a fragile hack and proof of concept, I don't suggest using it in a real code, but it might be tolerable as a temporary workaround, if you have very limited scenario to apply it.

nonuniformscale.png

gkv311 n's picture

I have drafted a patch that replaces gp_Trsf with gp_GTrsf in AIS viewer. It implies many changes in API, so I'm not sure if it really worth adding support for affine transformations to AIS like this, considered very rare scenarios, where it could be actually useful for CAx tasks...

image

Ivan Poberezhnyk's picture

Thank you very much for the patch.
Unfortunately, I cannot apply it as the base commit information is missing in the .patch file.
Can you please tell the commit to apply the patch to?

gkv311 n's picture

Can you please tell the commit to apply the patch to?

The current version of the patch could be found in the branch v7_7_x_prs_gtrsf, two commits:

  • "Visualization - add Transformed(gp_GTrsf) methods to gp_Pnt, gp_Dir and Bnd_Box";
  • "[DRAFT] Visualization - use gp_GTrsf instead of gp_Trsf for local transformation".