A proper way to make animation of AIS_Shape on scene

Hello. What is the best way to use animation of objects on scene (preferably, AIS_Shape entries)?

I have looked at the mfc-related example, but it was not clear for me. Now I'd like to start from simpler example - move a cyllinder with certain speed to a point and the stop. There are two ways:

1. Use AIS_Animation class ( https://www.opencascade.com/doc/occt-7.3.0/refman/html/class_a_i_s___ani... ). It is unclear how to use it anyway, there are no examples how to use it!

2. Simple use transformation within the thread, like it is described here: https://www.opencascade.com/content/rotating-and-translating-cylinder-3d...

Kirill Gavrilov's picture

AIS_Animation is a centralized tool for handling simple and complex (nested) animations in a reliable way,
introducing concepts like presentation timestamp, high-resolution timer, animation speed, interpolation routines, etc.
In case of local transformation applied to object, AIS_AnimationObject would do internally the same things as in referred links in your post.
MFC animation sample just have not been updated for using AIS_Animation.

AIS_Animation wouldn't do everything for you "automatically" - it should be properly initialized (defining animation sequence(s) - key frames) and used (to redraw viewer at proper intervals) at application level.
See also tests\v3d\anim\ Draw Harness tests demonstrating usage of vanimation command.

Ivan P's picture

Thanks for extended answer. I have started the draw.bat (a Draw Harness), issued File > Load Script, opened tests\v3d\anim\objects and got the following message in console:

==================================
Viewer animation - animate object moving
==================================
bgerror failed to handle background error.
    Original error: can't read "imagedir": no such variable
    Error in bgerror:

Objects loaded, but animation did not start.

Kirill Gavrilov's picture

Tests in OCCT are executed using test and testgrid commands, e.g.:

> test v3d anim propeller 1
> vanimation anim -play
Ivan P's picture

Dear Admin! Please, make a C++ example of animation.

Ivan P's picture

I have defined the following fragment for animation :

 std::vector<Handle(AIS_InteractiveObject)> privateSimpleRig; // I fill it while populating scene . I push_back here AIS_Shape handles, it is OK

gp_Trsf firstTransformation;
gp_Trsf secondTransformation; 
secondTransformation.SetTranslation(gp_Vec(100.0,100.0,0.0));
cyllindricalAnimation = new AIS_AnimationObject( TCollection_AsciiString(22), privateAISContext, privateSimpleRig[2], firstTransformation, secondTransformation);
cyllindricalAnimation->SetOwnDuration(100.0);
cyllindricalAnimation->StartTimer(0.0,1.0,true,false);

But animation does not work. Am I missing something? an item stands in place without moving.

Kirill Gavrilov's picture

See:
> AIS_Animation wouldn't do everything for you "automatically" - it should be properly initialized (defining animation sequence(s) - key frames)
> and used (to redraw viewer at proper intervals) at application level.

AIS_Animation::StartTimer() starts its internal timer returning elapsed time, the actual update of current frame is done by AIS_Animation::UpdateTimer(), which should be called before redrawing new frame in 3D Viewer.
Redrawing the viewer should be done by application itself, and will depend on used GUI framework and application design.

Ivan P's picture

That's clarifies something. Should I further subclass AIS_AnimationObject, (re)implement AIS_Animation::UpdateTimer() and call this routine on screen update?

Kirill Gavrilov's picture

You don't need re-implementing AIS_Animation::UpdateTimer(), standard implementation should be enough in most cases.
General idea is:
- Define single AIS_Animation instance at application level as a container for active animation(s) and main playback controller.
- Define animation atoms like AIS_AnimationObject/AIS_AnimationCamera and insert them into AIS_Animation container (AIS_Animation::Add()). Each item should define its own duration (should not be 0) and start presentation timestamp (PTS, 0 by default), so that AIS_Animation container will automatically calculate the complete animation duration as summary of its children. Nested AIS_Animation containers are also possible.
- Start AIS_Animation playback AIS_Animation::StartTimer(), and call AIS_Animation::UpdateTimer() within each Viewer redraw (for example, on multimedia timer triggering update at desired time intervals, as provided by GUI framework) until animation is not finished (AIS_Animation::IsStopped()). AIS_Animation controller will automatically execute (update) children, so that only one root AIS_Animation instance (controller) should be managed with StartTimer()/UpdateTimer()/IsStopped() calls.
- Custom animation items (similar to AIS_AnimationObject/AIS_AnimationCamera) should inherit AIS_Animation and override AIS_Animation::update() method. Existing implementation can be used as a reference. The main input for ::update() is a current presentation timestamp (PTS) and normalized (0..1) progress inside this specific item (based on own duration), which should be used for updating scene (for instance, by applying linear interpolation between two key frames), see also AIS_AnimationProgress definition.

john wick's picture

when I inherent from AIS_AnimationObject or AIS_Animation there is always a compile error saying DynamicType could not be not be linked? Why is that?

public: virtual class opencascade::handle<class Standard_Type> const & __cdecl SelfDefinedAnimationObject::DynamicType(void)const "

Kirill Gavrilov's picture

when I inherent from AIS_AnimationObject or AIS_Animation there is always a compile error saying DynamicType could not be not be linked? Why is that?

You have put DEFINE_STANDARD_RTTIEXT in your class, but haven't added IMPLEMENT_STANDARD_RTTIEXT in source code implementing these functions.

2271801720_140466's picture

I make an example below, like a main function in c.

TopoDS_Shape obj1 = BRepPrimAPI_MakeBox(100, 500, 20);
Handle(AIS_Shape) ais_obj1 = new AIS_Shape(obj1);
g_pDoc->myAISContext->Display(ais_obj1, true);

gp_Trsf start_pnt, end_pnt;
	
start_pnt.SetValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0);
end_pnt.SetValues(1, 0, 0, 100, 0, 1, 0, 100, 0, 0, 1, 100);
	
