Is World Origin Rebasing working?

Hi, I am using World Composition and enabled World Origin Rebasing in World Settings. My world is close to 64kmx64km with 16 tiles. Since I enabled origin rebasing, the engine goes with tons of logs like the following:

LogLevel: WORLD TRANSLATION BEGIN {-397825058, 1352878516, 0} -> {-387412943, 1366202854, 0}
LogLevel: WORLD TRANSLATION END {-387412943, 1366202854, 0} took 0.0589 ms
LogLevel: WORLD TRANSLATION BEGIN {-387412943, 1366202854, 0} -> {-377000828, 1379527192, 0}
LogLevel: WORLD TRANSLATION END {-377000828, 1379527192, 0} took 0.0841 ms
LogLevel: WORLD TRANSLATION BEGIN {-377000828, 1379527192, 0} -> {-366588713, 1392851530, 0}
LogLevel: WORLD TRANSLATION END {-366588713, 1392851530, 0} took 0.0913 ms
LogLevel: WORLD TRANSLATION BEGIN {-366588713, 1392851530, 0} -> {-356176598, 1406175868, 0}
LogLevel: WORLD TRANSLATION END {-356176598, 1406175868, 0} took 0.0721 ms
LogLevel: WORLD TRANSLATION BEGIN {-356176598, 1406175868, 0} -> {-345764483, 1419500206, 0}
LogLevel: WORLD TRANSLATION END {-345764483, 1419500206, 0} took 0.1010 ms
LogLevel: WORLD TRANSLATION BEGIN {-345764483, 1419500206, 0} -> {-335352368, 1432824544, 0}
LogLevel: WORLD TRANSLATION END {-335352368, 1432824544, 0} took 0.0730 ms
LogLevel: WORLD TRANSLATION BEGIN {-335352368, 1432824544, 0} -> {-324940253, 1446148882, 0}
LogLevel: WORLD TRANSLATION END {-324940253, 1446148882, 0} took 0.0631 ms
LogLevel: WORLD TRANSLATION BEGIN {-324940253, 1446148882, 0} -> {-314528138, 1459473220, 0}
LogLevel: WORLD TRANSLATION END {-314528138, 1459473220, 0} took 0.0769 ms

And this is taking an enormous amount of time, I didn’t even let it finish, it was still not complete after several minutes. I do not have lots of actors into the world, it is mostly empty. Is this a bug? I am using 4.18.2.

Update: I looked at the code and rebasing is occuring on UWorld::Tick() upon the following condition:

if (OriginLocation != RequestedOriginLocation)
{
    SetNewWorldOrigin(RequestedOriginLocation);
}

Problem is that RequestedOriginLocation is constantly changing, so it never ends…

Looking further into it, here is what is happening:
UWorld::Tick() is calling UpdateStreamingState() itself calling EvaluateWorldOriginLocation(ViewLocation), this leading to calling RequestNewWorldOrigin() with a new value, hence the constant recalculation of world origin. ViewLocation is not changing value though, so this is the following code into UWorldComposition::EvaluateWorldOriginLocation which is causing the constant rebasing:

    // Request to shift world in case current view is quite far from current origin
    if (Location.SizeSquared() > FMath::Square(RebaseOriginDistance))
    {
        OwningWorld->RequestNewWorldOrigin(FIntVector(Location.X, Location.Y, Location.Z) + OwningWorld->OriginLocation);
    }

Hi,

Could you check what is value of RebaseOriginDistance ? By default it should be set to (HALF_WORLD_MAX1*0.5f). But it also could be overridden in project config files. Check that your project ini sets it to sensible value if you override it. This is distance threshold from camera to world origin.

RebaseOriginDistance is the default value (HALF_WORLD_MAX*0.5) so 524 287.500
I breakpointed into “if (Location.SizeSquared() > FMath::Square(RebaseOriginDistance))” at some pointing during the rebasing and had these values:

Location = {X=10412115.0 Y=13324338.0 Z=0.000000000 }
so Location.SizeSquared() = (10412115.0² + 13324338.0²) = 285 950 121 911 469
and FMath::Square(RebaseOriginDistance) = 524 287.5² = 274 877 382 656.25

Since neither location nor RebaseOriginDistance are varying the condition is always true. RebaseOriginDistance is way lower than the location distance. I have tried to increase RebaseOriginDistance to half-half of my world size (being 6 451 200 squared, so RebaseOriginDistance=1 612 800) but RebaseOriginDistance² is still way lower than Location.SizeSquared(). I think this code should be reviewed, I am not sure it is right, in particular the calculation of Location which I think must be made taking into account the new origin.
In fact there may be a problem inside UWorldComposition::UpdateStreamingState() when calculating CentroidLocation: the calculation does not take into account the new origin, so ends up with a huge distance that will always be higher than RebaseOriginDistance.

When the origin is rebased ViewLocation should become nearly zero. Basically the origin rebasing operation just loops over all actors in the world and shifts them. For some reason actor owning the camera is not shifted. Anything special about your game player class? If it’s your custom class does it properly process AActor::ApplyWorldOffset() function call?

This is very possible because at game startup, I am loading a specific tile and only then I am adding the player to that tile. If this is not the right way to do then how to do it properly?
There is something I don’t understand in the way World Composition works, because at startup it is looking for APlayerController::GetPlayerViewPoint() but since there is no tile loaded at startup, how is it possible to have a player with a valid viewpoint? So if GetPlayerViewPoint() returns (0,0,0) then World Composition loads tile x0_y0 which is not what I want. So I am doing something quite convoluted to make things work the way I need:

  1. in my override of APlayerController::GetPlayerViewPoint(), I first return a very distant value so the engine does not try to load any tile at startup
  2. in my override of GameMode::StartPlay, I initiate LoadStreamingLevel of the tile I want, and I also make GetPlayerViewPoint() return a specific value inside that tile so the engine will not unload my tile on tick
  3. then, when the tile is loaded and shown, only then I add my First Player character and restore GetPlayerViewPoint() to return the current player position.

But this does not seem to work well with rebasing… any help would be appreciated !

So your code returns constant location from GetPlayerViewPoint() and engine tries to reach it by rebasing origin but never comes close it, since view point location never changes, and always stays far. Your code should respond to ApplyWorldOffset() call and shift that far constant value.

But it will be better to just pause level streaming while your game is working out where player should be spawned in the world. However I don’t see existing mechanism in code that does this, but you could add your own. Like a bool property in world composition object that will be checked inside UWorldComposition::UpdateStreamingState().

That makes sense, I will implement ApplyWorldOffset() and test, thanks. Btw, it would be great if we could provide our own subclasses for UWorldComposition and UWorld without having to modify the engine code.

Update: I implemented a custom ApplyWorldOffset() in my Player Controller class and now rebasing is working fine! However I am disabling rebasing by default and enabling it only on BeginPlay of my Persistent Level (using World Settings access).