This step reviews how to initialize and use HOOPS/MVO constant framerate functionality.
HOOPS/MVO constant framerate support centers around the creation of a list of 'simplification types' that are used in order to maintain a requested framerate when redrawing the scene. Each simplification types is associated with drawing the scene using a particular set of rendering modes (such as shaded, wireframe, bounding box, Level of Detail, etc...) Generally, the first type in the list will be used, and if the constant framerate logic determines that the target framerate cannot be met, it will switch to the next type in the list. Obviously, each sucessive type in the list should be 'simpler' (faster to draw) then the previous.
The HOOPS/MVO class that deals with constant framerate functionality is called HConstantFramerate. To initialize CFR, we get a handle to the object, pass in our simplification list, and call the HConstantFramerate::Init. The constant framerate logic is started by calling HConstantFramerate::Start. Let's perform this initialization work in a new method called HVisualizeView::OnEnableFramerate :
There is already an HConstant Framerate object associated with an HBaseView object, and we begin by getting a local pointer to the object:
{HConstantFrameRate *cframerate = GetConstantFrameRateObject();HConstFRSimpType **SimpList;
Next we setup the default simplification list. For our tutorial, we'll have three simplification levels. The 1st is 'HFrNoSimp' which is simply the full-resolution model, the 2nd is HFrLodThreshold which will specifies view-dependent LOD switching, and the 3rd is HFrSolidBBox which will causes a bounding box to be drawn in lieu of the shell. The HFrLodThreshold type has the nice effect where objects closer to the camera will be drawn with higher fidelity, and those farther away will be drawn with lower fidelity.
SimpList = new HConstFRSimpType *[3];SimpList[0] = new HFrNoSimp;SimpList[1] = new HFrLodThreshold(0,6);SimpList[2] = new HFrSolidBBox();
Now we initialize the CFR logic by setting the target framerate (the units are frames/second), and passing in the simplifciation list and list count:
cframerate->Init(30.0, SimpList, 3);
Finally, we start the framerate logic:
cframerate->Start();}
Now, let's add a new GUI button that calls CVisualizeView::OnEnableFramerate when selected. We'll have that method call our framerate initialization method, and then set up a CFR timer:
{((HVisualizeView *)m_pHView)->OnEnableFramerate();SetTimer(H_TIMER_ID_CONST_FRAMERATE, 200, 0);}
Experiment with the new CFR logic in the Visualize app by loading in a large assembly such as the train_chassis.hsf or formula1.hsf, building LODs, and enabling constant framerate. Also try using the power_plant.hsf dataset located in the same directory as this document (<hoops>/docs/BuildingApplications/tutorials/lmv)
Constant framerate logic is disabled by first stopping its operation, and then shutting it down. We'll add a new method called HVisualizeView::OnDisableFramerate which handles this logic:
{HConstantFrameRate *cframerate = GetConstantFrameRateObject();cframerate->Stop();cframerate->Shutdown();Update();}
Now, let's add a new GUI button that calls CVisualizeView::OnDisableFramerate when selected. We'll have that method call our HVisualizeView::OnDisable framerate method:
You may have noted that if you start interacting with the scene and then pause (with the mouse still held down), the scene stays at the (potentially) lower visual fidelity set by the CFR logic. If you wanted the scene to 'sweeten' during a pause, this could be achieved by putting code in our CFR timer that checks for user inactivity and 'bumps up the detail level'. We can do this by adding an OnTimer method to our CVisualizeView object:
We first check to see if the timer event type is H_TIMER_ID_CONST_FRAMERATE. If it is, then we kill the timer and get a pointer to the CFR object:
void CVisualizeView::OnTimer(UINT nIDEvent){if (nIDEvent == H_TIMER_ID_CONST_FRAMERATE){KillTimer(nIDEvent);HConstantFrameRate *cframerate = m_pHView->GetConstantFrameRateObject();
If constant framerate is active and there has been no activity for at least .3 seconds, then we bump up the detail level. We also reset the timer:
if (cframerate->GetActive()){pThisView = this;if (m_pHView->GetIdleTime() > 0.3f && cframerate->GetNoActivity())cframerate->IncreaseDetailTemp();// reset the timerSetTimer(H_TIMER_ID_CONST_FRAMERATE,200,0);}
We clear out the flag that indicates something has changed:
cframerate->SetNoActivity(true);}}
You may notice something new that's 'wrong' with the constant framerate logic, or at least uncomfortable from an end-user standpoint. If you pause during interaction (which results in the scene redrawing due to the bump in detail level described above) and then try to interact again, you will not be able to do so until the scene has finished redrawing at the new detail level; this redraw could take a long time if the scene is being bumped up to the full resolution model. In order to immediately interact during this redraw, the CFR logic would need to check for user interaction during the update, and then 'interrupt' the update of some user interaction occurred.
HOOPS/3dGS has a mechanism for interrupting the update by means of an 'event checker'. You define an event checker callback and pass the pointer of that callback function to HOOPS. In the event checker, you check if an 'interruptable' event (like a mouse click/move) has occurred, and if so, you flag HOOPS/3dGS that you want to interrupt the update. Let's define the event checker function such that it flags HOOPS to interrupt the update if the mouse has moved while the left mouse button is down:
{MSG msg;if (pThisView->m_hWnd != 0)
We are only interested in MouseMove events, so we 'peek' at the message queue to see if a move has occurred:
if (PeekMessage (&msg, pThisView->m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE)){switch (msg.message){
If it is a MouseMove and the Left button is down, we queue a 'special event' which breaks the update cycle, and tell the view that the update has been interrupted:
case WM_MOUSEMOVE:int state = GetAsyncKeyState(VK_LBUTTON);if (state & 32768){HC_Queue_Special_Event ("FRAMERATE", "INTERRUPT"); //any special event breaks the update cyclepThisView->m_pHView->SetUpdateInterrupted(true);}return;break;}}
We are also interested in MouseWheel events , so we 'peek' at the message queue for this case as well:
else if (PeekMessage (&msg, pThisView->m_hWnd, WM_MOUSEWHEEL, WM_MOUSEWHEEL, PM_NOREMOVE)){HC_Queue_Special_Event ("FRAMERATE", "INTERRUPT");pThisView->m_pHView->SetUpdateInterrupted(true);}elsereturn;}
Now we have to set the event checker. A good place to do this is in CVisualizeView::OnInitialUpdate:
Now reload the dataset, build LODs, enable CFR, and interact with the scene. Try orbiting, then pause during the orbit with the mouse still down (notice the scene will start to 'sweeten'), and then start to orbit again. You will be able to resume orbiting without a large delay.
For more detailed information on how to customize and use constant framerate support, refer to constant framerate section of the HOOPS/MVO Programming Guide.