Handle(AIS_Animation) ais_animation = new AIS_Animation("obj1");
Handle(AIS_AnimationObject) ais_ao = new AIS_AnimationObject("obj1", g_pDoc->myAISContext, ais_obj1, start_pnt, end_pnt);
ais_ao->SetOwnDuration(10);
ais_ao->SetStartPts(0);
	
ais_animation->Add(ais_ao);

double duration = ais_animation->Duration();

ais_animation->StartTimer(0, 1.0, true);

while (!ais_animation->IsStopped())
{
	ais_animation->UpdateTimer();
		
	g_pDoc->myAISContext->UpdateCurrentViewer();
}
kangzixiang6_144903's picture

Thanks for your contribution

m.nserat_157086's picture

thanks very much for your code.
I don't know if I should ask here, 
this code worked correctly 
but I tried to write this code within another "thread"  to get other functionalities in my app, like rotation,
so when I tried that  I had some problems:

1- the Viewer did not update, and I could not see it moving.

2- if I made another process in the main thread, I will get an exception in function "UpdateCurrentViewer"

can you give me some help, please?

TIAN DAJIANG's picture

Hi,Have you resolved this problem? I have same problem!

bioan m's picture

Hello everybody!
I also want to ask if there is any solution to this problem.
I like to create a simple animation (have an array of points and want a tool - cylinder - to follow the points array path) and could find any ideas to advance in this problem.

Kirill Gavrilov's picture

bioan wrote:

I like to create a simple animation (have an array of points and want a tool - cylinder - to follow the points array path) and could find any ideas to advance in this problem.

