Skip to content

Commit

Permalink
new release (source = 7040892d)
Browse files Browse the repository at this point in the history
  • Loading branch information
charleschandesris committed Dec 4, 2024
1 parent 29fe79e commit 92282e3
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 27 deletions.
19 changes: 19 additions & 0 deletions Documentation/InstallationAndUse/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,22 @@ It is also possible to load "external" tilesets (ie. non-iModels, like tileset c
This actor can be found in the content browser inside folder `Plugins/iTwin for Unreal C++ Classes/ITwinCesiumRuntime/Public`.<br>
- In the actor's Details panel, in the `Cesium` section, fill the `Source`, `Url`, `Ion Asset ID`, `Ion Access token` fields depending on the source of your tileset.

## Configuration file
Some advanced settings can be configured in a ".ini" configuration file located at `C:\Users\<YOUR_USERNAME>\AppData\Local\Unreal Engine\Engine\Config\UserEngine.ini`.
Be careful to use the exact subfolders as shown above, as Unreal Engine uses other configuration files at similar locations, but only editing this one will allow the plugin to access the settings.

The setting entries must be added to a `[/Script/ITwinRuntime.ITwinIModelSettings]` section as shown in this example (here with all default values):

```
[/Script/ITwinRuntime.ITwinIModelSettings]
CesiumMaximumCachedMegaBytes=1024
IModelMaximumCachedMegaBytes=4096
IModelCreatePhysicsMeshes=true
TilesetMaximumScreenSpaceError=16.0
Synchro4DMaxTimelineUpdateMilliseconds=50
Synchro4DQueriesDefaultPagination=10000
Synchro4DQueriesBindingsPagination=30000
```
Use a semi-colon `;` at the beginning of a line to comment it out.

See the plugin's `ITwinForUnreal\Source\ITwinRuntime\Public\ITwinIModelSettings.h` file for a detailed documentation of all available settings.
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ class AITwinIModel::FImpl
{
if (EState::NotStarted == State || EState::Finished == State || EState::StoppedOnError == State)
{
Cache.Reset(); // reinit, we may have a new changesetId for example
Cache.Uninitialize(); // reinit, we may have a new changesetId for example
UE_LOG(LogITwin, Display, TEXT("Elements metadata queries (re)starting..."));
DoRestart();
}
Expand All @@ -516,7 +516,7 @@ class AITwinIModel::FImpl
RequestInfo.emplace(Owner.WebServices->InfosToQueryIModel(Owner.ITwinId, Owner.IModelId,
Owner.ResolvedChangesetId, ECSQLQueryString, QueryRowStart, QueryRowCount));
QueryRowStart += QueryRowCount;
auto const Hit = Cache.LookUp(*RequestInfo, Mutex);
auto const Hit = Cache.IsValid() ? Cache.LookUp(*RequestInfo, Mutex) : std::nullopt;
if (Hit)
{
CurrentRequestID.Empty();
Expand Down Expand Up @@ -584,7 +584,8 @@ class AITwinIModel::FImpl
}
else
{
Cache.Write(*RequestInfo, std::get<0>(QueryResult), true, Mutex);
if (Cache.IsValid())
Cache.Write(*RequestInfo, std::get<0>(QueryResult), true, Mutex);
auto Reader = TJsonReaderFactory<TCHAR>::Create(std::get<0>(QueryResult));
if (!FJsonSerializer::Deserialize(Reader, JsonObj))
JsonObj.Reset();
Expand Down Expand Up @@ -612,6 +613,9 @@ class AITwinIModel::FImpl
UE_LOG(LogITwin, Display, TEXT("Total %s retrieved from %s: %d."), *BatchMsg,
/*likely all retrieved from same source...*/bFromCache ? TEXT("cache") : TEXT("remote"),
TotalRowsParsed);
// This call will release hold of the cache folder, which will "often" allow reuse by cloned
// actor when entering PIE (unless it was not yet finished downloading, of course)
Cache.Uninitialize();
State = EState::Finished;
}
return true;
Expand Down Expand Up @@ -680,17 +684,19 @@ void AITwinIModel::FImpl::MakeTileset(std::optional<FITwinExportInfo> const& Exp
Tileset->SetActorLabel(Owner.GetActorLabel() + TEXT(" tileset"));
#endif
Tileset->AttachToActor(&Owner, FAttachmentTransformRules::KeepRelativeTransform);

auto const Settings = GetDefault<UITwinIModelSettings>();
// TODO_GCO: Necessary for picking, unless there is another method that does
// not require the Physics data? Note that pawn collisions need to be disabled to
// still allow navigation through meshes (see SetActorEnableCollision).
//Tileset->SetCreatePhysicsMeshes(false);
Tileset->SetCreatePhysicsMeshes(Settings->IModelCreatePhysicsMeshes);
Tileset->SetMaximumScreenSpaceError(Settings->TilesetMaximumScreenSpaceError);
// connect mesh creation callback
Tileset->SetMeshBuildCallbacks(SceneMappingBuilder);
Tileset->SetGltfTuner(GltfTuner);
Tileset->SetTilesetSource(EITwinTilesetSource::FromUrl);
Tileset->SetUrl(CompleteInfo.MeshUrl);

auto const Settings = GetDefault<UITwinIModelSettings>();
Tileset->MaximumCachedBytes = std::max(0ULL, Settings->CesiumMaximumCachedMegaBytes * 1024 * 1024ULL);
// Avoid unloading/reloading tiles when merely rotating the camera
//Tileset->EnableFrustumCulling = false; // implied by SetUseLodTransitions(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,23 @@ FJsonQueriesCache::FJsonQueriesCache(UObject const& Owner) : Impl(MakePimpl<FImp
check(!Owner.HasAnyFlags(RF_ClassDefaultObject));
}

bool FJsonQueriesCache::IsValid() const
{
return !Impl->PathBase.IsEmpty();
}

int FJsonQueriesCache::CurrentTimestamp() const
{
return (Impl->PathBase.IsEmpty() ? (-1) : Impl->RecorderTimestamp);
return (IsValid() ? Impl->RecorderTimestamp : (-1));
}

void FJsonQueriesCache::Reset()
void FJsonQueriesCache::Uninitialize()
{
if (IsValid() && Impl->Manager)
{
Impl->Manager->MarkAsUsed(*this, Impl->Entry, FJsonQueriesCacheManager::EUseFlag::Unloading);
}
Impl->Manager.reset();
Impl->PathBase.Empty();
QueriesCache::FSessionMap Tmp;
Impl->SessionMap.swap(Tmp);
Expand All @@ -305,7 +315,7 @@ void FJsonQueriesCache::Reset()

void FJsonQueriesCache::ClearFromDisk()
{
if (!Impl->PathBase.IsEmpty())
if (IsValid())
IFileManager::Get().DeleteDirectory(*Impl->PathBase, /*requireExists*/false, /*recurse*/true);
}

Expand Down Expand Up @@ -347,27 +357,18 @@ bool FJsonQueriesCache::Initialize(FString CacheFolder, EITwinEnvironment const
{
UE_LOG(ITwinQuery, Error, TEXT("Error loading cache: %s\nClearing cache folder %s to avoid mixing corrupt and new data"), *ParseError, *CacheFolder);
IFileManager::Get().DeleteDirectory(*CacheFolder, /*requireExists*/false, /*recurse*/true);
Reset();
Uninitialize();
// was reset and folder deleted, but set them up again to use for recording what we will (re-)download
Impl->PathBase = CacheFolder;
ensure(IFileManager::Get().MakeDirectory(*CacheFolder, /*recurse*/true));
return false;
}
}

void FJsonQueriesCache::Uninitialize()
{
if (Impl->Manager)
{
Impl->Manager->MarkAsUsed(*this, Impl->Entry, FJsonQueriesCacheManager::EUseFlag::Unloading);
Impl->Manager.reset();
}
}

FJsonQueriesCache::~FJsonQueriesCache()
{
if (!ensure(!Impl->Manager))
Uninitialize(); // kind of a safety, but in UE dtor are not even always called...
if (Impl->Manager) // this case happens when in Editor only (not PIE) then closing Unreal
Uninitialize();
}

bool FJsonQueriesCache::LoadSessionSimulation(FString const& SimulateFromFolder)
Expand Down Expand Up @@ -410,6 +411,8 @@ void FJsonQueriesCache::Write(TSharedRef<FJsonObject>& JsonObj, int const Respon
FString const& ContentAsString, bool const bConnectedSuccessfully, ITwinHttp::FMutex& Mutex,
int const QueryTimestamp)
{
if (!ensure(IsValid()))
return;
if (Impl->bIsRecordingForSimulation)
{
ensure(QueryTimestamp != -1);
Expand Down Expand Up @@ -519,7 +522,7 @@ void FJsonQueriesCache::ToJson(FHttpRequestPtr const& Req, TSharedRef<FJsonObjec

void FJsonQueriesCache::RecordQuery(FHttpRequestPtr const& Request, ITwinHttp::FMutex& Mutex)
{
if (!Impl->PathBase.IsEmpty())
if (IsValid())
{
ITwinHttp::FLock Lock(Mutex);
auto JsonObj = MakeShared<FJsonObject>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,15 @@ class FJsonQueriesCache
explicit FJsonQueriesCache(UObject const& Owner);
~FJsonQueriesCache();

void Uninitialize();

bool IsValid() const;
/// Actually initializes the cache for your "session"
[[nodiscard]] bool Initialize(FString CacheFolder, EITwinEnvironment const Environment,
FString const& DisplayName, bool const bIsRecordingForSimulation = false);
/// Reset to uninitialized state (as if just default-constructed), clearing memory in the process
/// (but not the disk folder! see ClearFromDisk)
void Reset();
/// (but not the disk folder! see ClearFromDisk). Must be called by an owner UObject when it is about to
/// become garbage-collectable, for example in AActor::EndPlay, because when the owner's destructor will
/// be called, it may be too late already to use the static(!) data in the cache implementation details.
void Uninitialize();
[[nodiscard]] bool LoadSessionSimulation(FString const& SimulateFromFolder);
/// Deletes the filesystem folder containing the cache data
void ClearFromDisk();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ void FReusableJsonQueries<SimultaneousRequestsT>::UninitializeCache()
template<uint16_t SimultaneousRequestsT>
void FReusableJsonQueries<SimultaneousRequestsT>::ClearCacheFromMemory()
{
Impl->Cache.Reset();
Impl->Cache.Uninitialize();
Impl->ReplayMode = ReusableJsonQueries::EReplayMode::None;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,15 @@ void FITwinSchedulesImport::FImpl::RequestTransfoAssignment(ReusableJsonQueries:
}
else if (TransformListIncomplete.second == false)
CompletedProperty(Sched, TransformAssignment.Bindings, Lock, TEXT("Path3dAssign"));
//else: incomplete but already queried, just wait for completion
else
{
// incomplete but already queried: wait for completion, but also transfer responsibility
// of checking and notifying the completed bindings (same reason as above)
std::copy(TransformAssignment.Bindings.cbegin(), TransformAssignment.Bindings.cend(),
std::back_inserter(
Sched.Animation3DPaths[TransfoAsPath.Animation3DPathInVec].Bindings));
TransformAssignment.Bindings.clear();
}
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <functional>
#include <mutex>
#include <set>
#include <vector>

constexpr uint16_t SimultaneousRequestsAllowed = 6;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,35 @@ class ITWINRUNTIME_API UITwinIModelSettings : public UDeveloperSettings
Category = "iTwin")
int IModelMaximumCachedMegaBytes = 4096;

/// Whether to enable point-and-click selection on iModel meshes: this requires the creation of special
/// "physics" meshes that can adversely impact performance and memory footprint on large models. Set to
/// false if you know you won't need collision nor selection in the 3D viewport.
UPROPERTY(
Config,
EditAnywhere,
BlueprintReadOnly,
Category = "iTwin")
bool IModelCreatePhysicsMeshes = true;

/**
* From AITwinCesium3DTileset::MaximumScreenSpaceError:
*
* The maximum number of pixels of error when rendering this tileset.
*
* This is used to select an appropriate level-of-detail: A low value
* will cause many tiles with a high level of detail to be loaded,
* causing a finer visual representation of the tiles, but with a
* higher performance cost for loading and rendering. A higher value will
* cause a coarser visual representation, with lower performance
* requirements.
*/
UPROPERTY(
Config,
EditAnywhere,
BlueprintReadOnly,
Category = "iTwin")
double TilesetMaximumScreenSpaceError = 16.0;

/// Split applying animation on Elements among subsequent ticks to avoid spending more than this amount
/// of time each time. Visual update only occurs once the whole iModel (?) has been updated, though.
UPROPERTY(
Expand Down

0 comments on commit 92282e3

Please sign in to comment.