/* * Copyright (c) <2017> Side Effects Software Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include "HoudiniAssetParameterChoice.h" #include "HoudiniApi.h" #include "HoudiniEngineRuntimePrivatePCH.h" #include "HoudiniAssetComponent.h" #include "HoudiniEngine.h" #include "HoudiniEngineUtils.h" #include "HoudiniEngineString.h" #include "Misc/Variant.h" #include "Internationalization/Internationalization.h" #define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE UHoudiniAssetParameterChoice::UHoudiniAssetParameterChoice( const FObjectInitializer & ObjectInitializer ) : Super( ObjectInitializer ) , CurrentValue( 0 ) , bStringChoiceList( false ) { StringValue = TEXT( "" ); } UHoudiniAssetParameterChoice * UHoudiniAssetParameterChoice::Create( UObject * InPrimaryObject, UHoudiniAssetParameter * InParentParameter, HAPI_NodeId InNodeId, const HAPI_ParmInfo & ParmInfo ) { UObject * Outer = InPrimaryObject; if ( !Outer ) { Outer = InParentParameter; if ( !Outer ) { // Must have either component or parent not null. check( false ); } } UHoudiniAssetParameterChoice * HoudiniAssetParameterChoice = NewObject( Outer, UHoudiniAssetParameterChoice::StaticClass(), NAME_None, RF_Public | RF_Transactional ); HoudiniAssetParameterChoice->CreateParameter( InPrimaryObject, InParentParameter, InNodeId, ParmInfo ); return HoudiniAssetParameterChoice; } bool UHoudiniAssetParameterChoice::CreateParameter( UObject * InPrimaryObject, UHoudiniAssetParameter * InParentParameter, HAPI_NodeId InNodeId, const HAPI_ParmInfo & ParmInfo ) { if ( !Super::CreateParameter( InPrimaryObject, InParentParameter, InNodeId, ParmInfo ) ) return false; // Choice lists can be only integer or string. if ( ParmInfo.type != HAPI_PARMTYPE_INT && ParmInfo.type != HAPI_PARMTYPE_STRING ) return false; // Get the actual value for this property. CurrentValue = 0; if ( ParmInfo.type == HAPI_PARMTYPE_INT ) { // This is an integer choice list. bStringChoiceList = false; // Assign internal Hapi values index. SetValuesIndex( ParmInfo.intValuesIndex ); if ( FHoudiniApi::GetParmIntValues( FHoudiniEngine::Get().GetSession(), NodeId, &CurrentValue, ValuesIndex, TupleSize) != HAPI_RESULT_SUCCESS ) { return false; } if( CurrentValue >= ParmInfo.choiceCount ) { HOUDINI_LOG_WARNING(TEXT("parm '%s' has an invalid value %d, menu tokens are not supported for choice menus"), *GetParameterName(), CurrentValue); CurrentValue = 0; } } else if ( ParmInfo.type == HAPI_PARMTYPE_STRING ) { // This is a string choice list. bStringChoiceList = true; // Assign internal Hapi values index. SetValuesIndex( ParmInfo.stringValuesIndex ); HAPI_StringHandle StringHandle; if ( FHoudiniApi::GetParmStringValues( FHoudiniEngine::Get().GetSession(), NodeId, false, &StringHandle, ValuesIndex, TupleSize ) != HAPI_RESULT_SUCCESS ) { return false; } // Get the actual string value. FHoudiniEngineString HoudiniEngineString( StringHandle ); if ( !HoudiniEngineString.ToFString( StringValue ) ) return false; } // Get choice descriptors. TArray< HAPI_ParmChoiceInfo > ParmChoices; ParmChoices.SetNumUninitialized( ParmInfo.choiceCount ); for (int32 Idx = 0; Idx < ParmChoices.Num(); Idx++) FHoudiniApi::ParmChoiceInfo_Init(&(ParmChoices[Idx])); if ( FHoudiniApi::GetParmChoiceLists( FHoudiniEngine::Get().GetSession(), NodeId, &ParmChoices[ 0 ], ParmInfo.choiceIndex, ParmInfo.choiceCount ) != HAPI_RESULT_SUCCESS ) { return false; } // Get string values for all available choices. StringChoiceValues.Empty(); StringChoiceLabels.Empty(); bool bMatchedSelectionLabel = false; for ( int32 ChoiceIdx = 0; ChoiceIdx < ParmChoices.Num(); ++ChoiceIdx ) { FString * ChoiceValue = new FString(); FString * ChoiceLabel = new FString(); { FHoudiniEngineString HoudiniEngineString( ParmChoices[ ChoiceIdx ].valueSH ); if ( !HoudiniEngineString.ToFString( *ChoiceValue ) ) return false; StringChoiceValues.Add( TSharedPtr< FString >( ChoiceValue ) ); } { FHoudiniEngineString HoudiniEngineString( ParmChoices[ ChoiceIdx ].labelSH ); if ( !HoudiniEngineString.ToFString( *ChoiceLabel ) ) return false; StringChoiceLabels.Add( TSharedPtr< FString >( ChoiceLabel ) ); } // If this is a string choice list, we need to match name with corresponding selection label. if ( bStringChoiceList && !bMatchedSelectionLabel && ChoiceValue->Equals( StringValue ) ) { StringValue = *ChoiceLabel; bMatchedSelectionLabel = true; CurrentValue = ChoiceIdx; } else if ( !bStringChoiceList && ChoiceIdx == CurrentValue ) { StringValue = *ChoiceLabel; } } return true; } TOptional< TSharedPtr< FString > > UHoudiniAssetParameterChoice::GetValue( int32 Idx ) const { if ( Idx == 0 && StringChoiceValues.IsValidIndex(CurrentValue) ) return TOptional< TSharedPtr< FString > >( StringChoiceValues[ CurrentValue ] ); return TOptional< TSharedPtr< FString > >(); } int32 UHoudiniAssetParameterChoice::GetParameterValueInt() const { return CurrentValue; } const FString & UHoudiniAssetParameterChoice::GetParameterValueString() const { return StringValue; } bool UHoudiniAssetParameterChoice::IsStringChoiceList() const { return bStringChoiceList; } void UHoudiniAssetParameterChoice::SetValueInt( int32 Value, bool bTriggerModify, bool bRecordUndo ) { #if WITH_EDITOR FScopedTransaction Transaction( TEXT( HOUDINI_MODULE_RUNTIME ), LOCTEXT( "HoudiniAssetParameterChoiceChange", "Houdini Parameter Choice: Changing a value" ), PrimaryObject ); Modify(); if ( !bRecordUndo ) Transaction.Cancel(); #endif // WITH_EDITOR CurrentValue = Value; MarkChanged( bTriggerModify ); } bool UHoudiniAssetParameterChoice::UploadParameterValue() { if ( bStringChoiceList ) { // Get corresponding value. FString* ChoiceValue = StringChoiceValues.IsValidIndex(CurrentValue) ? StringChoiceValues[ CurrentValue ].Get() : nullptr; std::string String = TCHAR_TO_UTF8( *( *ChoiceValue ) ); FHoudiniApi::SetParmStringValue( FHoudiniEngine::Get().GetSession(), NodeId, String.c_str(), ParmId, 0 ); } else { // This is an int choice list. FHoudiniApi::SetParmIntValues( FHoudiniEngine::Get().GetSession(), NodeId, &CurrentValue, ValuesIndex, TupleSize ); } return Super::UploadParameterValue(); } bool UHoudiniAssetParameterChoice::SetParameterVariantValue( const FVariant & Variant, int32 Idx, bool bTriggerModify, bool bRecordUndo ) { EVariantTypes VariantType = Variant.GetType(); int32 VariantValue = 0; switch ( VariantType ) { case EVariantTypes::String: { if ( bStringChoiceList ) { bool bLabelFound = false; const FString & VariantStringValue = Variant.GetValue< FString >(); // We need to match selection based on label. int32 LabelIdx = 0; for ( ; LabelIdx < StringChoiceLabels.Num(); ++LabelIdx ) { FString * ChoiceLabel = StringChoiceLabels[ LabelIdx ].Get(); if ( ChoiceLabel->Equals( VariantStringValue ) ) { VariantValue = LabelIdx; bLabelFound = true; break; } } if ( !bLabelFound ) return false; } else { return false; } break; } case EVariantTypes::Int8: case EVariantTypes::Int16: case EVariantTypes::Int32: case EVariantTypes::Int64: case EVariantTypes::UInt8: case EVariantTypes::UInt16: case EVariantTypes::UInt32: case EVariantTypes::UInt64: { if ( CurrentValue >= 0 && CurrentValue < StringChoiceValues.Num() ) { VariantValue = Variant.GetValue< int32 >(); } else { return false; } break; } default: { return false; } } #if WITH_EDITOR FScopedTransaction Transaction( TEXT( HOUDINI_MODULE_RUNTIME ), LOCTEXT( "HoudiniAssetParameterChoiceChange", "Houdini Parameter Choice: Changing a value" ), PrimaryObject ); Modify(); if ( !bRecordUndo ) Transaction.Cancel(); #endif // WITH_EDITOR CurrentValue = VariantValue; MarkChanged( bTriggerModify ); return false; } void UHoudiniAssetParameterChoice::Serialize( FArchive & Ar ) { // Call base implementation. Super::Serialize( Ar ); Ar.UsingCustomVersion( FHoudiniCustomSerializationVersion::GUID ); if ( Ar.IsLoading() ) { StringChoiceValues.Empty(); StringChoiceLabels.Empty(); } int32 NumChoices = StringChoiceValues.Num(); Ar << NumChoices; int32 NumLabels = StringChoiceLabels.Num(); Ar << NumLabels; if ( Ar.IsSaving() ) { for ( int32 ChoiceIdx = 0; ChoiceIdx < StringChoiceValues.Num(); ++ChoiceIdx ) Ar << *( StringChoiceValues[ ChoiceIdx ].Get() ); for ( int32 LabelIdx = 0; LabelIdx < StringChoiceLabels.Num(); ++LabelIdx ) Ar << *( StringChoiceLabels[ LabelIdx ].Get() ); } else if ( Ar.IsLoading() ) { FString Temp; for ( int32 ChoiceIdx = 0; ChoiceIdx < NumChoices; ++ChoiceIdx ) { Ar << Temp; FString * ChoiceName = new FString( Temp ); StringChoiceValues.Add( TSharedPtr< FString >( ChoiceName ) ); } for ( int32 LabelIdx = 0; LabelIdx < NumLabels; ++LabelIdx ) { Ar << Temp; FString * ChoiceLabel = new FString( Temp ); StringChoiceLabels.Add( TSharedPtr< FString >( ChoiceLabel ) ); } } Ar << StringValue; Ar << CurrentValue; Ar << bStringChoiceList; } #if WITH_EDITOR void UHoudiniAssetParameterChoice::OnChoiceChange( TSharedPtr< FString > NewChoice ) { bool bLocalChanged = false; StringValue = *( NewChoice.Get() ); // We need to match selection based on label. int32 LabelIdx = 0; for ( ; LabelIdx < StringChoiceLabels.Num(); ++LabelIdx ) { FString * ChoiceLabel = StringChoiceLabels[ LabelIdx ].Get(); if ( ChoiceLabel->Equals( StringValue ) ) { bLocalChanged = true; break; } } if ( bLocalChanged ) { // Record undo information. FScopedTransaction Transaction( TEXT( HOUDINI_MODULE_RUNTIME ), LOCTEXT( "HoudiniAssetParameterChoiceChange", "Houdini Parameter Choice: Changing a value" ), PrimaryObject ); Modify(); CurrentValue = LabelIdx; // Mark this property as changed. MarkChanged(); } } FText UHoudiniAssetParameterChoice::HandleChoiceContentText() const { return FText::FromString( StringValue ); } #endif // WITH_EDITOR #undef LOCTEXT_NAMESPACE