The common solution looks like this:

  • Define duration of the whole animation and assign presentation timestamp to each point in array.
    • In the simplest case of equally distributed points this could be just splitting the whole duration by a number of points, or by calculating time based on the distance between points and linear speed.
    • In more complex cases of robotic animation that have other constraints (like maximum linear and angular speeds) the calculation will depend on specific robotics system.
  • Define object's (cylinder) location based on your point definition.
    • Considering the point is defined as gp_Ax2/gp_Ax3, you may construct gp_Trsf from these axes taking into account coordinate system, in which object is defined. In the simplest case this looks like this:
      gp_Ax3 theAxes = ...;
      gp_Trsf aTrsf;
      aTrsf.SetTransformation (theAxes, gp::XOY());
      ...
      Handle(AIS_InteractiveContext) theContext = ...;
      Handle(AIS_Shape) theShape = ...;
      theContext->SetLocation (theShape, aTrsf, true);
  • Define your own animation object, based on AIS_Animation or AIS_AnimationObject implementing AIS_Animation::update() interface.
    • This method receives a time to be displayed on the screen in form of AIS_AnimationProgress structure.
      Iterate over your point array and find two closest timestamps before and after current time (or equal to current time).
    • Compute an interpolated location (transformation) between these two points.
      gp_TrsfNLerp could be used for that purpose, which performs a per-component linear interpolation (location, orientation).
      You may define your own math that suits better in your case (for instance, you may involve a kinematics solver for this).
      void MyAnimation::update (const AIS_AnimationProgress& theProgress)
      {
        // find closest points
        int aPntIter = 0;
        for (; aPntIter < myNbPoints; ++aPntIter)
        {
          if (myPoints[aPntIter].Timestamp >= theProgress.LocalPts)
          {
            break;
          }
        }
        if (aPntIter == 0 || aPntIter == myNbPoints)
        {
          // clamp to the first / last point position
        }
      
        // take closest locations
        gp_Trsf aTrsfBefore = myPoints[aPntIter - 1].Trsf; // range checks are missing!
        gp_Trsf aTrsfAfter  = myPoints[aPntIter].Trsf;
        gp_TrsfNLerp aTrsfLerp (aTrsfBefore, aTrsfAfter);
      
        // interpolate closest locations
        double aLocalDuration = myPoints[aPntIter].Timestamp - myPoints[aPntIter - 1].Timestamp;
        double aLocalProgress = theProgress.LocalPts - myPoints[aPntIter - 1].Timestamp;
        gp_Trsf aTrsf;
        aTrsfLerp.Interpolate (aLocalProgress / aLocalDuration, aTrsf);
      
        // assign new location to interactive object
        if (!myContext.IsNull())
        {
          myContext->SetLocation (myObject, aTrsf);
          invalidateViewer();
        }
        else
        {
          myObject->SetLocalTransformation (aTrsf);
        }
      }
  • Start AIS_Animation playback in some way.
    • If you will assign animation to AIS_ViewController::ObjectsAnimation() property, view controller will allow camera manipulations during object's animation.
bioan m's picture

Thank you very much for your detailed explanation!
My array of points is nothing more than a toolpath which consists of sequence of lines, in some cases more than 10 Mb of data.
This array is composed of gp_Pnt's.
First questions:
1. what kind of data is a presentation timestamp? (never use it before)
2. how a presentation timestamp could be attached to a gp_Pnt?
3. in cases of Mb size of gp_Pnt's, just defining the duration of the whole animation wouldn't be a very consuming process?
4. why doesn't exist a simple and complete example of basic implementation (like this one) to avoid this wasting of time for everybody? Animation is an important aspect of 3D CAD world, and yet it does not exist some basic info about how to approach.

Kirill Gavrilov's picture

1. what kind of data is a presentation timestamp? (never use it before)

Presentation timestamp is a time (in seconds) on animation timeline when specific animation position should be displayed on the screen.

2. how a presentation timestamp could be attached to a gp_Pnt?

It is up to application to define animation timeline.

3. in cases of Mb size of gp_Pnt's, just defining the duration of the whole animation wouldn't be a very consuming process?

