Skip to content

Commit

Permalink
Version 2
Browse files Browse the repository at this point in the history
- Fix heightfield transforms when sending partially loaded
  landscapes to Houdini.
- Fix input landscape height offset issue.
- Fix HoudiniAssetComponent cook-on-transform-change regression.
  • Loading branch information
Van committed Mar 15, 2022
1 parent 64edf67 commit 8133c3f
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 67 deletions.
32 changes: 3 additions & 29 deletions Source/HoudiniEngine/Private/HoudiniInputTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1669,35 +1669,9 @@ FHoudiniInputTranslator::UploadHoudiniInputTransform(

// // Only apply diff for landscape since the HF's transform is used for value conversion as well
// FTransform CurrentTransform = InputLandscape->Transform;
const FTransform NewTransform = Landscape->ActorToWorld();

// // Only handle position/rotation differences
// FVector PosDiff = NewTransform.GetLocation() - CurrentTransform.GetLocation();
// FQuat RotDiff = NewTransform.GetRotation() - CurrentTransform.GetRotation();
//
// // Now get the HF's current transform
// HAPI_Transform HapiTransform;
// FHoudiniApi::Transform_Init(&HapiTransform);
//
// if ( HAPI_RESULT_SUCCESS != FHoudiniApi::GetObjectTransform(
// FHoudiniEngine::Get().GetSession(),
// InputLandscape->InputObjectNodeId, -1, HAPI_SRT, &HapiTransform))
// {
// bSuccess = false;
// break;
// }
//
// // Convert it to unreal
// FTransform HFTransform;
// FHoudiniEngineUtils::TranslateHapiTransform(HapiTransform, HFTransform);
//
// // Apply the position offset if needed
// if (!PosDiff.IsZero())
// HFTransform.AddToTranslation(PosDiff);
//
// // Apply the rotation offset if needed
// if (!RotDiff.IsIdentity())
// HFTransform.ConcatenateRotation(RotDiff);
// const FTransform NewTransform = Landscape->ActorToWorld();

const FTransform NewTransform = FHoudiniEngineRuntimeUtils::CalculateHoudiniLandscapeTransform(Landscape->GetLandscapeInfo());

// Convert back to a HAPI Transform and update the HF's transform
HAPI_TransformEuler NewHAPITransform;
Expand Down
64 changes: 39 additions & 25 deletions Source/HoudiniEngine/Private/HoudiniLandscapeTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1907,39 +1907,33 @@ FHoudiniLandscapeTranslator::OutputLandscape_ModifyLayer(
EditLayerType == HAPI_UNREAL_LANDSCAPE_EDITLAYER_TYPE_ADDITIVE))
return false;


// ----------------------------------------------------
// Calculate Tile location and landscape offset
// ----------------------------------------------------

int32 TargetMinX, TargetMaxX, TargetMinY, TargetMaxY;
TargetLandscapeInfo->GetLandscapeExtent(TargetMinX, TargetMinY, TargetMaxX, TargetMaxY);

// ----------------------------------------------------
// Calculate the draw location (in quad space) of
// where the Modify Layer should be drawn
// ----------------------------------------------------
FVector LandscapeBaseLoc = TargetLandscape->GetTransform().InverseTransformPosition(TargetLandscape->GetTransform().GetLocation());
FTransform HACTransform = HAC->GetComponentTransform();

// We need to unscale the TileTransform before concatenating it with the HAC transform.
FTransform UnscaledTileTransform = TileTransform;
UnscaledTileTransform.SetScale3D(FVector::OneVector);
// In order to determine the 'draw location' in quad space we have to do the following:
// 1. Calculate the heightfield's world space position
// 2. Calculate the transform of the heightfield _relative_ to the Landscape's origin
// 3. Convert the relative HF transform to a quad space coordinate
// 4. Final draw coordinate: Landscape's section base offset + relative quad space coordinate

// Calculate the tile's transform in quad space on the target landscape.
const FTransform LocalTileTransform = HACTransform * UnscaledTileTransform * TargetLandscape->GetTransform().Inverse();
FTransform UnscaledLandscapeTransform = TargetLandscapeTransform;
UnscaledLandscapeTransform.SetScale3D(FVector::OneVector);

const FVector LocalPos = LocalTileTransform.GetLocation();
TargetMinX += LocalPos.X;
TargetMinY += LocalPos.Y;
const FTransform TileWSTransform = TileTransform * HAC->GetComponentTransform();
const FTransform RelativeTileTransform = TileWSTransform * UnscaledLandscapeTransform.Inverse();

