Controlling your iModel in Unreal

Introduction

This tutorial looks at the various blueprint functions in the iTwin Unreal Datasmith Plugin you can use to manipulate your iModel in Unreal and builds a simple user interface with a scrubber for users to interact with your schedule animation in real-time. It also focuses on synchronizing the level sequence (used by actors with transform animations) with the ScheduledActor’s animations, which is necessary when you exported using the combine meshes option.

Info

Skill level:

Advanced

Duration:

30 minutes

Prerequisites

This tutorial assumes that you have:

1. Preparing actors in your scene

In the level editor where you see your imported data, you need to add some tags to specific actors so later code can find them easily. From the World Outliner at the top right of the editor, select the datasmith scene actor that was created when you imported your Datasmith content. Then in the Details panel, search for or scroll down to the Actor > Tags section and press the plus button to add a new tag, give it a value of “ExampleDatasmithScene1”.

If you selected the combine meshes option while exporting, you will have an actor named ScheduleActor as a child of your datasmith scene actor, Select it and give it a new tag with a value of “ExampleScheduleActor1”. It is not necessary to have done a combined-mesh export, however this tutorial will assume you have and write the code to synchronize the exported level sequence with the ScheduleActor driving animations in the combined mesh.

setting actor tags in the details panel

If you have not done so already, in the Content Browser, in the folder where you chose to import your datasmith content, go into the animations subfolder and drag-and-drop the ScheduleAnimation asset into the level to add the actor to the scene.

drag-and-drop-level-sequence

Make sure the new level sequence actor is selected, and in the Details Panel, give it an actor tag with a value of “ScheduleLevelSequence1”. Still in the Details Panel, find the Playback settings section, check Auto Play and set the Loop option to Loop Indefinitely.

set auto play

Using actor tags is one of several ways to make it easy to access actors in the level at runtime in our code. Now that you have them setup, you can use them in the code of the classes that will comprise your user interface.

2. Setting up our interface classes

In Unreal Engine, HUD (Heads Up Display) classes are used to draw the 2-dimensional interface overlaying the rendered scene. The HUD is controlled by the PlayerController it is attached to, and both are provisioned by the engine’s active GameMode. In this tutorial a basic gamemode setup will be created so when you run your project, the interface will have a scrubber timeline, some displayed information about the scene, and inputs to control the schedule animation playback.

Start by pressing the Add/Import button in the Content Browser, and under User Interface pick Widget Blueprint to create a new Widget Blueprint. Name it ExampleTimelineWidget.

The names used in this tutorial are arbitrary. You can use any name as long as you connect the classes correctly.
create a widget blueprint by using the add/import button in the Content Browser

Next, create a new GameModeBase blueprint by pressing the Add/Import button in the Content Browser again, this time choosing Blueprint Class at the top, under create Create Basic Asset. In the Pick Parent Class dialog, use the search menu to find the GameModeBase class. Some, but not all, classes may also be selected from the Common Classes list at the top of the dialog. Name your new GameModeBase subclass ExampleGameModeBase. In the same way create a new PlayerController blueprint, ExamplePlayerController, and a new HUD blueprint, ExampleHUD, as well. Double-click the newly created ExampleHUD asset in the content editor to open the blueprint editor.

In your HUD’s blueprint event graph, add a Create Widget node to the Begin Play event and call the Add to Viewport function on the created widget reference. Set the class parameter of the Create Widget node to the blueprint widget class you created, ExampleTimelineWidget. The resulting node graph for the Begin Play event should look as follows.

screenshot of blueprint code for ExampleHUD class

Go back to the Content Browser in the main level editor window and open the ExamplePlayerController class. In the Begin Play event, add a Get All Actors Of Class with Tag node and get all Scheduled Actors with the tag you added earlier, “ExampleScheduleActor1”. Get the element at index 0 (the first) element of the array, since we know we have one such actor in the scene, and set it as the value for a new variable, Scene. You can drag the output pin of the array indexing Get node, and release in the graph space to open the context menu and at the top select Promote to variable to do this quickly.

Then add a Get HUD node and a Cast to ExampleHUD node after the intialization of Scene. Store the ExampleHUD instance in a new variable, PlayerHUD.

screenshot of player controller's beginplay

Create a new boolean variable IsMouseCaptured, and right-click in the event graph, search in the context menu for “spacebar” to add the space bar input event. Then add the following blueprint code to use spacebar to toggle between controlling our spectator pawn or the mouse.

See the Unreal Engine input management documentation for better ways of managing input. Using a direct keyboard event is done for simplicity and there are more robust ways of reacting to input.
screenshot of player controller's spacebar event implementation

Finally, set your world to use these new blueprint classes, so that when the level is played, your widget is in the viewport. Click the Settings icon in the top bar, and click World Settings in its dropdown. In the World Settings panel, find the Game Mode section. There, see the GameMode Override dropdown value to your newly created ExampleGameModeBase. Then expand the Selected GameMode options by clicking the arrow to the left of the label, and set the HUD and PlayerController classes to the ones you created.

screenshot of gamemode override settings
In addition to a game mode override for local testing, you may also want to set the default GameMode in the project settings' Maps & Modes section.

Now that HUD and widget spawn when play begins, you need to add some controls to your widget to be able to see it, and then you will be able to hook up those controls to your iTwin’s animation.

3. Laying out the HUD

