Example #1
0
TArray<FString> UCurveBase::CreateCurveFromCSVString(const FString& InString)
{
	// Array used to store problems about curve import
	TArray<FString> OutProblems;

	TArray<FRichCurveEditInfo> Curves = GetCurves();
	const int32 NumCurves = Curves.Num();

	const FCsvParser Parser(InString);
	const FCsvParser::FRows& Rows = Parser.GetRows();

	if(Rows.Num() == 0)
	{
		OutProblems.Add(FString(TEXT("No data.")));
		return OutProblems;
	}

	// First clear out old data.
	ResetCurve();

	// Each row represents a point
	for(int32 RowIdx=0; RowIdx<Rows.Num(); RowIdx++)
	{
		const TArray<const TCHAR*>& Cells = Rows[RowIdx];
		const int32 NumCells = Cells.Num();

		// Need at least two cell, Time and one Value
		if(NumCells < 2)
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' has less than 2 cells."), RowIdx));
			continue;
		}

		float Time = FCString::Atof(Cells[0]);
		for(int32 CellIdx=1; CellIdx<NumCells && CellIdx<(NumCurves+1); CellIdx++)
		{
			FRichCurve* Curve = Curves[CellIdx-1].CurveToEdit;
			if(Curve != NULL)
			{
				FKeyHandle KeyHandle = Curve->AddKey(Time, FCString::Atof(Cells[CellIdx]));
				Curve->SetKeyInterpMode(KeyHandle, RCIM_Linear);
			}
		}

		// If we get more cells than curves (+1 for time cell)
		if(NumCells > (NumCurves + 1))
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' has too many cells for the curve(s)."), RowIdx));
		}
		// If we got too few cells
		else if(NumCells < (NumCurves + 1))
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' has too few cells for the curve(s)."), RowIdx));
		}
	}

	Modify(true);

	return OutProblems;
}
	bool ImportCurvesEmbeddedInSoundWave()
	{
		// find/create our sound wave
		USoundWave* const SoundWave = ImportSoundWave(TargetPackageName, TargetAssetName, WaveFile);
		if (SoundWave)
		{
			// create our curve table internal to the sound wave
			static const FName InternalCurveTableName("InternalCurveTable");
			SoundWave->Curves = NewObject<UCurveTable>(SoundWave, InternalCurveTableName);
			SoundWave->Curves->ClearFlags(RF_Public | RF_Standalone);
			SoundWave->Curves->SetFlags(SoundWave->Curves->GetFlags() | RF_Transactional);
			SoundWave->InternalCurves = SoundWave->Curves;

			// import curves from FBX
			float PreRollTime = 0.0f;
			if (FbxAnimUtils::ImportCurveTableFromNode(FbxFile, GetDefault<UFacialAnimationBulkImporterSettings>()->CurveNodeName, SoundWave->Curves, PreRollTime))
			{
				// we will need to add a curve to tell us the time we want to start playing audio
				FRichCurve* AudioCurve = SoundWave->Curves->RowMap.Add(TEXT("Audio"), new FRichCurve());
				AudioCurve->AddKey(PreRollTime, 1.0f);

				return true;
			}
		}

		return false;
	}