FVector LandscapeScale = TargetLandscapeTransform.GetScale3D();
const FVector RelativeTileCoordinate = RelativeTileTransform.GetLocation() / LandscapeScale;

// The layer location will be offset / drawn at the "min" location (on the target landscape) in quad space
const FVector LayerLoc = FVector(TargetMinX, TargetMinY, 0.f);
const FIntPoint LandscapeBaseLoc = TargetLandscape->GetSectionBaseOffset();

// Calculate the final draw coordinates

FIntPoint TargetTileLoc;

TargetTileLoc.X = FMath::RoundToInt(LayerLoc.X);
TargetTileLoc.Y = FMath::RoundToInt(LayerLoc.Y);
TargetTileLoc.X = LandscapeBaseLoc.X + FMath::RoundToInt(RelativeTileCoordinate.X);
TargetTileLoc.Y = LandscapeBaseLoc.Y + FMath::RoundToInt(RelativeTileCoordinate.Y);

FVector TileMin, TileMax;
TileMin.X = TargetTileLoc.X;
Expand All @@ -1948,8 +1942,6 @@ FHoudiniLandscapeTranslator::OutputLandscape_ModifyLayer(
TileMax.Y = TargetTileLoc.Y + LandscapeTileSizeInfo.UnrealSizeY - 1;
TileMin.Z = TileMax.Z = 0.f;

FTransform DestLandscapeTransform = TargetLandscapeProxy->LandscapeActorToWorld();

// NOTE: we don't manually inject a tile number in the object name. This should
// already be encoded in the TileName string.
FHoudiniPackageParams TilePackageParams = InPackageParams;
Expand Down Expand Up @@ -2434,6 +2426,28 @@ FHoudiniLandscapeTranslator::FindExistingLandscapeActor_Bake(
// }
}

if (OutWorld && OutLevel)
{
// Ensure that a LevelBounds actor is present in the sublevel. This is needed for world composition.
bool bHasLevelBounds = false;
for (TActorIterator<ALevelBounds> ActorItr(OutLevel->GetWorld()); ActorItr; ++ActorItr)
{
if (ActorItr->GetLevel() == OutLevel)
{
bHasLevelBounds = true;
break;
}
}

if (!bHasLevelBounds)
{
FActorSpawnParameters SpawnParms;
SpawnParms.OverrideLevel = OutLevel;
ALevelBounds* LevelBoundsActor = OutWorld->SpawnActor<ALevelBounds>(SpawnParms);
LevelBoundsActor->MarkPackageDirty();
}
}

return OutActor;
}