In the Content Browser, double-click the ExampleTimelineWidget asset you created to open the widget editor. From the Palette on the left, drag and drop a Slider control into the widget designer viewport. Make sure it is selected, and use the scale handles to scale it across the bottom edge of the viewport to use as our scrubber. Additionally, place a Text control at each the left and right end of the slider, and one more above the middle of the slider. You will show your iTwin’s schedule start, end, and current time of the animation in these text controls, updating the current time every tick.

In the Hierarchy panel on the left of the editor, rename your newly placed controls respectively to, Scrubber, StartTime, EndTime, and CurrentTime, and in the top-right corner of the Details panel make sure the Is Variable checkbox is checked so you can reference your controls in the event graph.

the location of the Is Variable checkbox in the interface

To start scripting your placed controls, switch to the blueprint graph by pressing the Graph button in the top right of the window in the blueprint widget editor. On the widget’s Construct event, first store both the “ExampleScheduleActor1”, and the “ScheduleLevelSequence1” tagged actors stored to variables in your script. Then call the StartTime text control’s Set Text function and set it to the formatted date text of the ScheduledActor’s AnimationStartTime. Do the same for EndTime but with the ScheduledActor’s AnimationEndTime.

If you did not perform an export with the combine meshes option, the ScheduledActor's static blueprint function GetSeparatedExportSceneData can be called with your Datasmith Scene Actor to receive the ScheduleStartDateTime, ScheduleEndDateTime, and ScheduleTicksPerSecond.

Then add a custom event to the event graph by right-clicking in the graph and search for Add Custom Event in the context menu. Name this event "Set Correct Level Sequence End", and invoke the event after the variables are initialized in the Construct event that you have been building up.

screenshot of timeline widget's construct event

Before you implement that custom event, in the tick event update the CurrentText control with the current time of the ScheduleActor. If you did not combine meshes during export, you can instead use the static plugin blueprint function GetScheduleDateTimeFromActiveSequence to get the current date given the level sequence actor. Then, after having set the text, calculate the proportion of the animation played and update Scrubber’s value to match, so that it moves along with the animation every tick.

screenshot of timeline widget's tick event

Now you should implement the custom event, SetCorrectLevelSequenceEnd. This step is necessary to synchronize the exported level sequence with the ScheduledActor, because the DatasmithSDK does not currently have a way to set the end frame of a level sequence. When an export is created with the combine meshes option, only transform data is put in the level sequence, therefore the true end frame which is typically a non-transform frame (i.e. visibility, color, etc, frame) is lost. To compensate for this, you can calculate the end frame manually from the metadata, and set the correct end time at startup. You could also set the end of the level sequence asset itself, but a one-time computation at startup is not expensive and does not need to be recalculated in the event of a re-export of your Datasmith content.

To calculate it, divide the schedule date range using the AnimationTicksPerSecondScale variable to find how many 24FPS frames occur during the equivalent game time, which can then be used to set the level sequence end frame from. In this example int64 is used in order to have the same resolution as DateTime which is internally a single int64. Additionally, some constants from Unreal’s native ETimespan enumeration are reproduced to calculate the amount of ticks from the amount of seconds using only available blueprint nodes.

screenshot of player timeline widget's custom event 'set correct level sequence end'

Now, when you hit the Play button to watch the scene unfold, your animations should play and loop, with the scrubber moving and the date text updating throughout. You should also be able to press the spacebar to get control of the mouse in order to move the scrubber. Moving the scrubber however, won’t do anything. That is your next step.

4. Scripting the scrubber

Go back to the widget designer by hitting the Designer button at the top-right of the widget blueprint graph. Select your scrubber control in the Hierarchy tab, and in the Details tab scroll all the way to the bottom to the Events section. Click the green plus On Mouse Capture Begin, On Mouse Capture End, and On Value Changed buttons to add those events. Each time you press one of the buttons, you will be taken to where the event was added in the graph, go back to the designer each time until all three events have been added.

the green plus buttons that let you add callbacks for scrubber controls to the event graph

On mouse capture, pause the animation, so that the scene does not keep playing while a user is trying to scrub through it. In the On Mouse Capture Begin event, set the ScheduleActor’s AnimationIsPlaying variable to false, and call the Pause function on the level sequence. In the On Mouse Capture End event, do the opposite, setting AnimationIsPlaying to true, and calling the Play function on the level sequence.

the implementation of On Mouse Capture events for the ExampleTimelineWidget for playback control

Finally, you must implement the On Value Changed event by converting the scrubber value from between 0 and 1 to the appropriate date in the schedule for setting the ScheduleActor’s AnimationTime, and you must calculate the proportional time in the level sequence and call its SetPlaybackPosition function. The necessary math for transforming from the value between 0 and 1 to the level sequence seconds and schedule date time can be seen in the following blueprint code.

the implementation of On Value Changed for the ExampleTimelineWidget

Conclusion

With your scene scrubber now working, you should be able to start extending the code with your own inputs and controls, such as using custom interface control styles, or new inputs like pause buttons, etc. For a full list of available blueprint functions in the iTwin Unreal Datasmith Plugin, see the Blueprint API reference.

If you have an idea for something that could improve your workflow and belongs in the plugin, visit our ideas portal.

More resources that you may like

Extended functionality and workflow descriptions in iTwin Exporter for Datasmith
Follow this tutorial to get iModel data into the Datasmith format as a combined mesh
See the official example HUD in Unreal to learn some HUD design techniques and best practices