void FMatineeImportTools::SetOrAddKey( FRichCurve& Curve, float Time, float Value, float ArriveTangent, float LeaveTangent, EInterpCurveMode MatineeInterpMode )
{
	FKeyHandle KeyHandle = Curve.AddKey( Time, Value, false, Curve.FindKey( Time ) );
	FRichCurveKey& Key = Curve.GetKey( KeyHandle );
	Key.ArriveTangent = ArriveTangent;
	Key.LeaveTangent = LeaveTangent;
	Key.InterpMode = MatineeInterpolationToRichCurveInterpolation( MatineeInterpMode );
	Key.TangentMode = MatineeInterpolationToRichCurveTangent( MatineeInterpMode );
}
float FCurveTableRowHandle::Eval(float XValue) const
{
	SCOPE_CYCLE_COUNTER(STAT_CurveTableRowHandleEval); 

	FRichCurve* Curve = GetCurve();
	if(Curve != NULL)
	{
		return Curve->Eval(XValue);
	}

	return 0;
}
bool FCurveTableRowHandle::Eval(float XValue, float* YValue) const
{
	SCOPE_CYCLE_COUNTER(STAT_CurveTableRowHandleEval); 

	FRichCurve* Curve = GetCurve();
	if(Curve != NULL && YValue != NULL)
	{
		*YValue = Curve->Eval(XValue);
		return true;
	}

	return false;
}
FGradientStopMark SColorGradientEditor::AddStop( const FVector2D& Position, const FGeometry& MyGeometry, bool bColorStop )
{
	FScopedTransaction AddStopTrans( LOCTEXT("AddGradientStop", "Add Gradient Stop") );

	CurveOwner->ModifyOwner();

	FTrackScaleInfo ScaleInfo(ViewMinInput.Get(),  ViewMaxInput.Get(), 0.0f, 1.0f, MyGeometry.Size);

	FVector2D LocalPos = MyGeometry.AbsoluteToLocal( Position );

	float NewStopTime = ScaleInfo.LocalXToInput( LocalPos.X );
			
	TArray<FRichCurveEditInfo> Curves = CurveOwner->GetCurves();

	FGradientStopMark NewStop;
	NewStop.Time = NewStopTime;

	if( bColorStop )
	{
		FRichCurve* RedCurve = Curves[0].CurveToEdit;
		FRichCurve* GreenCurve = Curves[1].CurveToEdit;
		FRichCurve* BlueCurve = Curves[2].CurveToEdit;
		
		NewStop.RedKeyHandle = RedCurve->AddKey( NewStopTime, LastModifiedColor.R );
		NewStop.GreenKeyHandle = GreenCurve->AddKey( NewStopTime, LastModifiedColor.G );
		NewStop.BlueKeyHandle = BlueCurve->AddKey( NewStopTime, LastModifiedColor.B );
	}
	else
	{
		FRichCurve* AlphaCurve = Curves[3].CurveToEdit;
		NewStop.AlphaKeyHandle = AlphaCurve->AddKey( NewStopTime, LastModifiedColor.A );
	}

	return NewStop;
}
void UMovieSceneSection::SetCurveDefault(FRichCurve& InCurve, float Value)
{
	if (TryModify())
	{
		InCurve.SetDefaultValue(Value);
	}
}
void UMovieSceneSection::AddKeyToCurve(FRichCurve& InCurve, float Time, float Value, EMovieSceneKeyInterpolation Interpolation, const bool bUnwindRotation)
{
	if (IsTimeWithinSection(Time))
	{
		if (TryModify())
		{
			FKeyHandle ExistingKeyHandle = InCurve.FindKey(Time);
			FKeyHandle NewKeyHandle = InCurve.UpdateOrAddKey(Time, Value, bUnwindRotation);

			if (!InCurve.IsKeyHandleValid(ExistingKeyHandle) && InCurve.IsKeyHandleValid(NewKeyHandle))
			{
				MovieSceneHelpers::SetKeyInterpolation(InCurve, NewKeyHandle, Interpolation);
			}
		}
	}
}
Example #9
0
void UMovieSceneSection::AddKeyToCurve( FRichCurve& InCurve, float Time, float Value )
{
	if(IsTimeWithinSection(Time))
	{
		Modify();
		InCurve.UpdateOrAddKey(Time, Value);
	}
}
void UMovieSceneParameterSection::AddScalarParameterKey( FName InParameterName, float InTime, float InValue )
{
	FRichCurve* ExistingCurve = nullptr;
	for ( FScalarParameterNameAndCurve& ScalarParameterNameAndCurve : ScalarParameterNamesAndCurves )
	{
		if ( ScalarParameterNameAndCurve.ParameterName == InParameterName )
		{
			ExistingCurve = &ScalarParameterNameAndCurve.ParameterCurve;
			break;
		}
	}
	if ( ExistingCurve == nullptr )
	{
		int32 NewIndex = ScalarParameterNamesAndCurves.Add( FScalarParameterNameAndCurve( InParameterName ) );
		ScalarParameterNamesAndCurves[NewIndex].Index = ScalarParameterNamesAndCurves.Num() + VectorParameterNamesAndCurves.Num() - 1;
		ExistingCurve = &ScalarParameterNamesAndCurves[NewIndex].ParameterCurve;
	}
	ExistingCurve->AddKey(InTime, InValue);
}
Example #11
0
void AProjectile::Explode()
{
	BP_Explode();
	

	const FVector Loc = GetActorLocation();

	for (TActorIterator<AActor> aItr(GetWorld()); aItr; ++aItr)
	{
		const float distance = GetDistanceTo(*aItr);
		if (distance<AffectArea && aItr && aItr->GetRootComponent() && aItr->GetRootComponent()->Mobility == EComponentMobility::Movable)
		{
			FVector dir = aItr->GetActorLocation() - Loc;
			dir.Normalize();

			FRichCurve* RadialDamageCurveData = RadialDamageCurve.GetRichCurve();
			FRichCurve* RadialImpulseCurveData = RadialImpulseCurve.GetRichCurve();


			ABaseCharacter* theChar = Cast<ABaseCharacter>(*aItr);

			//If Player apply damage
			if (theChar && RadialDamageCurveData)
			{
				//printr("Apply Damage");
				UGameplayStatics::ApplyDamage(theChar, RadialDamageCurveData->Eval(distance), NULL, this, ExplosionDamageType);
			}
			
			if (RadialImpulseCurveData && aItr->GetRootComponent()->IsSimulatingPhysics() && Cast<UPrimitiveComponent>(aItr->GetRootComponent()))
			{
				Cast<UPrimitiveComponent>(aItr->GetRootComponent())->AddImpulse(dir*RadialImpulseCurveData->Eval(distance));
				
			}
		}

	}




	Destroy();
}
Example #12
0
AProjectile::AProjectile(const class FObjectInitializer& PCIP)
	: Super(PCIP)
{

	//RootPoint = PCIP.CreateDefaultSubobject<USceneComponent>(this, TEXT("RootPoint"));

	//		The Collision Component 
	CollisionComp = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("SphereComp"));
	CollisionComp->MoveIgnoreActors.Add(this);
	CollisionComp->InitSphereRadius(5.0f);
	CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");	
	CollisionComp->OnComponentHit.AddDynamic(this, &AProjectile::OnHit);	
	RootComponent = CollisionComp;



	Mesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("ProjectileMesh"));
	Mesh->bReceivesDecals = false;
	Mesh->CastShadow = false;
	Mesh->AttachParent = CollisionComp;

	//		Projectile Movement
	ProjectileMovement = PCIP.CreateDefaultSubobject<UProjectileMovementComponent>(this, TEXT("ProjectileComp"));
	ProjectileMovement->UpdatedComponent = CollisionComp;
	ProjectileMovement->InitialSpeed = 3000.f;
	ProjectileMovement->MaxSpeed = 3000.f;
	ProjectileMovement->bRotationFollowsVelocity = true;
	ProjectileMovement->bShouldBounce = true;

	bReplicates = true;


	FRichCurve* RadialDamageCurveData = RadialDamageCurve.GetRichCurve();
	RadialDamageCurveData->AddKey(0, 90);
	RadialDamageCurveData->AddKey(380, 40);
	RadialDamageCurveData->AddKey(900, 0);
	

	FRichCurve* RadialImpulseCurveData = RadialImpulseCurve.GetRichCurve();
	RadialImpulseCurveData->AddKey(0, 15000);
	RadialImpulseCurveData->AddKey(900, 9000);


	
}
void FGradientStopMark::SetColor( const FLinearColor& InColor, FCurveOwnerInterface& CurveOwner )
{
	TArray<FRichCurveEditInfo> Curves = CurveOwner.GetCurves();

	// Update the color component on each curve
	if( IsValidColorMark(Curves) )
	{
		FRichCurve* RedCurve = Curves[0].CurveToEdit;
		FRichCurve* GreenCurve = Curves[1].CurveToEdit;
		FRichCurve* BlueCurve = Curves[2].CurveToEdit;

		RedCurve->SetKeyValue( RedKeyHandle, InColor.R );
		GreenCurve->SetKeyValue( GreenKeyHandle, InColor.G );
		BlueCurve->SetKeyValue( BlueKeyHandle, InColor.B );
	}
	else if( IsValidAlphaMark(Curves) )
	{
		FRichCurve* AlphaCurve = Curves[3].CurveToEdit;

		AlphaCurve->SetKeyValue( AlphaKeyHandle, InColor.A );
	}
}
void SColorGradientEditor::DeleteStop( const FGradientStopMark& InMark )
{
	FScopedTransaction DeleteStopTrans( LOCTEXT("DeleteGradientStop", "Delete Gradient Stop") );
	CurveOwner->ModifyOwner();

	TArray<FRichCurveEditInfo> Curves = CurveOwner->GetCurves();

	FRichCurve* RedCurve = Curves[0].CurveToEdit;
	FRichCurve* GreenCurve = Curves[1].CurveToEdit;
	FRichCurve* BlueCurve = Curves[2].CurveToEdit;
	FRichCurve* AlphaCurve = Curves[3].CurveToEdit;

	if( InMark.IsValidAlphaMark( Curves ) )
	{
		AlphaCurve->DeleteKey( InMark.AlphaKeyHandle );
	}
	else if( InMark.IsValidColorMark( Curves ) ) 
	{
		RedCurve->DeleteKey( InMark.RedKeyHandle );
		GreenCurve->DeleteKey( InMark.GreenKeyHandle );
		BlueCurve->DeleteKey( InMark.BlueKeyHandle );
	}
}
void FGradientStopMark::SetTime( float NewTime, FCurveOwnerInterface& CurveOwner )
{
	TArray<FRichCurveEditInfo> Curves = CurveOwner.GetCurves();

	// Update the time on each curve
	if( IsValidColorMark(Curves) )
	{
		FRichCurve* RedCurve = Curves[0].CurveToEdit;
		FRichCurve* GreenCurve = Curves[1].CurveToEdit;
		FRichCurve* BlueCurve = Curves[2].CurveToEdit;

		RedKeyHandle = RedCurve->SetKeyTime( RedKeyHandle, NewTime );
		GreenKeyHandle = GreenCurve->SetKeyTime( GreenKeyHandle, NewTime );
		BlueKeyHandle = BlueCurve->SetKeyTime( BlueKeyHandle, NewTime );
	}
	else if( IsValidAlphaMark(Curves) )
	{
		FRichCurve* AlphaCurve = Curves[3].CurveToEdit;

		AlphaKeyHandle = AlphaCurve->SetKeyTime( AlphaKeyHandle, NewTime );
	} 

	Time = NewTime;
}
TArray<FString> UCurveTable::CreateTableFromCSVString(const FString& InString, ERichCurveInterpMode InterpMode)
{
	// Array used to store problems about table creation
	TArray<FString> OutProblems;

	const FCsvParser Parser(InString);
	const FCsvParser::FRows& Rows = Parser.GetRows();

	// Must have at least 2 rows (x values + y values for at least one row)
	if(Rows.Num() <= 1)
	{
		OutProblems.Add(FString(TEXT("Too few rows.")));
		return OutProblems;
	}

	// Empty existing data
	EmptyTable();

	TArray<float> XValues;
	GetCurveValues(Rows[0], &XValues);

	// Iterate over rows
	for(int32 RowIdx = 1; RowIdx < Rows.Num(); RowIdx++)
	{
		const TArray<const TCHAR*>& Row = Rows[RowIdx];

		// Need at least 1 cells (row name)
		if(Row.Num() < 1)
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' has too few cells."), RowIdx));
			continue;
		}

		// Get row name
		FName RowName = MakeValidName(Row[0]);

		// Check its not 'none'
		if(RowName == NAME_None)
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' missing a name."), RowIdx));
			continue;
		}

		// Check its not a duplicate
		if(RowMap.Find(RowName) != NULL)
		{
			OutProblems.Add(FString::Printf(TEXT("Duplicate row name '%s'."), *RowName.ToString()));
			continue;
		}

		TArray<float> YValues;
		GetCurveValues(Row, &YValues);

		if(XValues.Num() != YValues.Num())
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%s' does not have the right number of columns."), *RowName.ToString()));
			continue;
		}

		FRichCurve* NewCurve = new FRichCurve();
		// Now iterate over cells (skipping first cell, that was row name)
		for(int32 ColumnIdx = 0; ColumnIdx < XValues.Num(); ColumnIdx++)
		{
			FKeyHandle KeyHandle = NewCurve->AddKey(XValues[ColumnIdx], YValues[ColumnIdx]);
			NewCurve->SetKeyInterpMode(KeyHandle, InterpMode);
		}

		RowMap.Add(RowName, NewCurve);
	}

	Modify(true);
	return OutProblems;
}
TArray<FString> UCurveTable::CreateTableFromJSONString(const FString& InString, ERichCurveInterpMode InterpMode)
{
	// Array used to store problems about table creation
	TArray<FString> OutProblems;

	if (InString.IsEmpty())
	{
		OutProblems.Add(TEXT("Input data is empty."));
		return OutProblems;
	}

	TArray< TSharedPtr<FJsonValue> > ParsedTableRows;
	{
		const TSharedRef< TJsonReader<TCHAR> > JsonReader = TJsonReaderFactory<TCHAR>::Create(InString);
		if (!FJsonSerializer::Deserialize(JsonReader, ParsedTableRows) || ParsedTableRows.Num() == 0)
		{
			OutProblems.Add(FString::Printf(TEXT("Failed to parse the JSON data. Error: %s"), *JsonReader->GetErrorMessage()));
			return OutProblems;
		}
	}

	// Empty existing data
	EmptyTable();

	// Iterate over rows
	for (int32 RowIdx = 0; RowIdx < ParsedTableRows.Num(); ++RowIdx)
	{
		const TSharedPtr<FJsonValue>& ParsedTableRowValue = ParsedTableRows[RowIdx];
		TSharedPtr<FJsonObject> ParsedTableRowObject = ParsedTableRowValue->AsObject();
		if (!ParsedTableRowObject.IsValid())
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' is not a valid JSON object."), RowIdx));
			continue;
		}

		// Get row name
		static const FString RowNameJsonKey = TEXT("Name");
		const FName RowName = MakeValidName(ParsedTableRowObject->GetStringField(RowNameJsonKey));

		// Check its not 'none'
		if (RowName == NAME_None)
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' missing a name."), RowIdx));
			continue;
		}

		// Check its not a duplicate
		if (RowMap.Find(RowName) != NULL)
		{
			OutProblems.Add(FString::Printf(TEXT("Duplicate row name '%s'."), *RowName.ToString()));
			continue;
		}

		// Add a key for each entry in this row
		FRichCurve* NewCurve = new FRichCurve();
		for (const auto& ParsedTableRowEntry : ParsedTableRowObject->Values)
		{
			// Skip the name entry
			if (ParsedTableRowEntry.Key == RowNameJsonKey)
			{
				continue;
			}

			// Make sure we have a valid float key
			float EntryKey = 0.0f;
			if (!LexicalConversion::TryParseString(EntryKey, *ParsedTableRowEntry.Key))
			{
				OutProblems.Add(FString::Printf(TEXT("Key '%s' on row '%s' is not a float and cannot be parsed."), *ParsedTableRowEntry.Key, *RowName.ToString()));
				continue;
			}

			// Make sure we have a valid float value
			double EntryValue = 0.0;
			if (!ParsedTableRowEntry.Value->TryGetNumber(EntryValue))
			{
				OutProblems.Add(FString::Printf(TEXT("Entry '%s' on row '%s' is not a float and cannot be parsed."), *ParsedTableRowEntry.Key, *RowName.ToString()));
				continue;
			}

			FKeyHandle KeyHandle = NewCurve->AddKey(EntryKey, static_cast<float>(EntryValue));
			NewCurve->SetKeyInterpMode(KeyHandle, InterpMode);
		}

		RowMap.Add(RowName, NewCurve);
	}

	Modify(true);
	return OutProblems;
}
UWheeledVehicleMovementComponent4W::UWheeledVehicleMovementComponent4W(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
#if WITH_VEHICLE
	// grab default values from physx
	PxVehicleDifferential4WData DefDifferentialSetup;
	TEnumAsByte<EVehicleDifferential4W::Type> DiffType((uint8)DefDifferentialSetup.mType);
	DifferentialSetup.DifferentialType = DiffType;
	DifferentialSetup.FrontRearSplit = DefDifferentialSetup.mFrontRearSplit;
	DifferentialSetup.FrontLeftRightSplit = DefDifferentialSetup.mFrontLeftRightSplit;
	DifferentialSetup.RearLeftRightSplit = DefDifferentialSetup.mRearLeftRightSplit;
	DifferentialSetup.CentreBias = DefDifferentialSetup.mCentreBias;
	DifferentialSetup.FrontBias = DefDifferentialSetup.mFrontBias;
	DifferentialSetup.RearBias = DefDifferentialSetup.mRearBias;

	PxVehicleEngineData DefEngineData;
	EngineSetup.MOI = DefEngineData.mMOI;
	EngineSetup.MaxRPM = OmegaToRPM(DefEngineData.mMaxOmega);
	EngineSetup.DampingRateFullThrottle = DefEngineData.mDampingRateFullThrottle;
	EngineSetup.DampingRateZeroThrottleClutchEngaged = DefEngineData.mDampingRateZeroThrottleClutchEngaged;
	EngineSetup.DampingRateZeroThrottleClutchDisengaged = DefEngineData.mDampingRateZeroThrottleClutchDisengaged;

	// Convert from PhysX curve to ours
	FRichCurve* TorqueCurveData = EngineSetup.TorqueCurve.GetRichCurve();
	for(PxU32 KeyIdx=0; KeyIdx<DefEngineData.mTorqueCurve.getNbDataPairs(); KeyIdx++)
	{
		float Input = DefEngineData.mTorqueCurve.getX(KeyIdx) * EngineSetup.MaxRPM;
		float Output = DefEngineData.mTorqueCurve.getY(KeyIdx) * DefEngineData.mPeakTorque;
		TorqueCurveData->AddKey(Input, Output);
	}

	PxVehicleClutchData DefClutchData;
	TransmissionSetup.ClutchStrength = DefClutchData.mStrength;

	PxVehicleAckermannGeometryData DefAckermannSetup;
	AckermannAccuracy = DefAckermannSetup.mAccuracy;

	PxVehicleGearsData DefGearSetup;
	TransmissionSetup.GearSwitchTime = DefGearSetup.mSwitchTime;
	TransmissionSetup.ReverseGearRatio = DefGearSetup.mRatios[PxVehicleGearsData::eREVERSE];
	TransmissionSetup.FinalRatio = DefGearSetup.mFinalRatio;

	PxVehicleAutoBoxData DefAutoBoxSetup;
	TransmissionSetup.NeutralGearUpRatio = DefAutoBoxSetup.mUpRatios[PxVehicleGearsData::eNEUTRAL];
	TransmissionSetup.GearAutoBoxLatency = DefAutoBoxSetup.getLatency();
	TransmissionSetup.bUseGearAutoBox = true;

	for (uint32 i = PxVehicleGearsData::eFIRST; i < DefGearSetup.mNbRatios; i++)
	{
		FVehicleGearData GearData;
		GearData.DownRatio = DefAutoBoxSetup.mDownRatios[i];
		GearData.UpRatio = DefAutoBoxSetup.mUpRatios[i];
		GearData.Ratio = DefGearSetup.mRatios[i];
		TransmissionSetup.ForwardGears.Add(GearData);
	}
	
	// Init steering speed curve
	FRichCurve* SteeringCurveData = SteeringCurve.GetRichCurve();
	SteeringCurveData->AddKey(0.f, 1.f);
	SteeringCurveData->AddKey(20.f, 0.9f);
	SteeringCurveData->AddKey(60.f, 0.8f);
	SteeringCurveData->AddKey(120.f, 0.7f);

	// Initialize WheelSetups array with 4 wheels
	WheelSetups.SetNum(4);
#endif // WITH_VEHICLE
}
/**
 *	Transforms CurveTable data into format more effecient to read at runtime.
 *	UCurveTable requires string parsing to map to GroupName/AttributeSet/Attribute
 *	Each curve in the table represents a *single attribute's values for all levels*.
 *	At runtime, we want *all attribute values at given level*.
 */