Expand Down
11 changes: 5 additions & 6 deletions Source/HoudiniEngine/Private/UnrealLandscapeTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,11 @@ FUnrealLandscapeTranslator::CreateHeightfieldFromLandscape(
TArray<float> HeightfieldFloatValues;
HAPI_VolumeInfo HeightfieldVolumeInfo;
FHoudiniApi::VolumeInfo_Init(&HeightfieldVolumeInfo);
//FTransform LandscapeTransform = LandscapeProxy->LandscapeActorToWorld();// LandscapeProxy->ActorToWorld();

// Get the actual transform of this proxy, not the landscape actor's transform!
FTransform LandscapeTM = LandscapeProxy->LandscapeActorToWorld();
FTransform ProxyRelativeTM(FVector(LandscapeProxy->LandscapeSectionOffset));
FTransform LandscapeTransform = ProxyRelativeTM * LandscapeTM;
// NOTE: The landscape actor's transform cannot be directly used to position the heightfield in Houdini since only a
// part of the full landscape may be loaded. We need to calculate the center of all the loaded tiles / landscape
// components.
FTransform LandscapeTransform = FHoudiniEngineRuntimeUtils::CalculateHoudiniLandscapeTransform(LandscapeProxy->GetLandscapeInfo());

FVector CenterOffset = FVector::ZeroVector;
if (!ConvertLandscapeDataToHeightfieldData(
Expand Down Expand Up @@ -482,7 +481,7 @@ FUnrealLandscapeTranslator::CreateHeightfieldFromLandscape(
//FMemory::Memzero< HAPI_TransformEuler >( HAPIObjectTransform );
LandscapeTransform.SetScale3D(FVector::OneVector);
FHoudiniEngineUtils::TranslateUnrealTransform(LandscapeTransform, HAPIObjectTransform);
HAPIObjectTransform.position[1] = 0.0f;
// HAPIObjectTransform.position[1] = 0.0f;

HAPI_NodeId ParentObjNodeId = FHoudiniEngineUtils::HapiGetParentNodeId(HeightFieldId);
FHoudiniApi::SetObjectTransform(FHoudiniEngine::Get().GetSession(), ParentObjNodeId, &HAPIObjectTransform);
Expand Down
9 changes: 7 additions & 2 deletions Source/HoudiniEngineEditor/Private/HoudiniEngineBakeUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
#include "ActorFactories/ActorFactoryClass.h"
#include "Containers/UnrealString.h"
#include "Components/AudioComponent.h"
#include "Engine/LevelBounds.h"
#include "Engine/WorldComposition.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "MaterialEditor/Public/MaterialEditingLibrary.h"
Expand Down Expand Up @@ -3587,7 +3588,7 @@ FHoudiniEngineBakeUtils::BakeLandscape(
// We force a PostEditChangeProperty event which will trigger world composition to properly detect
// newly created maps, if enabled.
FHoudiniEngineRuntimeUtils::DoPostEditChangeProperty(WorldSettings, "bEnableWorldComposition");
}
}
}

if (PackagesToSave.Num() > 0)
Expand Down Expand Up @@ -3789,7 +3790,6 @@ FHoudiniEngineBakeUtils::BakeLandscapeObject(
// To make matters worse, we can't manually load a newly created level into the editor as a _sublevel_
// since the whole WorldBrowser / LevelCollection API is private and the LevelEditor subsystem doesn't
// provide any functions to achieve this.
// ALandscapeProxy* NewLandscapeProxy = LandscapeInfo->MoveComponentsToLevel(TileActor->LandscapeComponents, TargetLevel);
ALandscapeProxy* NewLandscapeProxy = MoveLandscapeComponentsToLevel(LandscapeInfo, TileActor->LandscapeComponents, TargetLevel);

// We have now moved the landscape components into the new level. We can (hopefully) safely delete the
Expand Down Expand Up @@ -3823,6 +3823,11 @@ FHoudiniEngineBakeUtils::BakeLandscapeObject(
return false;
}
}
// Ensure target level bounds are up to date
if (IsValid(TargetLevel) && TargetLevel->LevelBoundsActor.IsValid())
{
TargetLevel->LevelBoundsActor->MarkLevelBoundsDirty();
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2016,7 +2016,7 @@ UHoudiniAssetComponent::OnUpdateTransform(EUpdateTransformFlags UpdateTransformF
if (!bCookOnTransformChange)
return;

if (GetComponentTransform().Equals(LastComponentTransform))
if (!GetComponentTransform().Equals(LastComponentTransform))
{
// Only set transform changed flag if the transform actually changed.
// WorldComposition can call ApplyWorldOffset with a zero vector (for example during a map save)
Expand Down
43 changes: 43 additions & 0 deletions Source/HoudiniEngineRuntime/Private/HoudiniEngineRuntimeUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "HoudiniEngineRuntimeUtils.h"
#include "HoudiniEngineRuntimePrivatePCH.h"
#include "HoudiniRuntimeSettings.h"
#include "LandscapeProxy.h"

#include "EngineUtils.h"
#include "Engine/EngineTypes.h"
Expand Down Expand Up @@ -480,6 +481,48 @@ FHoudiniEngineRuntimeUtils::MarkBlueprintAsModified(UActorComponent* ComponentTe
}
#endif

FTransform FHoudiniEngineRuntimeUtils::CalculateHoudiniLandscapeTransform(ULandscapeInfo* LandscapeInfo)
{
ALandscapeProxy* LandscapeProxy = LandscapeInfo->GetLandscapeProxy();
FTransform OutTransform = LandscapeProxy->GetTransform();

FVector LandscapeScale = OutTransform.GetScale3D();

// The final landscape transform that should go into Houdini consist of the following two components:
// - Shared Landscape Transform
// - Extents of all the loaded landscape components

// The houdini transform will always be in the center of the currently loaded landscape components.

FIntRect Extent;
Extent.Min.X = INT32_MAX;
Extent.Min.Y = INT32_MAX;
Extent.Max.X = INT32_MIN;
Extent.Max.Y = INT32_MIN;

LandscapeInfo->ForAllLandscapeComponents([&Extent](ULandscapeComponent* LandscapeComponent)
{
LandscapeComponent->GetComponentExtent(Extent.Min.X, Extent.Min.Y, Extent.Max.X, Extent.Max.Y);
});

FIntVector ExtentCenter(
(Extent.Min.X + Extent.Max.X)/2,
(Extent.Min.Y + Extent.Max.Y)/2,
1);

FVector ExtentMin = FVector(Extent.Min.X * LandscapeScale.X, Extent.Min.Y * LandscapeScale.Y, 1.0);
FVector ExtentMax = FVector(Extent.Max.X * LandscapeScale.X, Extent.Max.Y * LandscapeScale.Y, 1.0);

// Add section base offset to the landscape transform
FVector Loc = OutTransform.GetLocation();
Loc.X += ExtentCenter.X * LandscapeScale.X;
Loc.Y += ExtentCenter.Y * LandscapeScale.Y;

OutTransform.SetLocation(Loc);

return OutTransform;
}


#if WITH_EDITOR

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#pragma once

#include "LandscapeInfo.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/Class.h"
Expand Down Expand Up @@ -196,6 +197,12 @@ struct HOUDINIENGINERUNTIME_API FHoudiniEngineRuntimeUtils
static void MarkBlueprintAsModified(UActorComponent* ComponentTemplate);
#endif

// -------------------------------------------------
// Landscape utilities
// -------------------------------------------------

static FTransform CalculateHoudiniLandscapeTransform(ULandscapeInfo* LandscapeInfo);

// -------------------------------------------------
// Editor Helpers
// -------------------------------------------------
Expand Down
66 changes: 63 additions & 3 deletions Source/HoudiniEngineRuntime/Private/HoudiniInputObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ UHoudiniInputActor::GetActor() const
}

ALandscapeProxy*
UHoudiniInputLandscape::GetLandscapeProxy()
UHoudiniInputLandscape::GetLandscapeProxy() const
{
return Cast<ALandscapeProxy>(InputObject.LoadSynchronous());
}
Expand All @@ -384,6 +384,31 @@ UHoudiniInputLandscape::SetLandscapeProxy(UObject* InLandscapeProxy)
InputObject = LandscapeProxy;
}

int32 UHoudiniInputLandscape::CountLandscapeComponents() const
{
ALandscapeProxy* LandscapeProxy = GetLandscapeProxy();
if (!IsValid(LandscapeProxy))
{
return false;
}

ULandscapeInfo* LandscapeInfo = LandscapeProxy->GetLandscapeInfo();
if (!IsValid(LandscapeInfo))
{
return false;
}

int32 NumComponents = 0;
LandscapeInfo->ForAllLandscapeProxies([&NumComponents](ALandscapeProxy* Proxy)
{
if (IsValid(Proxy))
{
NumComponents += Proxy->LandscapeComponents.Num();
}
});
return NumComponents;
}

ABrush*
UHoudiniInputBrush::GetBrush() const
{
Expand Down Expand Up @@ -1559,19 +1584,54 @@ bool UHoudiniInputLandscape::ShouldTrackComponent(UActorComponent* InComponent)
return InComponent->IsA(ULandscapeComponent::StaticClass());
}

bool UHoudiniInputLandscape::HasContentChanged() const
{
if (Super::HasContentChanged())
{
return true;
}

const int32 NumComponents = CountLandscapeComponents();
return NumComponents != CachedNumLandscapeComponents;
}

void
UHoudiniInputLandscape::Update(UObject * InObject)
{
Super::Update(InObject);

ALandscapeProxy* Landscape = Cast<ALandscapeProxy>(InObject);
const ALandscapeProxy* Landscape = Cast<ALandscapeProxy>(InObject);

//ensure(Landscape);

if (Landscape)
{
// Nothing to do for landscapes?
Transform = FHoudiniEngineRuntimeUtils::CalculateHoudiniLandscapeTransform(Landscape->GetLandscapeInfo());
CachedNumLandscapeComponents = CountLandscapeComponents();
}
}

bool UHoudiniInputLandscape::HasActorTransformChanged() const
{
if (!GetActor())
return false;

if (HasComponentsTransformChanged())
return true;

// We replace the root component transform comparison, with a transform that is calculated, for Houdini, based
// on the currently loaded tiles.
ALandscapeProxy* LandscapeProxy = GetLandscapeProxy();
if (IsValid(LandscapeProxy))
{
const FTransform HoudiniTransform = FHoudiniEngineRuntimeUtils::CalculateHoudiniLandscapeTransform(LandscapeProxy->GetLandscapeInfo());
if (!Transform.Equals(HoudiniTransform))
{
return true;
}
}

return false;
}

EHoudiniInputObjectType
Expand Down
Loading

0 comments on commit 8133c3f

Please sign in to comment.