You may compute a timestamp for each point on-the-fly in case if animation timeline has a simple curve. I don't see any reason for performance issues to compute timestamps before starting animation, though.

4. why doesn't exist a simple and complete example of basic implementation

There are some legacy animation samples coming with OCCT. Fill free to contribute / share your sample code / article when you will figure out implementation details.

As for reusable mechanism inside OCCT itself - from my point of view different CAD applications compute animation pipeline in very different ways, so that a generalized solution is not applicable or wouldn't suffice different needs. There are also some interfaces defining Kinematics in STEP standard (CAD world), as well as simple animation workflow defined in glTF format (non-CAD world), which could be applicable to some scenarios.

bioan m's picture

My feeling is that you overcomplicate things and I'm not sure why.
There are people here spending years in creating CAM toolpaths using more or less complex geometric calculus (offsets, intersection curves, etc) and can't finish their work because there is no actual and complete documentation for a basic non-legacy animation!
I have a simple rectangle (100 units height / 50 width) and want to animate a cylinder with it's axis perpendicular on the rectangle plane and following the rectangle corners. Just simple as stupid, nothing fancy, no robotic animation, absolute no kinematics solver. I'm asking for a basic approach, without intention of a generalized one. But just to have a starting point, nothing more!

Kirill Gavrilov's picture

bioan wrote:

I'm asking for a basic approach, without intention of a generalized one. But just to have a starting point, nothing more!

Sure, animation is in my TODO list of articles - along with a dozen of other topics I would like to write about. Animation doesn't look like a simple subject to me, based on my background in writing a video player (where "presentation timestamp" (PTS), "decoding timestamp" (DTS) and other similar terms come from) and implementing animation in a couple of CAD applications. So, indeed, I may overcomplicate things here.

Meanwhile, maybe somebody else from OCCT community would be able to share experience and samples for the topic.

bioan m's picture

Hello!
Follow the example posted here, I create my own animation object - MyAnimation : public AIS_AnimationObject
I managed to receive all my points data (Position, Timestamp and Trsf) inside a struct array data member of MyAnimation class and can check it in MyAnimation::update()

I tried to start the animation using:

Handle(MyAnimation) myAnimation;
Handle(AIS_Animation) _Animation;
//..

myAISContext->Display(myAisTool, AIS_Shaded, 0, true);
myAnimation->SetStartPts(0);
myAnimation->SetOwnDuration(myAnimation->_TotalAnimationTime);
_Animation->Add(myAnimation);
_Animation->StartTimer(0, 1, true,false);

while (!_Animation->IsStopped())
{
_Animation->UpdateTimer();
myAISContext->UpdateCurrentViewer();
}
Using the code above I can see the animation on the screen but unfortunately I'm not able to rotate / pan and zoom during the simulation period.
So, my question is how to deal with this problem and implement AIS_ViewController::ObjectsAnimation() property.
Any idea?

Kirill Gavrilov's picture

bioan wrote:

So, my question is how to deal with this problem and implement AIS_ViewController::ObjectsAnimation() property.

occt-hello-viewer/aisobject includes a code snippet using AIS_ViewController::ObjectsAnimation(). You may try building this sample and finding some useful hints from there.

  MyViewer()
  {
    ...
    Handle(MyAisObject) aPrs = new MyAisObject();
    aPrs->SetAnimation (AIS_ViewController::ObjectsAnimation());
    ...
  }

...