void FAttributeSetInitter::PreloadAttributeSetData(UCurveTable* CurveData)
{
	if(!ensure(CurveData))
	{
		return;
	}

	/**
	 *	Get list of AttributeSet classes loaded
	 */

	TArray<TSubclassOf<UAttributeSet> >	ClassList;
	for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
	{
		UClass* TestClass = *ClassIt;
		if (TestClass->IsChildOf(UAttributeSet::StaticClass()))
		{
			ClassList.Add(TestClass);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
			// This can only work right now on POD attribute sets. If we ever support FStrings or TArrays in AttributeSets
			// we will need to update this code to not use memcpy etc.
			for (TFieldIterator<UProperty> PropIt(TestClass, EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt)
			{
				if (!PropIt->HasAllPropertyFlags(CPF_IsPlainOldData))
				{
					ABILITY_LOG(Error, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to Handle AttributeClass %s because it has a non POD property: %s"),
						*TestClass->GetName(), *PropIt->GetName());
					return;
				}
			}
#endif
		}
	}

	/**
	 *	Loop through CurveData table and build sets of Defaults that keyed off of Name + Level
	 */

	for (auto It = CurveData->RowMap.CreateConstIterator(); It; ++It)
	{
		FString RowName = It.Key().ToString();
		FString ClassName;
		FString SetName;
		FString AttributeName;
		FString Temp;

		RowName.Split(TEXT("."), &ClassName, &Temp);
		Temp.Split(TEXT("."), &SetName, &AttributeName);

		if (!ensure(!ClassName.IsEmpty() && !SetName.IsEmpty() && !AttributeName.IsEmpty()))
		{
			ABILITY_LOG(Verbose, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to parse row %s in %s"), *RowName, *CurveData->GetName());
			continue;
		}

		// Find the AttributeSet

		TSubclassOf<UAttributeSet> Set = FindBestAttributeClass(ClassList, SetName);
		if (!Set)
		{
			// This is ok, we may have rows in here that don't correspond directly to attributes
			ABILITY_LOG(Verbose, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to match AttributeSet from %s (row: %s)"), *SetName, *RowName);
			continue;
		}

		// Find the UProperty

		UNumericProperty* Property = FindField<UNumericProperty>(*Set, *AttributeName);
		if (!Property)
		{
			ABILITY_LOG(Verbose, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to match Attribute from %s (row: %s)"), *AttributeName, *RowName);
			continue;
		}

		FRichCurve* Curve = It.Value();
		FName ClassFName = FName(*ClassName);
		FAttributeSetDefaulsCollection& DefaultCollection = Defaults.FindOrAdd(ClassFName);

		int32 LastLevel = Curve->GetLastKey().Time;
		DefaultCollection.LevelData.SetNum(FMath::Max(LastLevel, DefaultCollection.LevelData.Num()));

		
		//At this point we know the Name of this "class"/"group", the AttributeSet, and the Property Name. Now loop through the values on the curve to get the attribute default value at each level.
		for (auto KeyIter = Curve->GetKeyIterator(); KeyIter; ++KeyIter)
		{
			const FRichCurveKey& CurveKey = *KeyIter;

			int32 Level = CurveKey.Time;
			float Value = CurveKey.Value;

			FAttributeSetDefaults& SetDefaults = DefaultCollection.LevelData[Level-1];

			FAttributeDefaultValueList* DefaultDataList = SetDefaults.DataMap.Find(Set);
			if (DefaultDataList == nullptr)
			{
				ABILITY_LOG(Verbose, TEXT("Initializing new default set for %s[%d]. PropertySize: %d.. DefaultSize: %d"), *Set->GetName(), Level, Set->GetPropertiesSize(), UAttributeSet::StaticClass()->GetPropertiesSize());
				
				DefaultDataList = &SetDefaults.DataMap.Add(Set);
			}

			// Import curve value into default data

			check(DefaultDataList);
			DefaultDataList->AddPair(Property, Value);
		}


	}
}