2375 lines
71 KiB
C++
2375 lines
71 KiB
C++
/*
|
|
* Copyright (c) <2021> Side Effects Software Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. The name of Side Effects Software may not be used to endorse or
|
|
* promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE "AS IS" AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
|
* NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "HoudiniAssetBlueprintComponent.h"
|
|
|
|
#include "HoudiniEngineCopyPropertiesInterface.h"
|
|
#include "HoudiniOutput.h"
|
|
#include "HoudiniEngineRuntime.h"
|
|
#include "HoudiniEngineRuntimeUtils.h"
|
|
#include "Engine/SCS_Node.h"
|
|
#include "Engine/SimpleConstructionScript.h"
|
|
#include "UObject/Object.h"
|
|
#include "Logging/LogMacros.h"
|
|
|
|
#include "HoudiniParameterFloat.h"
|
|
#include "HoudiniParameterToggle.h"
|
|
#include "HoudiniInput.h"
|
|
#include "HoudiniEngineRuntimePrivatePCH.h"
|
|
|
|
#if WITH_EDITOR
|
|
#include "Editor.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
#include "Toolkits/AssetEditorManager.h"
|
|
#include "Kismet2/ComponentEditorUtils.h"
|
|
#include "ComponentAssetBroker.h"
|
|
#endif
|
|
|
|
HOUDINI_BP_DEFINE_LOG_CATEGORY();
|
|
|
|
UHoudiniAssetBlueprintComponent::UHoudiniAssetBlueprintComponent(const FObjectInitializer & ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
|
|
#if WITH_EDITOR
|
|
if (IsTemplate())
|
|
{
|
|
// CachedAssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
|
|
//GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OnAssetEditorRequestClose().AddUObject( this, &UHoudiniAssetBlueprintComponent::ReceivedAssetEditorRequestCloseEvent );
|
|
}
|
|
#endif
|
|
|
|
bForceNeedUpdate = false;
|
|
bHoudiniAssetChanged = false;
|
|
bIsInBlueprintEditor = false;
|
|
bCanDeleteHoudiniNodes = false;
|
|
|
|
// AssetState will be updated by changes to the HoudiniAsset
|
|
// or parameter changes on the Component template.
|
|
AssetState = EHoudiniAssetState::None;
|
|
bHasRegisteredComponentTemplate = false;
|
|
bHasBeenLoaded = false;
|
|
bUpdatedFromTemplate = false;
|
|
|
|
// Disable proxy mesh by default (unsupported for now)
|
|
bOverrideGlobalProxyStaticMeshSettings = true;
|
|
bEnableProxyStaticMeshOverride = false;
|
|
bEnableProxyStaticMeshRefinementByTimerOverride = false;
|
|
bEnableProxyStaticMeshRefinementOnPreSaveWorldOverride = false;
|
|
bEnableProxyStaticMeshRefinementOnPreBeginPIEOverride = false;
|
|
StaticMeshMethod = EHoudiniStaticMeshMethod::RawMesh;
|
|
|
|
// Set default mobility to Movable
|
|
Mobility = EComponentMobility::Movable;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void
|
|
UHoudiniAssetBlueprintComponent::CopyStateToTemplateComponent()
|
|
{
|
|
// We need to propagate changes made here back to the corresponding component in
|
|
// the Blueprint Generated Class ("<COMPONENT_NAME>_GEN_VARIABLE"). The reason being that
|
|
// the Blueprint editor works directly with the GEN_VARIABLE component (all
|
|
// PostEditChange() calls, Details Customizations, etc will receive the GEN_VARIABLE instance) BUT
|
|
// when the Editor runs the construction script it uses a different component instance, so all changes
|
|
// made to that instance won't write back to the Blueprint definition.
|
|
// To Summarize:
|
|
// Be sure to sync the Parameters array (and any other relevant properties) back
|
|
// to the corresponding component on the Blueprint Generated class otherwise these wont be
|
|
// accessible in the Details Customization callbacks.
|
|
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::CopyStateFromTemplateComponent] Component: %s"), *(GetPathName()));
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::CopyStateFromTemplateComponent] To Component: %s"), *(CachedTemplateComponent->GetPathName()));
|
|
|
|
// This should never be called by component templates.
|
|
check(!IsTemplate());
|
|
|
|
if (!CachedTemplateComponent.IsValid())
|
|
return;
|
|
|
|
USimpleConstructionScript* SCS = CachedBlueprint->SimpleConstructionScript;
|
|
check(SCS);
|
|
|
|
/*
|
|
USCS_Node* SCSNodeForInstance = FindSCSNodeForInstanceComponent(SCS, this);
|
|
if (SCSNodeForInstance)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
*/
|
|
|
|
//// If we don't have an SCS node for this preview instance, we need to create one, regardless
|
|
//// of whether output updates are required.
|
|
//if (!CachedTemplateComponent->bOutputsRequireUpdate && SCSNodeForInstance != nullptr)
|
|
// return;
|
|
|
|
// TODO: If the blueprint editor is NOT open, then we shouldn't attempting
|
|
// to copy state back to the BPGC at all!
|
|
FBlueprintEditor* BlueprintEditor = FHoudiniEngineRuntimeUtils::GetBlueprintEditor(this);
|
|
check(BlueprintEditor);
|
|
|
|
USCS_Node* SCSHACNode = FindSCSNodeForTemplateComponentInClassHierarchy(CachedTemplateComponent.Get());
|
|
// check(SCSHACNode);
|
|
|
|
// This is the actor instance that is being used for component editing.
|
|
AActor* PreviewActor = GetPreviewActor();
|
|
check(PreviewActor);
|
|
|
|
// NOTE: Inputs are only from component templates to instances, not the other way around ... I think.
|
|
|
|
// -----------------------------------------------------
|
|
// Copy outputs to component template
|
|
// -----------------------------------------------------
|
|
|
|
// Populate / update the outputs for the template from the preview / instance.
|
|
// TODO: Wrap the Blueprint manipulation in a transaction
|
|
TArray<UHoudiniOutput*>& TemplateOutputs = CachedTemplateComponent->Outputs;
|
|
TSet<UHoudiniOutput*> StaleTemplateOutputs(TemplateOutputs);
|
|
|
|
TemplateOutputs.SetNum(Outputs.Num());
|
|
CachedOutputNodes.Empty();
|
|
|
|
for (int i = 0; i < Outputs.Num(); i++)
|
|
{
|
|
// Find a output on the template that corresponds to this output from the instance.
|
|
UHoudiniOutput* TemplateOutput = nullptr;
|
|
UHoudiniOutput* InstanceOutput = nullptr;
|
|
InstanceOutput = Outputs[i];
|
|
|
|
//check(InstanceOutput)
|
|
if (!InstanceOutput || InstanceOutput->IsPendingKill())
|
|
continue;
|
|
|
|
// Ensure that instance outputs won't delete houdini content.
|
|
// Houdini content should only be allowed to be deleted from
|
|
// the component template.
|
|
InstanceOutput->SetCanDeleteHoudiniNodes(false);
|
|
|
|
TemplateOutput = TemplateOutputs[i];
|
|
|
|
if (TemplateOutput)
|
|
{
|
|
check(TemplateOutput->GetOuter() == CachedTemplateComponent.Get());
|
|
StaleTemplateOutputs.Remove(TemplateOutput);
|
|
}
|
|
|
|
|
|
if (TemplateOutput)
|
|
{
|
|
// Copy properties from the current instance component while preserving output objects
|
|
// and instanced outputs.
|
|
TemplateOutput->CopyPropertiesFrom(InstanceOutput, true);
|
|
}
|
|
else
|
|
{
|
|
// NOTE: If the template output is NULL it means that the HDA spawned a new component / output in the transient world
|
|
// and the new output object needs to be copied back to the BPGC.
|
|
|
|
// Corresponding template output could not be found. Create one by duplicating the instance output.
|
|
TemplateOutput = InstanceOutput->DuplicateAndCopyProperties(CachedTemplateComponent.Get(), FName(InstanceOutput->GetName()));
|
|
// Treat these the same one would components created by CreateDefaultSubObject.
|
|
// NOTE: CreateDefaultSubobject performs lots of checks, and unfortunately we can't use it directly (it is
|
|
// only allowed to be used in a constructor). Not sure whether we need to either. For now, we just set the
|
|
// object flags to be similar to components created by CreateDefaultSubobject.
|
|
TemplateOutput->SetFlags(RF_Public|RF_ArchetypeObject|RF_DefaultSubObject);
|
|
TemplateOutputs[i] = TemplateOutput;
|
|
}
|
|
|
|
check(TemplateOutput);
|
|
TemplateOutput->SetCanDeleteHoudiniNodes(false);
|
|
|
|
// Keep track of potential stale output objects on the template component, for this output.
|
|
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& TemplateOutputObjects = TemplateOutput->GetOutputObjects();
|
|
TArray<FHoudiniOutputObjectIdentifier> StaleTemplateObjects;
|
|
TemplateOutputObjects.GetKeys(StaleTemplateObjects);
|
|
|
|
for (auto& Entry : InstanceOutput->GetOutputObjects())
|
|
{
|
|
|
|
// Prepare the FHoudiniOutputObject for the template component
|
|
const FHoudiniOutputObject& InstanceObj = Entry.Value;
|
|
FHoudiniOutputObject TemplateObj;
|
|
|
|
// Any output present in the Instance Outputs should be
|
|
// transferred to the template.
|
|
// Remove this output object from stale outputs list.
|
|
StaleTemplateObjects.Remove(Entry.Key);
|
|
|
|
if (TemplateOutputObjects.Contains(Entry.Key))
|
|
{
|
|
// Reuse the existing template object
|
|
TemplateObj = TemplateOutputObjects.FindChecked(Entry.Key);
|
|
}
|
|
else
|
|
{
|
|
// Create a new template output object object by duplicating the instance object.
|
|
// Keep the output object, but clear the output component since we have to
|
|
// create a new component template.
|
|
TemplateObj = InstanceObj;
|
|
TemplateObj.ProxyComponent = nullptr;
|
|
TemplateObj.OutputComponent = nullptr;
|
|
TemplateObj.ProxyObject = nullptr;
|
|
}
|
|
|
|
USceneComponent* ComponentInstance = Cast<USceneComponent>(InstanceObj.OutputComponent);
|
|
USceneComponent* ComponentTemplate = Cast<USceneComponent>(TemplateObj.OutputComponent);
|
|
UObject* OutputObject = InstanceObj.OutputObject;
|
|
|
|
if (ComponentInstance)
|
|
{
|
|
// The translation process has either constructed new components, or it is
|
|
// reusing existing components, or changed an output (or all or none of the aforementioned).
|
|
// Carefully inspect the SCS graph to determine whether there is a corresponding
|
|
// (and compatible) node for this output. If not, create a new node and remove unusable node, if any.
|
|
|
|
USCS_Node* ComponentNode = nullptr;
|
|
{
|
|
// Check whether the current OutputComponent being referenced by the template is still valid.
|
|
// Even if it was removed in the editor, it doesn't have any associated destroyed / pendingkill state.
|
|
// Instead we're going to check for validity by finding an SCS node with a matching template component.
|
|
bool bValidComponentTemplate = (ComponentTemplate != nullptr);
|
|
if (ComponentTemplate)
|
|
{
|
|
|
|
ComponentNode = FindSCSNodeForTemplateComponentInClassHierarchy(ComponentTemplate);
|
|
bValidComponentTemplate = bValidComponentTemplate && (ComponentNode != nullptr);
|
|
}
|
|
|
|
if (!bValidComponentTemplate)
|
|
{
|
|
// Either this component was removed from the editor or it doesn't exist yet.
|
|
// Ensure the references are cleared
|
|
TemplateObj.OutputComponent = nullptr;
|
|
ComponentTemplate = nullptr;
|
|
}
|
|
}
|
|
|
|
// NOTE: we can't use the component instance name directly due to the Blueprint compiler performing an internal checking
|
|
// using FComponentEditorUtils::IsValidVariableNameString(), which will return false if the name looks like an autogenerated name...
|
|
//FString ComponentName = ComponentInstance->GetName();
|
|
FString ComponentName = FBlueprintEditorUtils::GetClassNameWithoutSuffix(ComponentInstance->GetClass());
|
|
FName ComponentFName = FName(ComponentName);
|
|
|
|
|
|
const auto ComponentCopyOptions = ( EditorUtilities::ECopyOptions::Type )(
|
|
EditorUtilities::ECopyOptions::PropagateChangesToArchetypeInstances |
|
|
EditorUtilities::ECopyOptions::CallPostEditChangeProperty |
|
|
EditorUtilities::ECopyOptions::CallPostEditMove);
|
|
|
|
if (IsValid(ComponentNode))
|
|
{
|
|
// Check if we have an existing and compatible SCS node containing a USceneComponent as a template component.
|
|
bool bComponentNodeIsValid = true;
|
|
|
|
ComponentTemplate = Cast<USceneComponent>(ComponentNode->ComponentTemplate);
|
|
|
|
bComponentNodeIsValid = bComponentNodeIsValid && ComponentInstance->GetClass() == ComponentNode->ComponentClass;
|
|
bComponentNodeIsValid = bComponentNodeIsValid && ComponentTemplate != nullptr;
|
|
// TODO: Do we need to perform any other compatibility checks?
|
|
|
|
if (!bComponentNodeIsValid)
|
|
{
|
|
// Component template is not compatible. We can't reuse it.
|
|
|
|
SCSHACNode->RemoveChildNode(ComponentNode);
|
|
SCS->RemoveNode(ComponentNode);
|
|
ComponentNode = nullptr;
|
|
ComponentTemplate = nullptr;
|
|
CachedTemplateComponent->MarkAsBlueprintStructureModified();
|
|
}
|
|
}
|
|
|
|
if (ComponentNode)
|
|
{
|
|
// We found a reusable SCS node. Just copy the component instance
|
|
// properties over to the existing template.
|
|
check(ComponentNode->ComponentTemplate);
|
|
|
|
// UEngine::FCopyPropertiesForUnrelatedObjectsParams Params;
|
|
// //Params.bReplaceObjectClassReferences = false;
|
|
// Params.bDoDelta = false; // Perform a deep copy
|
|
// Params.bClearReferences = false;
|
|
// UEngine::CopyPropertiesForUnrelatedObjects(ComponentInstance, ComponentNode->ComponentTemplate, Params);
|
|
|
|
FHoudiniEngineRuntimeUtils::CopyComponentProperties(ComponentInstance, ComponentNode->ComponentTemplate, ComponentCopyOptions);
|
|
CachedTemplateComponent->MarkAsBlueprintStructureModified();
|
|
|
|
ComponentNode->ComponentTemplate->CreationMethod = EComponentCreationMethod::Native;
|
|
}
|
|
else
|
|
{
|
|
// We couldn't find a reusable SCS node.
|
|
// Duplicate the instance component and create a new corresponding SCS node
|
|
ComponentNode = SCS->CreateNode(ComponentInstance->GetClass(), ComponentFName);
|
|
|
|
UEditorEngine::FCopyPropertiesForUnrelatedObjectsParams Params;
|
|
Params.bDoDelta = false; // We need a deep copy of parameters here so the CDO values get copied as well
|
|
UEditorEngine::CopyPropertiesForUnrelatedObjects(ComponentInstance, ComponentNode->ComponentTemplate, Params);
|
|
// FHoudiniEngineRuntimeUtils::CopyComponentProperties(ComponentInstance, ComponentNode->ComponentTemplate, ComponentCopyOptions);
|
|
|
|
// {
|
|
// UInstancedStaticMeshComponent* Component = Cast<UInstancedStaticMeshComponent>(ComponentNode->ComponentTemplate);
|
|
// if (Component)
|
|
// {
|
|
// }
|
|
// }
|
|
|
|
// NOTE: The EComponentCreationMethod here is currently set to be the same as a component that was
|
|
// created manually in the editor.
|
|
ComponentNode->ComponentTemplate->CreationMethod = EComponentCreationMethod::Native;
|
|
|
|
// Add this node to the SCS root set.
|
|
|
|
// Attach the new node the HAC SCS node
|
|
// NOTE: This will add the node to the SCS->AllNodes list too but it won't update
|
|
// the nodename map. We can't forcibly update the Node/Name map either since the
|
|
// relevant functions have not been exported.
|
|
SCSHACNode->AddChildNode(ComponentNode);
|
|
|
|
// Set the output component.
|
|
TemplateObj.OutputComponent = ComponentNode->ComponentTemplate;
|
|
|
|
CachedTemplateComponent->MarkAsBlueprintStructureModified();
|
|
}
|
|
|
|
// Cache the mapping between the output and the SCS node.
|
|
check(ComponentNode);
|
|
CachedOutputNodes.Add(Entry.Key, ComponentNode->VariableGuid);
|
|
} // if (ComponentInstance)
|
|
/*
|
|
else if (InstanceObj.OutputObject)
|
|
{
|
|
|
|
}
|
|
*/
|
|
|
|
// Add the updated output object to the template output
|
|
TemplateOutputObjects.Add(Entry.Key, TemplateObj);
|
|
}
|
|
|
|
// Cleanup stale objects for this template output.
|
|
for (const auto& StaleId : StaleTemplateObjects)
|
|
{
|
|
FHoudiniOutputObject& OutputObj = TemplateOutputObjects.FindChecked(StaleId);
|
|
|
|
// Ensure the component template is no longer referencing this output.
|
|
TemplateOutputObjects.Remove(StaleId);
|
|
|
|
USceneComponent* TemplateComponent = Cast<USceneComponent>(OutputObj.OutputComponent);
|
|
|
|
if (TemplateComponent)
|
|
{
|
|
USCS_Node* StaleNode = FindSCSNodeForTemplateComponentInClassHierarchy(TemplateComponent);
|
|
if (StaleNode)
|
|
{
|
|
|
|
SCS->RemoveNode(StaleNode, false);
|
|
CachedTemplateComponent->MarkAsBlueprintStructureModified();
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
|
|
}
|
|
*/
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
|
|
}
|
|
*/
|
|
}
|
|
} //for (int i = 0; i < Outputs.Num(); i++)
|
|
|
|
// Clean up stale outputs on the component template.
|
|
for (UHoudiniOutput* StaleOutput : StaleTemplateOutputs)
|
|
{
|
|
if (!StaleOutput)
|
|
continue;
|
|
|
|
// Remove any components contained in this output from the SCS graph
|
|
for (auto& Entry : StaleOutput->GetOutputObjects())
|
|
{
|
|
FHoudiniOutputObject& StaleObject = Entry.Value;
|
|
USceneComponent* OutputComponent = Cast<USceneComponent>(StaleObject.OutputComponent);
|
|
|
|
if (OutputComponent)
|
|
{
|
|
|
|
USCS_Node* StaleNode = FindSCSNodeForTemplateComponentInClassHierarchy(OutputComponent);
|
|
if (StaleNode)
|
|
{
|
|
|
|
SCS->RemoveNode(StaleNode, false);
|
|
CachedTemplateComponent->MarkAsBlueprintStructureModified();
|
|
}
|
|
}
|
|
}
|
|
|
|
TemplateOutputs.Remove(StaleOutput);
|
|
//StaleOutput->ConditionalBeginDestroy();
|
|
}
|
|
|
|
SCS->ValidateSceneRootNodes();
|
|
|
|
// Copy parameters from this component to the template component.
|
|
// NOTE: We need to do this since the preview component will be cooking the HDA and get populated with
|
|
// all the parameters. This data needs to be sent back to the component template.
|
|
UClass* ComponentClass = CachedTemplateComponent->GetClass();
|
|
UHoudiniAssetBlueprintComponent* DefaultObj = Cast<UHoudiniAssetBlueprintComponent>(ComponentClass->GetDefaultObject());
|
|
bool bBPStructureModified = false;
|
|
CachedTemplateComponent->CopyDetailsFromComponent(
|
|
this,
|
|
true,
|
|
true,
|
|
true,
|
|
false,
|
|
true,
|
|
bBPStructureModified,
|
|
/* SetFlags */ CachedTemplateComponent->GetMaskedFlags(RF_PropagateToSubObjects));
|
|
|
|
if (bBPStructureModified)
|
|
CachedTemplateComponent->MarkAsBlueprintStructureModified();
|
|
|
|
// Copy the cached output nodes back to the template so that
|
|
// reconstructed actors can correctly update output objects
|
|
// with newly constructed components during ApplyComponentInstanceData() calls.
|
|
CachedTemplateComponent->CachedOutputNodes = CachedOutputNodes;
|
|
|
|
CachedTemplateComponent->MarkPackageDirty();
|
|
PostEditChange();
|
|
|
|
CachedTemplateComponent->AssetId = AssetId;
|
|
CachedTemplateComponent->HapiGUID = HapiGUID;
|
|
CachedTemplateComponent->AssetCookCount = AssetCookCount;
|
|
CachedTemplateComponent->AssetStateResult = AssetStateResult;
|
|
CachedTemplateComponent->bLastCookSuccess = bLastCookSuccess;
|
|
|
|
#if WITH_EDITOR
|
|
// TODO: Do we need to handle this right now or can we wait for the next Houdini Engine manager tick to process it?
|
|
if (CachedTemplateComponent->NeedBlueprintStructureUpdate())
|
|
{
|
|
// We are about to recompile the blueprint. This will reconstruct the preview actor so we need to ensure
|
|
// that the old actor won't release the houdini nodes.
|
|
FHoudiniEngineRuntimeUtils::MarkBlueprintAsStructurallyModified(CachedTemplateComponent.Get());
|
|
SetCanDeleteHoudiniNodes(false);
|
|
}
|
|
/*else if (CachedTemplateComponent->NeedBlueprintUpdate())
|
|
{
|
|
FHoudiniEngineRuntimeUtils::MarkBlueprintAsModified(CachedTemplateComponent.Get());
|
|
}*/
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
void
|
|
UHoudiniAssetBlueprintComponent::CopyStateFromTemplateComponent(UHoudiniAssetBlueprintComponent* FromComponent, const bool bClearFromInputs, const bool bClearToInputs, const bool bCopyInputObjectComponentProperties)
|
|
{
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::CopyStateFromTemplateComponent] Component: %s"), *(GetPathName()));
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::CopyStateFromTemplateComponent] From Component: %s"), *(FromComponent->GetPathName()));
|
|
|
|
// This should never be called by component templates.
|
|
check(!IsTemplate());
|
|
|
|
// Make sure all TransientDuplicate properties from the Template Component needed by this transient component
|
|
// gets copied.
|
|
|
|
ComponentGUID = FromComponent->ComponentGUID;
|
|
|
|
/*
|
|
{
|
|
const TArray<USceneComponent*> Children = GetAttachChildren();
|
|
for (USceneComponent* Child : Children)
|
|
{
|
|
if (!Child)
|
|
continue;
|
|
}
|
|
}
|
|
*/
|
|
|
|
// AssetState = FromComponent->PreviewAssetState;
|
|
|
|
// This state should not be shared between template / instance components.
|
|
//bFullyLoaded = FromComponent->bFullyLoaded;
|
|
|
|
bNoProxyMeshNextCookRequested = FromComponent->bNoProxyMeshNextCookRequested;
|
|
|
|
// Reconstruct outputs and update them to point to component instances as opposed to templates.
|
|
UObject* TemplateOuter = CachedTemplateComponent->GetOuter();
|
|
|
|
USimpleConstructionScript* SCS = CachedBlueprint->SimpleConstructionScript;
|
|
check(SCS);
|
|
|
|
// NOTE: We can find the SCS node for the HoudiniAssetComponent from either the template component or the instance (editor preview) component.
|
|
USCS_Node* SCSHACNode = FindSCSNodeForTemplateComponentInClassHierarchy(CachedTemplateComponent.Get());
|
|
check(SCSHACNode);
|
|
|
|
// -----------------------------------------------------
|
|
// Copy outputs to component template
|
|
// -----------------------------------------------------
|
|
|
|
TArray<UHoudiniOutput*>& TemplateOutputs = CachedTemplateComponent->Outputs;
|
|
|
|
TSet<UHoudiniOutput*> StaleInstanceOutputs(Outputs);
|
|
|
|
Outputs.SetNum(TemplateOutputs.Num());
|
|
|
|
for (int i = 0; i < TemplateOutputs.Num(); i++)
|
|
{
|
|
UHoudiniOutput* TemplateOutput = TemplateOutputs[i];
|
|
if (!IsValid(TemplateOutput))
|
|
continue;
|
|
|
|
UHoudiniOutput* InstanceOutput = Outputs[i];
|
|
if (!(InstanceOutput->GetOuter() == this))
|
|
InstanceOutput = nullptr;
|
|
|
|
if (InstanceOutput)
|
|
{
|
|
StaleInstanceOutputs.Remove(InstanceOutput);
|
|
}
|
|
|
|
if (InstanceOutput)
|
|
{
|
|
// Copy properties from the current instance component while preserving output objects
|
|
// and instanced outputs.
|
|
InstanceOutput->CopyPropertiesFrom(TemplateOutput, true);
|
|
}
|
|
else
|
|
{
|
|
InstanceOutput = TemplateOutput->DuplicateAndCopyProperties(this, FName(TemplateOutput->GetName()));
|
|
if (IsValid(InstanceOutput))
|
|
InstanceOutput->ClearFlags(RF_ArchetypeObject|RF_DefaultSubObject);
|
|
}
|
|
|
|
Outputs[i] = InstanceOutput;
|
|
|
|
if (!IsValid(InstanceOutput))
|
|
continue;
|
|
|
|
InstanceOutput->SetCanDeleteHoudiniNodes(false);
|
|
|
|
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& TemplateOutputObjects = TemplateOutput->GetOutputObjects();
|
|
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& InstanceOutputObjects = InstanceOutput->GetOutputObjects();
|
|
TArray<FHoudiniOutputObjectIdentifier> StaleOutputObjects;
|
|
InstanceOutputObjects.GetKeys(StaleOutputObjects);
|
|
|
|
for (auto& Entry : TemplateOutputObjects)
|
|
{
|
|
const FHoudiniOutputObject& TemplateObj = Entry.Value;
|
|
FHoudiniOutputObject InstanceObj = TemplateObj;
|
|
|
|
if (!InstanceOutputObjects.Contains(Entry.Key))
|
|
continue;
|
|
|
|
StaleOutputObjects.Remove(Entry.Key);
|
|
InstanceObj = InstanceOutputObjects.FindChecked(Entry.Key);
|
|
|
|
} // for (auto& Entry : TemplateOutputObjects)
|
|
|
|
// Cleanup stale output objects for this output.
|
|
for (const auto& StaleId : StaleOutputObjects)
|
|
{
|
|
//TemplateOutput
|
|
//check(TemplateOutputs);
|
|
|
|
FHoudiniOutputObject& OutputObj = InstanceOutputObjects.FindChecked(StaleId);
|
|
|
|
InstanceOutputObjects.Remove(StaleId);
|
|
if (OutputObj.OutputComponent)
|
|
{
|
|
//OutputObj.OutputComponent->ConditionalBeginDestroy();
|
|
OutputObj.OutputComponent = nullptr;
|
|
}
|
|
}
|
|
} // for (int i = 0; i < TemplateOutputs.Num(); i++)
|
|
|
|
// Cleanup any stale outputs found on the component instance.
|
|
for (UHoudiniOutput* StaleOutput : StaleInstanceOutputs)
|
|
{
|
|
if (!StaleOutput)
|
|
continue;
|
|
|
|
if (!(StaleOutput->GetOuter() == this))
|
|
continue;
|
|
|
|
// We don't want to clear stale outputs on components instances. Only on template components.
|
|
StaleOutput->SetCanDeleteHoudiniNodes(false);
|
|
}
|
|
|
|
// Copy parameters from the component template to the instance.
|
|
bool bBlueprintStructureChanged = false;
|
|
CopyDetailsFromComponent(FromComponent,
|
|
false,
|
|
bClearFromInputs,
|
|
bClearToInputs,
|
|
false,
|
|
true,
|
|
bBlueprintStructureChanged,
|
|
/*SetFlags*/ RF_Public,
|
|
/*ClearFlags*/ RF_DefaultSubObject|RF_ArchetypeObject);
|
|
}
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
void
|
|
UHoudiniAssetBlueprintComponent::CopyDetailsFromComponent(
|
|
UHoudiniAssetBlueprintComponent* FromComponent,
|
|
const bool bCreateSCSNodes,
|
|
const bool bClearChangedToInputs,
|
|
const bool bClearChangedFromInputs,
|
|
const bool bInCanDeleteHoudiniNodes,
|
|
const bool bCopyInputObjectComponentProperties,
|
|
bool &bOutBlueprintStructureChanged,
|
|
EObjectFlags SetFlags,
|
|
EObjectFlags ClearFlags)
|
|
{
|
|
check(FromComponent);
|
|
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::CopyDetailsFromComponent] Component: %s"), *(GetPathName()));
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::CopyDetailsFromComponent] FromComponent: %s"), *(FromComponent->GetPathName()));
|
|
|
|
/*
|
|
if (!FromComponent->HoudiniAsset)
|
|
{
|
|
return;
|
|
}
|
|
*/
|
|
|
|
// TODO: Try to reuse objects here when we're able.
|
|
//// Copy UHoudiniOutput state from instance to template
|
|
//UEngine::FCopyPropertiesForUnrelatedObjectsParams Params;
|
|
////Params.bReplaceObjectClassReferences = false;
|
|
////Params.bClearReferences = false;
|
|
//Params.bDoDelta = true;
|
|
//UEngine::CopyPropertiesForUnrelatedObjects(ComponentInstance, ComponentNode->ComponentTemplate, Params);
|
|
|
|
// Record input remapping that will need to take place when duplicating parameters.
|
|
TMap<UHoudiniInput*, UHoudiniInput*> InputMapping;
|
|
|
|
// -----------------------------------------------------
|
|
// Copy inputs
|
|
// -----------------------------------------------------
|
|
|
|
// TODO: Add support for input components
|
|
{
|
|
TArray<UHoudiniInput*>& FromInputs = FromComponent->Inputs;
|
|
TSet<UHoudiniInput*> StaleInputs(Inputs);
|
|
USimpleConstructionScript* SCS = GetSCS();
|
|
USCS_Node* SCSHACNode = nullptr;
|
|
|
|
if (bCreateSCSNodes)
|
|
{
|
|
SCSHACNode = FindSCSNodeForTemplateComponentInClassHierarchy(CachedTemplateComponent.Get());
|
|
}
|
|
|
|
Inputs.SetNum(FromInputs.Num());
|
|
for (int i = 0; i < FromInputs.Num(); i++)
|
|
{
|
|
UHoudiniInput* FromInput = nullptr;
|
|
UHoudiniInput* ToInput = nullptr;
|
|
FromInput = FromInputs[i];
|
|
|
|
check(FromInput);
|
|
|
|
ToInput = Inputs[i];
|
|
|
|
if (ToInput)
|
|
{
|
|
// Check whether the instance and template input objects are compatible.
|
|
bool bIsValid = true;
|
|
bIsValid = bIsValid && ToInput->Matches(*FromInput);
|
|
bIsValid = bIsValid && ToInput->GetOuter() == this;
|
|
|
|
if (!bIsValid)
|
|
{
|
|
ToInput = nullptr;
|
|
}
|
|
}
|
|
|
|
// TODO: Process stale input objects
|
|
|
|
// NOTE: The CopyStateFrom() / DuplicateAndCopyState() will copy/duplicate/cleanup internal inputs to
|
|
// ensure that there aren't any shared instances between the ToInput/FromInput.
|
|
if (ToInput)
|
|
{
|
|
// We have a compatible input that we can reuse.
|
|
StaleInputs.Remove(ToInput);
|
|
ToInput->CopyStateFrom(FromInput, true, bInCanDeleteHoudiniNodes);
|
|
}
|
|
else
|
|
{
|
|
|
|
// We don't have an existing / compatible input. Create a new one.
|
|
ToInput = FromInput->DuplicateAndCopyState(this, bInCanDeleteHoudiniNodes);
|
|
if (SetFlags != RF_NoFlags)
|
|
ToInput->SetFlags(SetFlags);
|
|
if (ClearFlags != RF_NoFlags)
|
|
ToInput->ClearFlags( ClearFlags );
|
|
}
|
|
|
|
check(ToInput);
|
|
|
|
|
|
UpdateInputObjectComponentReferences(SCS, FromInput, ToInput, bCopyInputObjectComponentProperties, bCreateSCSNodes, SCSHACNode, &bOutBlueprintStructureChanged);
|
|
|
|
Inputs[i] = ToInput;
|
|
InputMapping.Add(FromInput, ToInput);
|
|
|
|
if (bClearChangedToInputs)
|
|
{
|
|
// Clear the changed flags on the FromInput so that it doesn't trigger
|
|
// another update. The ToInput will now be carrying to changed/update flags.
|
|
ToInput->MarkChanged(false);
|
|
ToInput->MarkAllInputObjectsChanged(false);
|
|
}
|
|
|
|
if (bClearChangedFromInputs)
|
|
{
|
|
// Clear the changed flags on the FromInput so that it doesn't trigger
|
|
// another update. The ToInput will now be carrying to changed/update flags.
|
|
FromInput->MarkChanged(false);
|
|
FromInput->MarkAllInputObjectsChanged(false);
|
|
}
|
|
}
|
|
|
|
// Cleanup any stale inputs from this component.
|
|
// NOTE: We would typically only have stale inputs when copying state from
|
|
// the component instance to the component template. Garbage collection
|
|
// eventually picks up the input objects and removes the content
|
|
// but until such time we are stuck with those nodes as inputs in the Houdini session
|
|
// so we get rid of those nodes immediately here to avoid some user confusion.
|
|
for (UHoudiniInput* StaleInput : StaleInputs)
|
|
{
|
|
if (!IsValid(StaleInput))
|
|
continue;
|
|
|
|
check(StaleInput->GetOuter() == this);
|
|
|
|
if (StaleInput->HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad))
|
|
continue;
|
|
|
|
StaleInput->ConditionalBeginDestroy();
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------
|
|
// Copy parameters (and optionally remap inputs).
|
|
// -----------------------------------------------------
|
|
TMap<UHoudiniParameter*, UHoudiniParameter*> ParameterMapping;
|
|
|
|
TArray<UHoudiniParameter*>& FromParameters = FromComponent->Parameters;
|
|
Parameters.SetNum(FromParameters.Num());
|
|
|
|
for (int i = 0; i < FromParameters.Num(); i++)
|
|
{
|
|
UHoudiniParameter* FromParameter = nullptr;
|
|
UHoudiniParameter* ToParameter = nullptr;
|
|
|
|
FromParameter = FromParameters[i];
|
|
|
|
check(FromParameter);
|
|
|
|
if (Parameters.IsValidIndex(i))
|
|
{
|
|
ToParameter = Parameters[i];
|
|
}
|
|
|
|
if (ToParameter)
|
|
{
|
|
bool bIsValid = true;
|
|
// Check whether To/From parameters are compatible
|
|
bIsValid = bIsValid && ToParameter->Matches(*FromParameter);
|
|
bIsValid = bIsValid && ToParameter->GetOuter() == this;
|
|
|
|
if (!bIsValid)
|
|
ToParameter = nullptr;
|
|
}
|
|
|
|
if (ToParameter)
|
|
{
|
|
// Parameter already exists. Simply sync the state.
|
|
ToParameter->CopyStateFrom(FromParameter, true, ClearFlags, SetFlags);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Check whether parameters are the same to avoid recreating them.
|
|
ToParameter = FromParameter->DuplicateAndCopyState(this, ClearFlags, SetFlags);
|
|
Parameters[i] = ToParameter;
|
|
}
|
|
|
|
check(ToParameter);
|
|
ParameterMapping.Add(FromParameter, ToParameter);
|
|
|
|
if (bClearChangedFromInputs)
|
|
{
|
|
// We clear the Changed flag on the FromParameter (most likely on the component template)
|
|
// since the template parameter state has now been transfered to the preview component and
|
|
// will resume processing from there.
|
|
FromParameter->MarkChanged(false);
|
|
}
|
|
}
|
|
|
|
// Apply remappings on the new parameters
|
|
for (UHoudiniParameter* ToParameter : Parameters)
|
|
{
|
|
ToParameter->RemapParameters(ParameterMapping);
|
|
ToParameter->RemapInputs(InputMapping);
|
|
}
|
|
|
|
FProperty* ParametersProperty = GetClass()->FindPropertyByName(TEXT("Parameters"));
|
|
FPropertyChangedEvent Evt(ParametersProperty);
|
|
PostEditChangeProperty(Evt);
|
|
|
|
bEnableCooking = FromComponent->bEnableCooking;
|
|
bRecookRequested = FromComponent->bRecookRequested;
|
|
bRebuildRequested = FromComponent->bRebuildRequested;
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::UpdateInputObjectComponentReferences(
|
|
USimpleConstructionScript* SCS,
|
|
UHoudiniInput* FromInput,
|
|
UHoudiniInput* ToInput,
|
|
const bool bCopyInputObjectProperties,
|
|
const bool bCreateMissingSCSNodes,
|
|
USCS_Node* SCSHACParent,
|
|
bool* bOutSCSNodeCreated)
|
|
{
|
|
TArray<UHoudiniInputHoudiniSplineComponent*> ToInputObjects;
|
|
TArray<UHoudiniInputHoudiniSplineComponent*> FromInputObjects;
|
|
TArray<UHoudiniInputHoudiniSplineComponent*> StaleInputObjects;
|
|
|
|
ToInput->GetAllHoudiniInputSplineComponents(ToInputObjects);
|
|
FromInput->GetAllHoudiniInputSplineComponents(FromInputObjects);
|
|
|
|
StaleInputObjects = ToInputObjects;
|
|
|
|
const int32 NumInputObjects = FromInputObjects.Num();
|
|
ToInputObjects.SetNum(NumInputObjects);
|
|
|
|
const auto ComponentCopyOptions = ( EditorUtilities::ECopyOptions::Type )(EditorUtilities::ECopyOptions::Default);
|
|
UEngine::FCopyPropertiesForUnrelatedObjectsParams Params;
|
|
//Params.bDoDelta = false; // Perform a deep copy
|
|
Params.bClearReferences = false;
|
|
|
|
for(int32 InputObjectIndex = 0; InputObjectIndex < NumInputObjects; ++InputObjectIndex)
|
|
{
|
|
UHoudiniInputHoudiniSplineComponent* FromInputObject = FromInputObjects[InputObjectIndex];
|
|
UHoudiniInputHoudiniSplineComponent* ToInputObject = ToInputObjects[InputObjectIndex];
|
|
if (!FromInputObject)
|
|
continue;
|
|
if (!ToInputObject)
|
|
continue;
|
|
|
|
USCS_Node* SCSNode = nullptr;
|
|
if (CachedInputNodes.Contains(ToInputObject->Guid))
|
|
{
|
|
// Reuse / update the existing SCS node.
|
|
SCSNode = SCS->FindSCSNodeByGuid( CachedInputNodes.FindChecked(ToInputObject->Guid) );
|
|
}
|
|
|
|
if (!SCSNode)
|
|
{
|
|
if (!bCreateMissingSCSNodes)
|
|
continue; // This input object should be removed.
|
|
}
|
|
|
|
USceneComponent* ToComponent = nullptr;
|
|
USceneComponent* FromComponent = Cast<USceneComponent>(FromInputObject->GetObject());
|
|
|
|
StaleInputObjects.Remove(ToInputObject);
|
|
|
|
if (FromComponent)
|
|
{
|
|
if (!SCSNode)
|
|
{
|
|
if (bCreateMissingSCSNodes)
|
|
{
|
|
// Create a new SCS node
|
|
SCSNode = SCS->CreateNode(FromComponent->GetClass());
|
|
SCSHACParent->AddChildNode(SCSNode);
|
|
if (bOutSCSNodeCreated)
|
|
{
|
|
*bOutSCSNodeCreated = true;
|
|
}
|
|
AddInputObjectMapping(ToInputObject->Guid, SCSNode->VariableGuid);
|
|
}
|
|
}
|
|
|
|
if (SCSNode)
|
|
{
|
|
if (bCreateMissingSCSNodes)
|
|
{
|
|
// If we have been instructed to create missing SCS nodes, assume we are copying
|
|
// the the component template.
|
|
ToComponent = Cast<USceneComponent>(SCSNode->ComponentTemplate);
|
|
}
|
|
else
|
|
{
|
|
// We are not copying to the component template, so we're assuming this is a
|
|
// component instance. Find the component on the owning actor that matches the SCS node.
|
|
AActor* ToOwningActor = ToInput->GetTypedOuter<AActor>();
|
|
check(ToOwningActor);
|
|
|
|
ToComponent = Cast<USceneComponent>(FindComponentInstanceInActor(ToOwningActor, SCSNode));
|
|
}
|
|
|
|
if (bCopyInputObjectProperties && ToComponent)
|
|
{
|
|
USceneComponent* ToAttachParent = ToComponent->GetAttachParent();
|
|
// Copy specific properties from the component template to the instance, if supported by the component.
|
|
// We typically resort to this in order to transfer Transient and TransientDuplicate properties from the
|
|
// component template over to the instance (typically HasChanged / NeedsToTriggerUpdate flags) in order for
|
|
// the instance to cook properly.
|
|
IHoudiniEngineCopyPropertiesInterface* ToCopyableComponent = Cast<IHoudiniEngineCopyPropertiesInterface>(ToComponent);
|
|
if (ToCopyableComponent)
|
|
{
|
|
// Let the component manage its own data copying.
|
|
ToCopyableComponent->CopyPropertiesFrom(FromComponent);
|
|
}
|
|
else
|
|
{
|
|
// The component doesn't implement the property copy interface. Simply do a general property copy.
|
|
//UEngine::CopyPropertiesForUnrelatedObjects(FromComponent, ToComponent, Params);
|
|
FHoudiniEngineRuntimeUtils::CopyComponentProperties(FromComponent, ToComponent, ComponentCopyOptions);
|
|
}
|
|
ToComponent->PostEditChange();
|
|
}
|
|
}
|
|
}
|
|
|
|
ToInputObject->Update(ToComponent);
|
|
ToInputObjects[InputObjectIndex] = ToInputObject;
|
|
}
|
|
|
|
for (UHoudiniInputObject* StaleInputObject : StaleInputObjects)
|
|
{
|
|
if (!StaleInputObject)
|
|
continue;
|
|
StaleInputObject->InvalidateData();
|
|
ToInput->RemoveHoudiniInputObject(StaleInputObject);
|
|
ToInput->MarkChanged(true);
|
|
// TODO: Find the corresponding SCS node and remove it
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
bool
|
|
UHoudiniAssetBlueprintComponent::HasOpenEditor() const
|
|
{
|
|
if (IsTemplate())
|
|
{
|
|
IAssetEditorInstance* EditorInstance = FindEditorInstance();
|
|
|
|
return EditorInstance != nullptr;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
IAssetEditorInstance*
|
|
UHoudiniAssetBlueprintComponent::FindEditorInstance() const
|
|
{
|
|
UClass* BPGC = Cast<UClass>(GetOuter());
|
|
if (!IsValid(BPGC))
|
|
return nullptr;
|
|
UBlueprint* Blueprint = Cast<UBlueprint>(BPGC->ClassGeneratedBy);
|
|
if (!IsValid(Blueprint))
|
|
return nullptr;
|
|
if (!CachedAssetEditorSubsystem.IsValid())
|
|
return nullptr;
|
|
|
|
IAssetEditorInstance* EditorInstance = CachedAssetEditorSubsystem->FindEditorForAsset(Blueprint, false);
|
|
|
|
return EditorInstance;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
AActor*
|
|
UHoudiniAssetBlueprintComponent::GetPreviewActor() const
|
|
{
|
|
FBlueprintEditor* BlueprintEditor = FHoudiniEngineRuntimeUtils::GetBlueprintEditor(this);
|
|
if (BlueprintEditor)
|
|
{
|
|
return BlueprintEditor->GetPreviewActor();
|
|
}
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
UHoudiniAssetComponent*
|
|
UHoudiniAssetBlueprintComponent::GetCachedTemplate() const
|
|
{
|
|
return CachedTemplateComponent.Get();
|
|
}
|
|
|
|
//bool
|
|
//UHoudiniAssetBlueprintComponent::CanUpdateParameters() const
|
|
//{
|
|
// return IsTemplate();
|
|
//}
|
|
//
|
|
//bool
|
|
//UHoudiniAssetBlueprintComponent::CanUpdateInputs() const
|
|
//{
|
|
// return !IsTemplate();
|
|
//}
|
|
//
|
|
//bool
|
|
//UHoudiniAssetBlueprintComponent::CanUpdateOutputs() const
|
|
//{
|
|
// return !IsTemplate();
|
|
//}
|
|
//
|
|
//bool
|
|
//UHoudiniAssetBlueprintComponent::CanProcessOutputs() const
|
|
//{
|
|
// return !IsTemplate();
|
|
//}
|
|
|
|
//bool
|
|
//UHoudiniAssetBlueprintComponent::CanInstantiateAsset() const
|
|
//{
|
|
// // If this is a preview component, it should not trigger an asset instantiation. It should wait
|
|
// // for the BPGC template component to finish the cook, get the synced data and then translate.
|
|
//
|
|
// if (IsPreview())
|
|
// return false;
|
|
//
|
|
// return true;
|
|
//}
|
|
//
|
|
//// Check whether the HAC can translate Houdini outputs at all
|
|
//bool
|
|
//UHoudiniAssetBlueprintComponent::CanTranslateFromHoudini() const
|
|
//{
|
|
// // Template components can't translate Houdini output since they typically do not exist in a world.
|
|
// if (IsTemplate())
|
|
// return false;
|
|
// // Preview components and normally instanced actors can translate Houdini outputs.
|
|
// return true;
|
|
//}
|
|
//
|
|
//// Check whether the HAC can translate a specific output type.
|
|
//bool
|
|
//UHoudiniAssetBlueprintComponent::CanTranslateFromHoudini(EHoudiniOutputType OutputType) const
|
|
//{
|
|
// // Blueprint components have limited translation support, for now.
|
|
// if (OutputType == EHoudiniOutputType::Mesh)
|
|
// return true;
|
|
//
|
|
// return false;
|
|
//}
|
|
//
|
|
bool
|
|
UHoudiniAssetBlueprintComponent::CanDeleteHoudiniNodes() const
|
|
{
|
|
return bCanDeleteHoudiniNodes;
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::SetCanDeleteHoudiniNodes(bool bInCanDeleteNodes)
|
|
{
|
|
bCanDeleteHoudiniNodes = bInCanDeleteNodes;
|
|
|
|
for (UHoudiniInput* Input : Inputs)
|
|
{
|
|
Input->SetCanDeleteHoudiniNodes(bInCanDeleteNodes);
|
|
}
|
|
|
|
for (UHoudiniOutput* Output : Outputs)
|
|
{
|
|
Output->SetCanDeleteHoudiniNodes(bInCanDeleteNodes);
|
|
}
|
|
}
|
|
|
|
bool
|
|
UHoudiniAssetBlueprintComponent::IsValidComponent() const
|
|
{
|
|
if (!Super::IsValidComponent())
|
|
return false;
|
|
|
|
if (IsTemplate())
|
|
{
|
|
UObject* Outer = this->GetOuter();
|
|
if (!IsValid(Outer))
|
|
return false;
|
|
UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(Outer);
|
|
if (!BPGC)
|
|
return false;
|
|
// Ensure this component is still in the SCS
|
|
USimpleConstructionScript* SCS = BPGC->SimpleConstructionScript;
|
|
if (!SCS)
|
|
return false;
|
|
USCS_Node* SCSNode = FindSCSNodeForTemplateComponentInClassHierarchy(this);
|
|
if (!SCSNode)
|
|
return false;
|
|
/*UClass* OwnerClass = Outer->GetClass();
|
|
if (!IsValid(OwnerClass))
|
|
return false;*/
|
|
/*UBlueprint* Blueprint = Cast<UBlueprint>(Outhe);
|
|
if (Blueprint)
|
|
{
|
|
|
|
}*/
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
if (!IsTemplate())
|
|
{
|
|
if (!GetOwner())
|
|
{
|
|
// If it's not a template, it needs an owner!
|
|
return false;
|
|
}
|
|
|
|
USimpleConstructionScript* SCS = GetSCS();
|
|
if (SCS)
|
|
{
|
|
// We're dealing with a Blueprint related component.
|
|
AActor* PreviewActor = GetPreviewActor();
|
|
AActor* OwningActor = GetOwner();
|
|
if (!OwningActor)
|
|
return false;
|
|
if (OwningActor != PreviewActor)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (IsPreview() && false)
|
|
{
|
|
USimpleConstructionScript* SCS = GetSCS();
|
|
if (!SCS)
|
|
return false; // Preview components should have an SCS.
|
|
|
|
// We want to specifically detect whether an editor component is still being previewed. We do this
|
|
// by checking whether the owning actor is still the active editor actor in the SCS.
|
|
AActor* PreviewActor = GetPreviewActor();
|
|
if (!PreviewActor)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Ensure this component still belongs the to the current preview actor.
|
|
if (PreviewActor != GetOwner())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
AActor* EditorActor = SCS->GetComponentEditorActorInstance();
|
|
if (GetOwner() != EditorActor)
|
|
{
|
|
return false;
|
|
}
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
UHoudiniAssetBlueprintComponent::IsInputTypeSupported(EHoudiniInputType InType) const
|
|
{
|
|
switch (InType)
|
|
{
|
|
case EHoudiniInputType::Geometry:
|
|
case EHoudiniInputType::Curve:
|
|
return true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
UHoudiniAssetBlueprintComponent::IsOutputTypeSupported(EHoudiniOutputType InType) const
|
|
{
|
|
switch (InType)
|
|
{
|
|
case EHoudiniOutputType::Mesh:
|
|
case EHoudiniOutputType::Instancer:
|
|
return true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
UHoudiniAssetBlueprintComponent::IsProxyStaticMeshEnabled() const
|
|
{
|
|
// TODO: Investigate adding support for proxy meshes in BP
|
|
// Disabled for now
|
|
return false;
|
|
}
|
|
|
|
//void
|
|
//UHoudiniAssetBlueprintComponent::BroadcastPreAssetCook()
|
|
//{
|
|
// // ------------------------------------------------
|
|
// // NOTE: This code will run on TEMPLATE components
|
|
// // ------------------------------------------------
|
|
//
|
|
// // The HoudiniAsset is about to be recooked. This flag will indicate to
|
|
// // the transient components that output processing needs to be baked
|
|
// // back to the BP definition.
|
|
// bOutputsRequireUpdate = true;
|
|
//
|
|
// Super::BroadcastPreAssetCook();
|
|
//}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnPrePreCook()
|
|
{
|
|
check(IsPreview());
|
|
|
|
Super::OnPrePreCook();
|
|
|
|
// We need to allow deleting houdini nodes
|
|
SetCanDeleteHoudiniNodes(true);
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnPostPreCook()
|
|
{
|
|
check(IsPreview());
|
|
|
|
Super::OnPostPreCook();
|
|
|
|
// Ensure the houdini nodes can be deleted during the translation process.
|
|
SetCanDeleteHoudiniNodes(false);
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnPreOutputProcessing()
|
|
{
|
|
check(IsPreview());
|
|
|
|
Super::OnPreOutputProcessing();
|
|
|
|
// Ensure the houdini nodes can be deleted during the translation process.
|
|
SetCanDeleteHoudiniNodes(true);
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnPostOutputProcessing()
|
|
{
|
|
Super::OnPostOutputProcessing();
|
|
|
|
// ------------------------------------------------
|
|
// NOTE:
|
|
// In Blueprint editor mode, this code will run on PREVIEW components
|
|
// In Map editor mode, this code will run on component instances.
|
|
// ------------------------------------------------
|
|
if (IsPreview())
|
|
{
|
|
// Ensure all the inputs / outputs belonging to the
|
|
// preview actor won't be deleted by PreviewActor destruction.
|
|
SetCanDeleteHoudiniNodes(false);
|
|
|
|
#if WITH_EDITOR
|
|
CopyStateToTemplateComponent();
|
|
#endif
|
|
|
|
}
|
|
bUpdatedFromTemplate = false;
|
|
}
|
|
|
|
void UHoudiniAssetBlueprintComponent::OnPrePreInstantiation()
|
|
{
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::OnPrePreInstantiation] Component: %s"), *(GetPathName()));
|
|
|
|
check(IsPreview());
|
|
|
|
if (bUpdatedFromTemplate)
|
|
return;
|
|
|
|
check(CachedTemplateComponent.IsValid());
|
|
|
|
// This HDA is about to be cooked but not through template parameter changes. It is likely that an input changed directly in the preview world.
|
|
// We need to flag our inputs and parameters appropriately in order to preserve their values.
|
|
|
|
// We need to mark all our parameters as changed/not triggering update
|
|
for (auto CurrentParam : Parameters)
|
|
{
|
|
if (CurrentParam)
|
|
{
|
|
CurrentParam->MarkChanged(true);
|
|
CurrentParam->SetNeedsToTriggerUpdate(false);
|
|
}
|
|
}
|
|
|
|
// We need to mark all our inputs as changed/not triggering update
|
|
for (auto CurrentInput : Inputs)
|
|
{
|
|
if (CurrentInput)
|
|
{
|
|
CurrentInput->MarkChanged(true);
|
|
CurrentInput->SetNeedsToTriggerUpdate(false);
|
|
CurrentInput->MarkDataUploadNeeded(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::NotifyHoudiniRegisterCompleted()
|
|
{
|
|
if (IsTemplate())
|
|
{
|
|
// TODO: Do we need to set any status flags or clear stuff to ensure
|
|
// the BP HAC will cook properly when the BP is opened again...
|
|
|
|
// If the template is being registered, we need to invalidate the AssetId here since it likely
|
|
// contains a stale asset id from its last cook.
|
|
AssetId = -1;
|
|
// Template component's have very limited update requirements / capabilities.
|
|
// Mostly just cache parameters and cook state.
|
|
SetAssetState(EHoudiniAssetState::ProcessTemplate);
|
|
}
|
|
|
|
Super::NotifyHoudiniRegisterCompleted();
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::NotifyHoudiniPreUnregister()
|
|
{
|
|
if (IsTemplate())
|
|
{
|
|
// Templates can delete Houdini nodes when they get deregistered.
|
|
SetCanDeleteHoudiniNodes(true);
|
|
}
|
|
Super::NotifyHoudiniPreUnregister();
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::NotifyHoudiniPostUnregister()
|
|
{
|
|
InvalidateData();
|
|
|
|
Super::NotifyHoudiniPostUnregister();
|
|
|
|
if (IsTemplate())
|
|
{
|
|
SetCanDeleteHoudiniNodes(false);
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnComponentCreated()
|
|
{
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::OnComponentCreated] Component: %s"), *(GetPathName()));
|
|
|
|
Super::OnComponentCreated();
|
|
bUpdatedFromTemplate = false;
|
|
|
|
CachePreviewState();
|
|
|
|
if (IsPreview())
|
|
{
|
|
// Don't set an initial AssetState here. Preview components should only cook when template's
|
|
// Houdini Asset or HDA parameters have changed.
|
|
|
|
// Clear these to ensure that we're not sharing references with the component template (otherwise
|
|
// the shared objects will get deleted when the component instance gets destroyed).
|
|
// These objects will be properly duplicated when copying state from the component template.
|
|
Inputs.Empty();
|
|
Parameters.Empty();
|
|
}
|
|
|
|
// Wait until InitializeComponent() for blueprint construction to complete before we start caching blueprint data.
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnRegister()
|
|
{
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::OnRegister] Component: %s"), *(GetPathName()));
|
|
|
|
Super::OnRegister();
|
|
|
|
// We run our Blueprint caching functions here since this the last hook that we have before
|
|
// entering HoudiniEngineTick();
|
|
|
|
CacheBlueprintData();
|
|
CachePreviewState();
|
|
|
|
if (IsPreview())
|
|
{
|
|
check(CachedTemplateComponent.Get());
|
|
// Ensure that the component template has been registered since it needs to be processed for parameter updates by the HE manager.
|
|
if (!FHoudiniEngineRuntime::Get().IsComponentRegistered(CachedTemplateComponent.Get()))
|
|
{
|
|
// The template component has not been registered yet, which means that we're probably busy opening a Blueprint editor and this
|
|
// preview component will need to be updated.
|
|
|
|
FHoudiniEngineRuntime::Get().RegisterHoudiniComponent(CachedTemplateComponent.Get(), true);
|
|
CachedTemplateComponent->SetCanDeleteHoudiniNodes(false);
|
|
// Since we're likely opening a fresh blueprint editor, we'll need to instantiate the HDA.
|
|
bHasRegisteredComponentTemplate = true;
|
|
}
|
|
}
|
|
|
|
if (IsTemplate())
|
|
{
|
|
// We're initializing the asset id for HAC template here since it doesn't get unloaded
|
|
// from memory, for example, between Blueprint Editor open/close so we need to make sure
|
|
// that the AssetId has indeed been reset between registrations.
|
|
AssetId = -1;
|
|
}
|
|
|
|
//TickInitialization();
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::BeginDestroy()
|
|
{
|
|
Super::BeginDestroy();
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::DestroyComponent(bool bPromoteChildren)
|
|
{
|
|
//FDebug::DumpStackTraceToLog();
|
|
if (CachedTemplateComponent.IsValid() && TemplatePropertiesChangedHandle.IsValid())
|
|
{
|
|
CachedTemplateComponent->Modify();
|
|
CachedTemplateComponent->OnParametersChangedEvent.Remove(TemplatePropertiesChangedHandle);
|
|
}
|
|
Super::DestroyComponent(bPromoteChildren);
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
|
|
{
|
|
Super::OnComponentDestroyed(bDestroyingHierarchy);
|
|
}
|
|
|
|
TStructOnScope<FActorComponentInstanceData>
|
|
UHoudiniAssetBlueprintComponent::GetComponentInstanceData() const
|
|
{
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::GetComponentInstanceData] Component: %s"), *(GetPathName()));
|
|
|
|
TStructOnScope<FActorComponentInstanceData> ComponentInstanceData = MakeStructOnScope<FActorComponentInstanceData, FHoudiniAssetBlueprintInstanceData>(this);
|
|
FHoudiniAssetBlueprintInstanceData* InstanceData = ComponentInstanceData.Cast<FHoudiniAssetBlueprintInstanceData>();
|
|
|
|
InstanceData->AssetId = AssetId;
|
|
InstanceData->AssetState = AssetState;
|
|
InstanceData->SubAssetIndex = SubAssetIndex;
|
|
InstanceData->ComponentGUID = ComponentGUID;
|
|
InstanceData->HapiGUID = HapiGUID;
|
|
InstanceData->HoudiniAsset = HoudiniAsset;
|
|
InstanceData->SourceName = GetPathName();
|
|
InstanceData->AssetCookCount = AssetCookCount;
|
|
InstanceData->bHasBeenLoaded = bHasBeenLoaded;
|
|
InstanceData->bHasBeenDuplicated = bHasBeenDuplicated;
|
|
InstanceData->bPendingDelete = bPendingDelete;
|
|
InstanceData->bRecookRequested = bRecookRequested;
|
|
InstanceData->bEnableCooking = bEnableCooking;
|
|
InstanceData->bForceNeedUpdate = bForceNeedUpdate;
|
|
InstanceData->bLastCookSuccess = bLastCookSuccess;
|
|
InstanceData->bRegisteredComponentTemplate = bHasRegisteredComponentTemplate;
|
|
|
|
InstanceData->Inputs.Empty();
|
|
|
|
for (UHoudiniInput* Input : Inputs)
|
|
{
|
|
if (!Input)
|
|
continue;
|
|
UHoudiniInput* TransientInput = Input->DuplicateAndCopyState(GetTransientPackage(), false);
|
|
InstanceData->Inputs.Add(TransientInput);
|
|
}
|
|
|
|
// Cache the current outputs
|
|
InstanceData->Outputs.Empty();
|
|
int OutputIndex = 0;
|
|
for(UHoudiniOutput* Output : Outputs)
|
|
{
|
|
if (!Output)
|
|
continue;
|
|
|
|
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject> OutputObjects = Output->GetOutputObjects();
|
|
for (auto& Entry : OutputObjects)
|
|
{
|
|
FHoudiniAssetBlueprintOutput OutputObjectData;
|
|
OutputObjectData.OutputIndex = OutputIndex;
|
|
OutputObjectData.OutputObject = Entry.Value;
|
|
InstanceData->Outputs.Add(Entry.Key, OutputObjectData);
|
|
}
|
|
|
|
++OutputIndex;
|
|
}
|
|
|
|
return ComponentInstanceData;
|
|
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::ApplyComponentInstanceData(FHoudiniAssetBlueprintInstanceData* InstanceData, const bool bPostUCS)
|
|
{
|
|
HOUDINI_BP_MESSAGE(TEXT("[UHoudiniAssetBlueprintComponent::ApplyComponentInstanceData] Component: %s"), *(GetPathName()));
|
|
check(InstanceData);
|
|
|
|
if (!bPostUCS)
|
|
{
|
|
// Initialize the component before the User Construction Script runs
|
|
USimpleConstructionScript* SCS = GetSCS();
|
|
check(SCS);
|
|
|
|
TArray<UHoudiniInput*> StaleInputs(Inputs);
|
|
|
|
// We need to update references contain in inputs / outputs to point to new reconstructed components.
|
|
const int32 NumInputs = InstanceData->Inputs.Num();
|
|
Inputs.SetNum(NumInputs);
|
|
for (int i = 0; i < NumInputs; ++i)
|
|
{
|
|
UHoudiniInput* FromInput = InstanceData->Inputs[i];
|
|
UHoudiniInput* ToInput = Inputs[i];
|
|
|
|
if (ToInput)
|
|
{
|
|
bool bIsValid = true;
|
|
bIsValid = bIsValid && ToInput->Matches(*FromInput);
|
|
bIsValid = bIsValid && ToInput->GetOuter() == this;
|
|
if (!bIsValid)
|
|
{
|
|
ToInput = nullptr;
|
|
}
|
|
}
|
|
|
|
if (ToInput)
|
|
{
|
|
// Reuse input
|
|
StaleInputs.Remove(ToInput);
|
|
ToInput->CopyStateFrom(FromInput, true, false);
|
|
}
|
|
else
|
|
{
|
|
// Create new input
|
|
ToInput = FromInput->DuplicateAndCopyState(this, false);
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
// We can't create missing SCS nodes here since we're likely already in the middle of a
|
|
// Blueprint reconstruction. We'll have to recreate missing SCS nodes next time the
|
|
// component state if copied to the template.
|
|
UpdateInputObjectComponentReferences(SCS, FromInput, ToInput, true, false);
|
|
#endif
|
|
|
|
Inputs[i] = ToInput;
|
|
}
|
|
|
|
// We need to update FHoudiniOutputObject SceneComponent references to
|
|
// the newly created components. Since we cached a map of Output Object IDs to
|
|
// SCSNodes (during CopyStateToTemplateComponent), we can the SCSNode that corresponds to this output objects and find
|
|
// the SceneComponent that matches the SCSNode's variable name.
|
|
// It is important to note that it is safe to do it this way since we're in the pre-UCS
|
|
// phase so that current components should match the SCS graph exactly (no user construction script
|
|
// interference here yet).
|
|
|
|
for (auto& Entry : InstanceData->Outputs)
|
|
{
|
|
FHoudiniOutputObjectIdentifier& ObjectId = Entry.Key;
|
|
FHoudiniAssetBlueprintOutput& OutputData = Entry.Value;
|
|
|
|
// NOTE: Output objects are going to be empty here since they dissapear during actor reconstruction.
|
|
// We'll need to repopulate from the instance data.
|
|
|
|
check(Outputs.IsValidIndex(OutputData.OutputIndex));
|
|
UHoudiniOutput* Output = Outputs[OutputData.OutputIndex];
|
|
check(Output);
|
|
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& OutputObjects = Output->GetOutputObjects();
|
|
FHoudiniOutputObject NewObject = OutputData.OutputObject;
|
|
|
|
if (OutputData.OutputObject.OutputComponent)
|
|
{
|
|
// Update the output component reference.
|
|
check(CachedOutputNodes.Contains(ObjectId))
|
|
const FGuid VariableGuid = CachedOutputNodes.FindChecked(ObjectId);
|
|
USCS_Node* SCSNode = SCS->FindSCSNodeByGuid(VariableGuid);
|
|
|
|
if (SCSNode)
|
|
{
|
|
// Find the component that corresponds to the SCS node.
|
|
USceneComponent* SceneComponent = FindActorComponentByName(GetOwner(), SCSNode->GetVariableName());
|
|
NewObject.OutputComponent = SceneComponent;
|
|
}
|
|
else
|
|
{
|
|
NewObject.OutputComponent = nullptr;
|
|
}
|
|
}
|
|
|
|
OutputObjects.Add(ObjectId, NewObject);
|
|
}
|
|
|
|
if (CachedTemplateComponent.IsValid())
|
|
{
|
|
#if WITH_EDITOR
|
|
CopyStateFromTemplateComponent( CachedTemplateComponent.Get(), false, false, true);
|
|
#endif
|
|
}
|
|
|
|
AssetId = InstanceData->AssetId;
|
|
SubAssetIndex = InstanceData->SubAssetIndex;
|
|
ComponentGUID = InstanceData->ComponentGUID;
|
|
HapiGUID = InstanceData->HapiGUID;
|
|
|
|
// Apply the previous HoudiniAsset to the component
|
|
// so that we can compare it against the template during CopyStateFromTemplate() calls to see whether it changed.
|
|
HoudiniAsset = InstanceData->HoudiniAsset;
|
|
|
|
AssetCookCount = InstanceData->AssetCookCount;
|
|
bHasBeenLoaded = InstanceData->bHasBeenLoaded;
|
|
bHasBeenDuplicated = InstanceData->bHasBeenDuplicated;
|
|
bPendingDelete = InstanceData->bPendingDelete;
|
|
bRecookRequested = InstanceData->bRecookRequested;
|
|
bEnableCooking = InstanceData->bEnableCooking;
|
|
bForceNeedUpdate = InstanceData->bForceNeedUpdate;
|
|
bLastCookSuccess = InstanceData->bLastCookSuccess;
|
|
bHasRegisteredComponentTemplate = InstanceData->bRegisteredComponentTemplate;
|
|
|
|
AssetState = InstanceData->AssetState;
|
|
|
|
SetCanDeleteHoudiniNodes(false);
|
|
|
|
} // if (!bPostUCS)
|
|
/*
|
|
else
|
|
{
|
|
// PostUCS
|
|
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::HoudiniEngineTick()
|
|
{
|
|
if (!IsFullyLoaded())
|
|
{
|
|
USimpleConstructionScript* SCS = GetSCS();
|
|
if (SCS == nullptr)
|
|
{
|
|
OnFullyLoaded();
|
|
}
|
|
else if (IsPreview())
|
|
{
|
|
AActor* OwningActor = GetOwner();
|
|
check(OwningActor);
|
|
|
|
// If this is a *preview component*, it is important to wait for the template component to be fully loaded
|
|
// since it needs to be initialized so that the component instance can copy initial values from the template.
|
|
check(CachedTemplateComponent.Get());
|
|
|
|
if (CachedTemplateComponent->IsFullyLoaded())
|
|
{
|
|
#if WITH_EDITOR
|
|
if(SCS->IsConstructingEditorComponents())
|
|
{
|
|
// We're stuck in an editor blueprint construction / preview actor update. Wait some more.
|
|
}
|
|
else
|
|
{
|
|
OnFullyLoaded();
|
|
}
|
|
#else
|
|
OnFullyLoaded();
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Anything else can go onto being fully loaded at this point.
|
|
OnFullyLoaded();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnFullyLoaded()
|
|
{
|
|
Super::OnFullyLoaded();
|
|
|
|
// Check whether this component is inside a Blueprint editor. If this object lives outside the blueprint editor (in , then we need to ensure that this
|
|
// component won't be influencing the Blueprint asset.
|
|
|
|
if (!IsTemplate())
|
|
{
|
|
#if WITH_EDITOR
|
|
AActor* PreviewActor = GetPreviewActor();
|
|
#else
|
|
AActor* PreviewActor = nullptr;
|
|
#endif
|
|
AActor* OwningActor = GetOwner();
|
|
|
|
if (!PreviewActor)
|
|
{
|
|
bIsInBlueprintEditor = false;
|
|
AssetState = EHoudiniAssetState::None;
|
|
return;
|
|
}
|
|
|
|
if (OwningActor && PreviewActor != OwningActor)
|
|
{
|
|
bIsInBlueprintEditor = false;
|
|
AssetState = EHoudiniAssetState::None;
|
|
return;
|
|
}
|
|
}
|
|
|
|
bIsInBlueprintEditor = true;
|
|
|
|
CachePreviewState();
|
|
CacheBlueprintData();
|
|
|
|
/*
|
|
for (UHoudiniOutput* Output : Outputs)
|
|
{
|
|
if (!Output)
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
if (IsTemplate())
|
|
{
|
|
AssetId = -1;
|
|
AssetState = EHoudiniAssetState::ProcessTemplate;
|
|
}
|
|
|
|
if (IsPreview())
|
|
{
|
|
check(CachedTemplateComponent.Get());
|
|
|
|
// If this is a preview actor, sync initial settings from the component template
|
|
#if WITH_EDITOR
|
|
CopyStateFromTemplateComponent(CachedTemplateComponent.Get(), false, false, true);
|
|
#endif
|
|
|
|
TemplatePropertiesChangedHandle = CachedTemplateComponent->OnParametersChangedEvent.AddUObject(this, &UHoudiniAssetBlueprintComponent::OnTemplateParametersChangedHandler);
|
|
if (bHoudiniAssetChanged)
|
|
{
|
|
|
|
// The HoudiniAsset has changed, so we need to force the PreviewInstance to re-instantiate
|
|
AssetState = EHoudiniAssetState::NeedInstantiation;
|
|
bForceNeedUpdate = true;
|
|
bHoudiniAssetChanged = false;
|
|
// TODO: Make this better?
|
|
CachedTemplateComponent->bHoudiniAssetChanged = false;
|
|
}
|
|
|
|
if (bHasRegisteredComponentTemplate)
|
|
{
|
|
// We have a newly registered component template. One of two things happened to cause this:
|
|
// 1. A new HoudiniAssetBlueprintComponent was created and registered.
|
|
// 2. The Blueprint Editor was closed / opened.
|
|
// The problem that arises in the #2 is that the template component was never fully unloaded
|
|
// from memory (it was deregistered but not destroyed). After deregistration we had the
|
|
// opportunity to invalidate asset/node ids but now that it has reregistered (without going
|
|
// through the normal initialization process) we will have to force a call to MarkAsNeedInstantiation
|
|
// during the next OnTemplateParametersChangedHandler() invocation.
|
|
bHasBeenLoaded = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnTemplateParametersChanged()
|
|
{
|
|
OnParametersChangedEvent.Broadcast(this);
|
|
}
|
|
|
|
void UHoudiniAssetBlueprintComponent::OnBlueprintStructureModified()
|
|
{
|
|
check(IsTemplate());
|
|
bBlueprintStructureModified = false;
|
|
|
|
#if WITH_EDITOR
|
|
if (IsTemplate())
|
|
{
|
|
FHoudiniEngineRuntimeUtils::MarkBlueprintAsStructurallyModified(this);
|
|
}
|
|
else
|
|
{
|
|
check(CachedTemplateComponent.IsValid());
|
|
FHoudiniEngineRuntimeUtils::MarkBlueprintAsStructurallyModified(CachedTemplateComponent.Get());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UHoudiniAssetBlueprintComponent::OnBlueprintModified()
|
|
{
|
|
check(IsTemplate());
|
|
bBlueprintModified = false;
|
|
#if WITH_EDITOR
|
|
FHoudiniEngineRuntimeUtils::MarkBlueprintAsModified(this);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnHoudiniAssetChanged()
|
|
{
|
|
if (IsTemplate())
|
|
{
|
|
// Invalidate data associated with this component since we're about to change and reinstantiate the Houdini Asset.
|
|
SetCanDeleteHoudiniNodes(true);
|
|
InvalidateData();
|
|
SetCanDeleteHoudiniNodes(false);
|
|
Parameters.Empty();
|
|
Inputs.Empty();
|
|
}
|
|
|
|
Super::OnHoudiniAssetChanged();
|
|
|
|
// Set on template components, then copied to preview components, and
|
|
// then used (and reset) during OnFullyLoaded.
|
|
bHoudiniAssetChanged = true;
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::RegisterHoudiniComponent(UHoudiniAssetComponent *InComponent)
|
|
{
|
|
// We only want to register this component if it is the preview actor for the Blueprint editor.
|
|
#if WITH_EDITOR
|
|
AActor* PreviewActor = GetPreviewActor();
|
|
#else
|
|
AActor* PreviewActor = nullptr;
|
|
#endif
|
|
AActor* OwningActor = GetOwner();
|
|
if (!OwningActor)
|
|
return;
|
|
|
|
if (PreviewActor != OwningActor)
|
|
return;
|
|
|
|
Super::RegisterHoudiniComponent(InComponent);
|
|
}
|
|
|
|
|
|
|
|
|
|
//bool UHoudiniAssetBlueprintComponent::TickInitialization()
|
|
//{
|
|
// return true;
|
|
//
|
|
// if (IsFullyLoaded())
|
|
// return true;
|
|
//
|
|
// bool bHasFinishedLoading = Super::TickInitialization();
|
|
//
|
|
// if (!bHasFinishedLoading)
|
|
// return false;
|
|
//
|
|
// if (!IsTemplate())
|
|
// {
|
|
//
|
|
// if (CachedTemplateComponent.Get())
|
|
// {
|
|
// // Now that that SCS has finished constructing editor components, we can continue.
|
|
// // Copy the current state from the template component, in case there is something that can be processed.
|
|
// CopyStateFromTemplateComponent(CachedTemplateComponent.Get());
|
|
// }
|
|
//
|
|
// AssetStateResult = EHoudiniAssetStateResult::None;
|
|
// AssetState = EHoudiniAssetState::None;
|
|
//
|
|
// bForceNeedUpdate = true;
|
|
// AssetState = EHoudiniAssetState::PostCook;
|
|
// }
|
|
//
|
|
// return true;
|
|
//}
|
|
|
|
template<typename ParamT, typename ValueT>
|
|
inline void
|
|
UHoudiniAssetBlueprintComponent::SetTypedValueAt(const FString& Name, ValueT& Value, int Index)
|
|
{
|
|
ParamT* Parameter = Cast<ParamT>(FindParameterByName(Name));
|
|
if (!Parameter)
|
|
return;
|
|
|
|
Parameter->SetValueAt(Value, Index);
|
|
}
|
|
|
|
bool
|
|
UHoudiniAssetBlueprintComponent::HasParameter(FString Name)
|
|
{
|
|
return FindParameterByName(Name) != nullptr;
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::SetFloatParameter(FString Name, float Value, int Index)
|
|
{
|
|
SetTypedValueAt<UHoudiniParameterFloat>(Name, Value, Index);
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::SetToggleValueAt(FString Name, bool Value, int Index)
|
|
{
|
|
UHoudiniParameterToggle* Parameter = Cast<UHoudiniParameterToggle>(FindParameterByName(Name));
|
|
if (!Parameter)
|
|
return;
|
|
|
|
Parameter->SetValueAt(Value, Index);
|
|
}
|
|
|
|
//void UHoudiniAssetBlueprintComponent::OnPostCookHandler(UHoudiniAssetComponent* InComponent)
|
|
//{
|
|
//
|
|
// // Before this component handles any translation, we need to make sure that it still belongs to the editor actor.
|
|
// // When a blueprint gets recompiled, the editor actor gets replaced with a new one but the old actor has not yet
|
|
// // been ftroyed / garbage collected so its components still receive cook events from the template.
|
|
//
|
|
// UHoudiniAssetBlueprintComponent* ComponentTemplate = Cast<UHoudiniAssetBlueprintComponent>(InComponent);
|
|
// if (!IsValid(ComponentTemplate))
|
|
// return;
|
|
//
|
|
// CopyStateFromTemplateComponent(ComponentTemplate);
|
|
// bForceNeedUpdate = true;
|
|
//}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::OnTemplateParametersChangedHandler(UHoudiniAssetComponent* InComponentTemplate)
|
|
{
|
|
if (!(AssetState == EHoudiniAssetState::None || AssetState == EHoudiniAssetState::NeedInstantiation || AssetState == EHoudiniAssetState::NeedRebuild))
|
|
// Don't process parameter changes since we're already cooking -- it is going to break things badly if we do.
|
|
return;
|
|
|
|
if (!IsValidComponent())
|
|
return;
|
|
|
|
UHoudiniAssetBlueprintComponent* ComponentTemplate = Cast<UHoudiniAssetBlueprintComponent>(InComponentTemplate);
|
|
if (!ComponentTemplate)
|
|
return;
|
|
|
|
// The component instance needs to copy values from the template.
|
|
bool bBlueprintStructureChanged = false;
|
|
#if WITH_EDITOR
|
|
CopyDetailsFromComponent(ComponentTemplate,
|
|
false,
|
|
false,
|
|
true,
|
|
false,
|
|
true,
|
|
bBlueprintStructureChanged,
|
|
RF_Public,
|
|
RF_ClassDefaultObject|RF_ArchetypeObject);
|
|
#endif
|
|
|
|
SetCanDeleteHoudiniNodes(false);
|
|
|
|
if (bHasRegisteredComponentTemplate)
|
|
{
|
|
// NOTE: It is very important to call this *after* CopyDetailsFromComponent(), since CopyDetailsFromComponent
|
|
// will clobber the inputs and parameter states on this component.
|
|
|
|
// If we already have a valid asset id, keep it.
|
|
if (AssetId >= 0)
|
|
{
|
|
MarkAsNeedCook();
|
|
}
|
|
else
|
|
{
|
|
MarkAsNeedInstantiation();
|
|
}
|
|
|
|
bHasRegisteredComponentTemplate = false;
|
|
bFullyLoaded = true; // MarkAsNeedInstantiation sets this to false. Force to true.
|
|
// While MarkAsNeedInstantiation() sets ParametersChanged to true, it does not
|
|
// set the 'NeedToTriggerUpdate' flag (both of which needs to be true in order
|
|
// to trigger an HDA update) so we are going to force NeedUpdate() to return true
|
|
// in order to get an initial cook.
|
|
bForceNeedUpdate = true;
|
|
}
|
|
|
|
bUpdatedFromTemplate = true;
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::InvalidateData()
|
|
{
|
|
if (IsTemplate())
|
|
{
|
|
// Ensure transient properties are invalidated/released for parameters, inputs and outputs as if the
|
|
// the object was undergoing destruction since the template component will likely be reregistered
|
|
// without being destroyed.
|
|
for(UHoudiniParameter* Param : Parameters)
|
|
{
|
|
Param->InvalidateData();
|
|
}
|
|
|
|
for(UHoudiniInput* Input : Inputs)
|
|
{
|
|
Input->InvalidateData();
|
|
}
|
|
|
|
FHoudiniEngineRuntime::Get().MarkNodeIdAsPendingDelete(AssetId, true);
|
|
AssetId = -1;
|
|
}
|
|
}
|
|
|
|
//void UHoudiniAssetBlueprintComponent::OnTemplateHoudiniAssetChangedHandler(UHoudiniAssetComponent* InComponentTemplate)
|
|
//{
|
|
//
|
|
// UHoudiniAssetBlueprintComponent* ComponentTemplate = Cast<UHoudiniAssetBlueprintComponent>(InComponentTemplate);
|
|
// if (!ComponentTemplate)
|
|
// return;
|
|
// check(IsPreview());
|
|
//
|
|
// // The Houdini Asset was changed on the template. We need to recook.
|
|
// AssetState = EHoudiniAssetState::NeedInstantiation;
|
|
//
|
|
//}
|
|
|
|
USceneComponent*
|
|
UHoudiniAssetBlueprintComponent::FindOwnerComponentByName(FName ComponentName) const
|
|
{
|
|
AActor* Owner = GetOwner();
|
|
if (!Owner)
|
|
return nullptr;
|
|
|
|
return FindActorComponentByName(Owner, ComponentName);
|
|
}
|
|
|
|
USceneComponent*
|
|
UHoudiniAssetBlueprintComponent::FindActorComponentByName(AActor* InActor, FName ComponentName) const
|
|
{
|
|
const TSet<UActorComponent*>& Components = InActor->GetComponents();
|
|
|
|
for (UActorComponent* Component : Components)
|
|
{
|
|
USceneComponent* SceneComponent = Cast<USceneComponent>(Component);
|
|
if (!IsValid(SceneComponent))
|
|
continue;
|
|
if (FName(SceneComponent->GetName()) == ComponentName)
|
|
return SceneComponent;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool UHoudiniAssetBlueprintComponent::GetInputObjectSCSVariableGuid(const FGuid& InputGuid, FGuid& OutSCSGuid)
|
|
{
|
|
FGuid* SCSGuid = CachedInputNodes.Find(InputGuid);
|
|
if (!SCSGuid)
|
|
return false;
|
|
OutSCSGuid = *SCSGuid;
|
|
return true;
|
|
}
|
|
|
|
USCS_Node*
|
|
UHoudiniAssetBlueprintComponent::FindSCSNodeForTemplateComponent(USimpleConstructionScript* SCS, const UActorComponent* InComponent) const
|
|
{
|
|
const TArray<USCS_Node*>& AllNodes = SCS->GetAllNodes();
|
|
|
|
for (USCS_Node* Node : AllNodes)
|
|
{
|
|
if (!Node)
|
|
continue;
|
|
|
|
if (Node->ComponentTemplate == InComponent)
|
|
return Node;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
USCS_Node*
|
|
UHoudiniAssetBlueprintComponent::FindSCSNodeForTemplateComponentInClassHierarchy(
|
|
const UActorComponent* InComponent) const
|
|
{
|
|
UObject* Outer = this->GetOuter();
|
|
if (!IsValid(Outer))
|
|
return nullptr;
|
|
UBlueprintGeneratedClass* MainBPGC;
|
|
if (IsTemplate())
|
|
{
|
|
MainBPGC = Cast<UBlueprintGeneratedClass>(Outer);
|
|
}
|
|
else
|
|
{
|
|
AActor* OwningActor = GetOwner();
|
|
MainBPGC = Cast<UBlueprintGeneratedClass>(OwningActor->GetClass());
|
|
}
|
|
|
|
check(MainBPGC);
|
|
TArray<const UBlueprintGeneratedClass*> BPGCStack;
|
|
UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(MainBPGC, BPGCStack);
|
|
for(const UBlueprintGeneratedClass* BPGC : BPGCStack)
|
|
{
|
|
USimpleConstructionScript* SCS = BPGC->SimpleConstructionScript;
|
|
if (!SCS)
|
|
return nullptr;
|
|
USCS_Node* SCSNode = FindSCSNodeForTemplateComponent(SCS, InComponent);
|
|
SCSNode = SCS->FindSCSNode(InComponent->GetFName());
|
|
if (SCSNode)
|
|
return SCSNode;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
USCS_Node*
|
|
UHoudiniAssetBlueprintComponent::FindSCSNodeForInstanceComponent(USimpleConstructionScript* SCS, const UActorComponent* InComponent) const
|
|
{
|
|
const TArray<USCS_Node*>& AllNodes = SCS->GetAllNodes();
|
|
|
|
if (!InComponent)
|
|
return nullptr;
|
|
|
|
for (USCS_Node* Node : AllNodes)
|
|
{
|
|
if (!Node)
|
|
continue;
|
|
if (Node->EditorComponentInstance.Get() == InComponent)
|
|
return Node;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
UActorComponent*
|
|
UHoudiniAssetBlueprintComponent::FindComponentInstanceInActor(const AActor* InActor,
|
|
USCS_Node* SCSNode) const
|
|
{
|
|
UActorComponent* ComponentTemplate = SCSNode->ComponentTemplate;
|
|
|
|
UActorComponent* ComponentInstance = NULL;
|
|
if (InActor != NULL)
|
|
{
|
|
if (SCSNode != NULL)
|
|
{
|
|
FName VariableName = SCSNode->GetVariableName();
|
|
if (VariableName != NAME_None)
|
|
{
|
|
UWorld* World = InActor->GetWorld();
|
|
FObjectPropertyBase* Property = FindFProperty<FObjectPropertyBase>(InActor->GetClass(), VariableName);
|
|
if (Property != NULL)
|
|
{
|
|
// Return the component instance that's stored in the property with the given variable name
|
|
ComponentInstance = Cast<UActorComponent>(Property->GetObjectPropertyValue_InContainer(InActor));
|
|
}
|
|
else if (World != nullptr && World->WorldType == EWorldType::EditorPreview)
|
|
{
|
|
// If this is the preview actor, return the cached component instance that's being used for the pmnaview actor prior to recompiling the Blueprint
|
|
#if WITH_EDITOR
|
|
ComponentInstance = SCSNode->EditorComponentInstance.Get();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else if (ComponentTemplate != NULL)
|
|
{
|
|
#if WITH_EDITOR
|
|
TInlineComponentArray<UActorComponent*> Components;
|
|
InActor->GetComponents(Components);
|
|
ComponentInstance = FComponentEditorUtils::FindMatchingComponent(ComponentTemplate, Components);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return ComponentInstance;
|
|
}
|
|
|
|
|
|
//void UHoudiniAssetBlueprintComponent::OnOutputProcessingCompletedHandler(UHoudiniAssetComponent* InComponent)
|
|
//{
|
|
//
|
|
// UHoudiniAssetBlueprintComponent* TemplateComponent = Cast<UHoudiniAssetBlueprintComponent>(InComponent);
|
|
// if (!IsValid(TemplateComponent))
|
|
// return;
|
|
//
|
|
// CopyStateFromComponent(TemplateComponent);
|
|
// bForceNeedUpdate = true;
|
|
//}
|
|
|
|
//#if WITH_EDITOR
|
|
//void UHoudiniAssetBlueprintComponent::ReceivedAssetEditorRequestCloseEvent(UObject* Asset, EAssetEditorCloseReason CloseReason)
|
|
//{
|
|
//
|
|
// if (CachedBlueprint.Get())
|
|
// {
|
|
// }
|
|
//
|
|
// if (Asset)
|
|
// {
|
|
//
|
|
// }
|
|
//
|
|
//}
|
|
//#endif
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::CachePreviewState()
|
|
{
|
|
bCachedIsPreview = false;
|
|
|
|
#if WITH_EDITOR
|
|
AActor* ComponentOwner = GetOwner();
|
|
if (!IsValid(ComponentOwner))
|
|
return;
|
|
|
|
USimpleConstructionScript* SCS = GetSCS();
|
|
if (SCS == nullptr)
|
|
return;
|
|
|
|
// Get the preview actor directly from the BlueprintEditor.
|
|
FBlueprintEditor* BlueprintEditor = FHoudiniEngineRuntimeUtils::GetBlueprintEditor(this);
|
|
if (BlueprintEditor)
|
|
{
|
|
AActor* PreviewActor = BlueprintEditor->GetPreviewActor();
|
|
if (PreviewActor == ComponentOwner)
|
|
{
|
|
bCachedIsPreview = true;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
UHoudiniAssetBlueprintComponent::CacheBlueprintData()
|
|
{
|
|
CachedBlueprint = nullptr;
|
|
CachedActorCDO = nullptr;
|
|
CachedTemplateComponent = IsTemplate() ? this : nullptr;
|
|
|
|
#if WITH_EDITOR
|
|
CachedAssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
|
|
#endif
|
|
|
|
UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(GetOuter());
|
|
if (BPGC)
|
|
{
|
|
// Dealing with a component template
|
|
CachedBlueprint = Cast<UBlueprint>(BPGC->ClassGeneratedBy);
|
|
}
|
|
else
|
|
{
|
|
// Dealing with a component instance.
|
|
CachedBlueprint = Cast<UBlueprint>(GetOuter()->GetClass()->ClassGeneratedBy);
|
|
}
|
|
|
|
if (CreationMethod != EComponentCreationMethod::SimpleConstructionScript)
|
|
return;
|
|
|
|
AActor* ComponentOwner = this->GetOwner();
|
|
if (!IsValid(ComponentOwner))
|
|
return;
|
|
UClass* OwnerClass = ComponentOwner->GetClass();
|
|
if (!IsValid(OwnerClass))
|
|
return;
|
|
|
|
if (!IsTemplate())
|
|
{
|
|
// NOTE: The following code allows us to find the component template from an instance.
|
|
CachedActorCDO = Cast< AActor >(CachedBlueprint->GeneratedClass->GetDefaultObject());
|
|
if (!CachedActorCDO.IsValid() || (CachedActorCDO.Get() == ComponentOwner))
|
|
return;
|
|
#if WITH_EDITOR
|
|
UActorComponent* TargetComponent = EditorUtilities::FindMatchingComponentInstance(this, CachedActorCDO.Get());
|
|
CachedTemplateComponent = Cast<UHoudiniAssetBlueprintComponent>(TargetComponent);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
USimpleConstructionScript*
|
|
UHoudiniAssetBlueprintComponent::GetSCS() const
|
|
{
|
|
if (!CachedBlueprint.Get())
|
|
return nullptr;
|
|
|
|
return CachedBlueprint->SimpleConstructionScript;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
// FHoudiniAssetBlueprintInstanceData
|
|
//------------------------------------------------------------------------------------------------
|
|
|
|
FHoudiniAssetBlueprintInstanceData::FHoudiniAssetBlueprintInstanceData()
|
|
: HoudiniAsset(nullptr)
|
|
, AssetId(-1)
|
|
, AssetState(EHoudiniAssetState::None)
|
|
, SubAssetIndex(-1)
|
|
, AssetCookCount(0)
|
|
, bHasBeenLoaded(false)
|
|
, bHasBeenDuplicated(false)
|
|
, bPendingDelete(false)
|
|
, bRecookRequested(false)
|
|
, bRebuildRequested(false)
|
|
, bEnableCooking(true)
|
|
, bForceNeedUpdate(false)
|
|
, bLastCookSuccess(false)
|
|
, ComponentGUID(FGuid())
|
|
, HapiGUID(FGuid())
|
|
, bRegisteredComponentTemplate(false)
|
|
, SourceName()
|
|
{
|
|
|
|
}
|
|
|
|
FHoudiniAssetBlueprintInstanceData::FHoudiniAssetBlueprintInstanceData(const UHoudiniAssetBlueprintComponent* SourceComponent)
|
|
: FActorComponentInstanceData(SourceComponent)
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
FHoudiniAssetBlueprintInstanceData::AddReferencedObjects(FReferenceCollector & Collector)
|
|
{
|
|
Super::AddReferencedObjects(Collector);
|
|
// TODO: Do we need to add references to output objects here?
|
|
// Any other references?
|
|
// What are the implications?
|
|
}
|