bool MyAisOwner::HandleMouseClick()
{
  ...
  gp_Trsf aTrsfTo;
  aTrsfTo.SetRotation (gp_Ax1 (gp::Origin(), gp::DX()), isFirst ? M_PI * 0.5 : -M_PI * 0.5);
  gp_Trsf aTrsfFrom = anObj->LocalTransformation();
  Handle(AIS_AnimationObject) anAnim = new AIS_AnimationObject ("MyAnim", anObj->InteractiveContext(), anObj, aTrsfFrom, aTrsfTo);
  anAnim->SetOwnDuration (2.0);

  myAnim->Clear();
  myAnim->Add (anAnim);
  myAnim->StartTimer (0.0, 1.0, true
  ...
Justun John's picture

Great post this

bioan m's picture

Hi Kiril!
First off all I have to thank you very very much for the example posted before (MyAnimation::update) which is critical for starting the animation.
As I have already explained , I managed to animate the cylinder to follow the array points, but with frozen display - no mouse interaction, no pan, no zoom ,etc
Now, just to understand correctly, as I read from your article- https://unlimited3d.wordpress.com/2019/11/06/ais_viewcontroller-in-occt-...
AIS_ViewController is a new class which offers alternative to V3d_View methods (pan/zoom).
So have few question before starting to deal with AIS_ViewController:
1. can I mix the old V3d_View methods with AIS_ViewController functionality? These two can co-exist inside a single project?
2. In the example posted - occt-hello-viewer/aisobject - MyViewer : public AIS_ViewController() is implemented at the very low level of operating system (creating the window, setting the window message handler, etc). Is not the case in my project, everything is just in place, the only need is to unfrozen the display. What is needed to be contained in a minimal MyViewer sub-class from AIS_ViewController? (Do I need to create MyViewer::myContext or MyViewer::myView? as I already use myAISContext = GetContext();)
3. it is possible to indicate in a simple pseudocode how the hole process must be accomplished not necessarily for me, but in general, for all users interested to deal with unfrozen of an animation display in OOCT?

Any step forward is much appreciated!!!

Kirill Gavrilov's picture

bioan wrote

1. can I mix the old V3d_View methods with AIS_ViewController functionality? These two can co-exist inside a single project?

You may call V3d_View methods with a caution, but uncontrolled mixture of AIS_ViewController/V3d_View for camera manipulations (as well as redrawing the View) may lead to unexpected results and visual glitches. AIS_ViewController is not just a tool that helps with camera manipulations, it is also a workflow designed to say when and how to manage viewer events and to avoid unexpected viewer redraws.

2. In the example posted - occt-hello-viewer/aisobject - MyViewer : public AIS_ViewController() is implemented at the very low level of operating system (creating the window, setting the window message handler, etc). Is not the case in my project, everything is just in place,

This particular sample doesn't use any external GUI framework, but you may find other samples demonstrating AIS_ViewController usage with popular GUI frameworks like Qt/QtWidgets, QtQuick/QML, GTK, MFC and some others.

the only need is to unfrozen the display

AIS_ViewController here is just an interface to control mouse input (for camera manipulations), animations (of camera and objects) and viewer updates in a single place and in a well-defined order. You may solve this task at application level by (re)organizing viewer updates and camera manipulations in desired way.

The first step to unblock animation is to call AIS_Animation::UpdateTimer() one time within application-managed callback (using multimedia timers in GUI framework or similar approaches - so that animation updates will be interleaved with user input events) instead of calling it within the fixed loop (in this case user input events will arrive only after for-loop completion).

bioan m's picture

So, to unblock animation there is no need to deal with AIS_ViewController class, just need to be focused of a proper viewer update within app callback events. Correct me if I'm wrong!

Kirill Gavrilov's picture

bioan wrote:

So, to unblock animation there is no need to deal with AIS_ViewController class, just need to be focused of a proper viewer update within app callback events. Correct me if I'm wrong!

This is correct. AIS_Animation class exists since 2016 (OCCT 7.1.0), while AIS_ViewController has been added later in 2019 (OCCT 7.4.0).

bioan m's picture

Thank you very much, at least I'm following the correct path!!
In this case I must modify some basic code on my original base platform - an open source app based on OCCT - which I try to avoid to touch for many years!
Too bad we have no lighter solution like an attachment to AIS_InteractiveContext in this particular case of animation to deal with all these updates automatically, it would have made our work a lot easier !