-
Notifications
You must be signed in to change notification settings - Fork 394
Expand file tree
/
Copy pathHoudiniAssetBlueprintComponent.cpp
More file actions
executable file
·2496 lines (2093 loc) · 74.9 KB
/
HoudiniAssetBlueprintComponent.cpp
File metadata and controls
executable file
·2496 lines (2093 loc) · 74.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* 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 "HoudiniEngineRuntimePrivatePCH.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 "HoudiniParameterInt.h"
#include "HoudiniParameterFloat.h"
#include "HoudiniParameterString.h"
#include "HoudiniParameterColor.h"
#include "HoudiniParameterToggle.h"
#include "HoudiniParameterFile.h"
#include "HoudiniInput.h"
#if WITH_EDITOR
#include "Editor.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.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;
bUseDeprecatedRawMeshSupport = false;
// 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);
//// 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 (!IsValid(InstanceOutput))
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.OutputComponents.Empty();
TemplateObj.ProxyObject = nullptr;
}
// If the there is only one component we try to reuse templates. However, if there is more than
// one component, don't bother; the logic for doing so can become quite complicated and multiple components
// outputs are only currently used for world partition, which don't make sense for use in blueprints.
USceneComponent* ComponentTemplate = nullptr;
if (TemplateObj.OutputComponents.Num() == 1)
{
ComponentTemplate = Cast<USceneComponent>(TemplateObj.OutputComponents[0]);
}
else
{
TemplateObj.OutputComponents.Empty();
}
for(auto Component : InstanceObj.OutputComponents)
{
USceneComponent* ComponentInstance = Cast<USceneComponent>(Component);
// 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;
if (InstanceObj.OutputComponents.Num() == 1)
{
// 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.OutputComponents.Empty();
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.OutputComponents.Empty();
TemplateObj.OutputComponents.Add(ComponentNode->ComponentTemplate);
CachedTemplateComponent->MarkAsBlueprintStructureModified();
}
// Cache the mapping between the output and the SCS node.
check(ComponentNode);
CachedOutputNodes.Add(Entry.Key, ComponentNode->VariableGuid);
} // if (ComponentInstance)
// 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);
for(auto Component : OutputObj.OutputComponents)
{
USceneComponent* TemplateComponent = Cast<USceneComponent>(Component);
if (TemplateComponent)
{
USCS_Node* StaleNode = FindSCSNodeForTemplateComponentInClassHierarchy(TemplateComponent);
if (StaleNode)
{
SCS->RemoveNode(StaleNode, false);
CachedTemplateComponent->MarkAsBlueprintStructureModified();
}
}
}
}
} //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;
for(auto Component : StaleObject.OutputComponents)
{
USceneComponent* OutputComponent = Cast<USceneComponent>(Component);
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.OutputComponents.Num() > 0)
{
//OutputObj.OutputComponent->ConditionalBeginDestroy();
OutputObj.OutputComponents.Empty();
}
}
} // 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);
FHoudiniInputObjectSettings ToInputSettings(ToInput);
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, ToInputSettings);
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