# 新建 niagara renderer

# UNiagaraRendererProperties

这是个 niagara renderer 的资产,通过 Uobject 反射宏机制生成界面,让用户可以实现各种绑定材质和各种参数什么的,就是这东西:

Untitled

顺便说一下其他一些要用到类对应 niagara 界面控件

FNiagaraVariableAttributeBinding:

Untitled

FNiagaraVariable:

Untitled

说回 RendererProperties,RendererProperties 要通过函数注册到 Niagara editor 模组里面,这样就可以再 Niagara 界面里面调用了,这个函数最好写在插件 startup 函数里面。

1
2
3
4
5
6
7
8
9
10
11
12
#if WITH_EDITOR
FNiagaraEditorModule& NiagaraEditorModule = FNiagaraEditorModule::Get();
NiagaraEditorModule.RegisterRendererCreationInfo(FNiagaraRendererCreationInfo(
UNiagaraSkeletalRendererProperties::StaticClass()->GetDisplayNameText(),
FText::FromString(UNiagaraSkeletalRendererProperties::StaticClass()->GetDescription()),
UNiagaraSkeletalRendererProperties::StaticClass()->GetClassPathName(),
FNiagaraRendererCreationInfo::FRendererFactory::CreateLambda([](UObject* OuterEmitter)
{
UNiagaraSkeletalRendererProperties* NewRenderer = NewObject<UNiagaraSkeletalRendererProperties>(OuterEmitter, NAME_None, RF_Transactional);
return NewRenderer;
})));
#endif

首先我们要继承 UNiagaraRendererProperties 写一个类

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
UCLASS(editinlinenew,MinimalAPI, meta = (DisplayName = "Skeletal Renderer"))
class UNiagaraSkeletalRendererProperties : public UNiagaraRendererProperties
{
GENERATED_BODY()
public:
UNiagaraSkeletalRendererProperties();
//UObject接口,没什么好说的
virtual void PostLoad() override;
virtual void PostInitProperties() override;
//UObject Interface END

//初始化默认类 在模组startup调用他
static void InitCDOPropertiesAfterModuleStartup();

//~ UNiagaraRendererProperties interface
//renderer实现函数,准备数据向GPU发送drawcall,这函数会向InController添加一个你的renderer,最后一起调用renderer
virtual FNiagaraRenderer* CreateEmitterRenderer(ERHIFeatureLevel::Type FeatureLevel, const FNiagaraEmitterInstance* Emitter, const FNiagaraSystemInstanceController& InController) override;
virtual class FNiagaraBoundsCalculator* CreateBoundsCalculator() override { return nullptr; }
//是否支持GPU或者CPU模拟
virtual bool IsSimTargetSupported(ENiagaraSimTarget InSimTarget) const override { return InSimTarget == ENiagaraSimTarget::CPUSim; };
//提取真正要用的材质,例如你renderer里面有没有override material 或者userparameterBinding,还是用你原来的材质
virtual void GetUsedMaterials(const FNiagaraEmitterInstance* InEmitter, TArray<UMaterialInterface*>& OutMaterials) const override;
//储存要可能会在渲染中用到的值
virtual bool PopulateRequiredBindings(FNiagaraParameterStore& InParameterStore) override;
//缓存binding的值,给Renderer来用,不要混淆UNiagaraRendererProperties和FNiagaraRenderer
virtual void CacheFromCompiledData(const FNiagaraDataSetCompiledData* CompiledData) override;

//一些提示界面什么的接口
#if WITH_EDITORONLY_DATA
virtual void GetRendererWidgets(const FNiagaraEmitterInstance* InEmitter, TArray<TSharedPtr<SWidget>>& OutWidgets, TSharedPtr<FAssetThumbnailPool> InThumbnailPool) const override;
virtual const FSlateBrush* GetStackIcon() const override;
virtual void GetRendererTooltipWidgets(const FNiagaraEmitterInstance* InEmitter, TArray<TSharedPtr<SWidget>>& OutWidgets, TSharedPtr<FAssetThumbnailPool> InThumbnailPool) const override;
virtual const TArray<FNiagaraVariable>& GetOptionalAttributes() override;
virtual void GetRendererFeedback(const FVersionedNiagaraEmitter& InEmitter, TArray<FNiagaraRendererFeedback>& OutErrors, TArray<FNiagaraRendererFeedback>& OutWarnings, TArray<FNiagaraRendererFeedback>& OutInfo) const override;
#endif // WITH_EDITORONLY_DATA

//这个要不要开启Renderer的SystemPostTick,发射component要用到
virtual bool NeedsSystemPostTick() const override { return true; }
virtual bool NeedsSystemCompletion() const override { return true; }
//material parameters 改材质变量要用到,就是renderer下面哪里
virtual bool NeedsMIDsForMaterials() const override { return MaterialParameters.HasAnyBindings();
//binding
UPROPERTY(EditAnywhere, Category = "SkeletalRendering")
TArray<FNiagaraSkeletalReference> SkeletalMeshes;

UPROPERTY(EditAnywhere, Category = "SkeletalRendering")
TArray<TObjectPtr<UAnimationAsset>> Animations;

UPROPERTY(EditAnywhere, Category = "SkeletalRendering")
int32 RendererVisibility;

UPROPERTY(EditAnywhere, Category = "SkeletalRendering", meta = (ClampMin = 1))
uint32 ComponentCountLimit;

UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "SkeletalRendering")
bool bAssignComponentsOnParticleID = true;

UPROPERTY(EditAnywhere,Category = "Bindings")
FNiagaraVariableAttributeBinding PositionBinding;

UPROPERTY(EditAnywhere,Category = "Bindings")
FNiagaraVariableAttributeBinding RotationBinding;

UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding ScaleBinding;

UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding AnimTimeBinding;

UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding AnimIndexBinding;

UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding EnabledBinding;

UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding RendererVisibilityTagBinding;

UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraRendererMaterialParameters MaterialParameters;

//在CacheFromCompiledData把Binding值储存到Accessor给renderer用
FNiagaraDataSetAccessor<FNiagaraPosition> PositionAccessor;
FNiagaraDataSetAccessor<FVector3f> RotateAccessor;
FNiagaraDataSetAccessor<FVector3f> ScaleAccessor;
FNiagaraDataSetAccessor<FNiagaraBool> EnabledAccessor;
FNiagaraDataSetAccessor<float> AnimTimeAccessor;
FNiagaraDataSetAccessor<int32> VisTagAccessor;
FNiagaraDataSetAccessor<int32> AnimIndexAccessor;
FNiagaraDataSetAccessor<int32> UniqueIDAccessor;

protected:
void InitBindings();
static void InitDefaultAttributes();
static FNiagaraVariable Particles_Age;
static FNiagaraVariable Particles_Rotate;
static FNiagaraVariable Particles_AnimIndex;
static FNiagaraVariable Particles_Enabled;
private:
static TArray<TWeakObjectPtr<UNiagaraSkeletalRendererProperties>> SkeletalRendererPropertiesToDeferredInit;
}

这里就列出一些重要函数,其他函数都可以抄 UNiagaraGeometryCacheRendererProperties 的,就在 UNiagaraGeometryCacheRendererProperties.h 在 5.1 版本以上才有

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
FNiagaraVariable UNiagaraSkeletalRendererProperties::Particles_Age;
FNiagaraVariable UNiagaraSkeletalRendererProperties::Particles_Rotate;
FNiagaraVariable UNiagaraSkeletalRendererProperties::Particles_AnimIndex;
FNiagaraVariable UNiagaraSkeletalRendererProperties::Particles_Enabled;
TArray<TWeakObjectPtr<UNiagaraSkeletalRendererProperties>> UNiagaraSkeletalRendererProperties::SkeletalRendererPropertiesToDeferredInit;

#define LOCTEXT_NAMESPACE "UNiagaraSkeletalRendererProperties"

UNiagaraSkeletalRendererProperties::UNiagaraSkeletalRendererProperties()
{
AttributeBindings.Reserve(7);
AttributeBindings.Add(&PositionBinding);
AttributeBindings.Add(&RotationBinding);
AttributeBindings.Add(&ScaleBinding);
AttributeBindings.Add(&AnimTimeBinding);
AttributeBindings.Add(&AnimIndexBinding);
AttributeBindings.Add(&RendererVisibilityTagBinding);
AttributeBindings.Add(&EnabledBinding);
if(SkeletalMeshes.Num() == 0)
{
SkeletalMeshes.AddDefaulted();
}
SkeletalMeshes.Shrink();
}

void UNiagaraSkeletalRendererProperties::GetUsedMaterials(const FNiagaraEmitterInstance* InEmitter, TArray<UMaterialInterface*>& OutMaterials) const
{
for (const FNiagaraSkeletalReference& Entry : SkeletalMeshes)
{
if (Entry.SkeletalMesh)
{
TArray<FSkeletalMaterial> &SkeletalMaterials = Entry.SkeletalMesh->GetMaterials();
const int32 MaxIndex = FMath::Max(SkeletalMaterials.Num(), Entry.OverrideMaterials.Num());
OutMaterials.Reserve(MaxIndex);
for (int i = 0; i < MaxIndex; i++)
{
if (Entry.OverrideMaterials.IsValidIndex(i) )
{
if(Entry.OverrideMaterials[i].UserParamBinding.Parameter.IsValid())
{
UMaterialInterface* OverrideMatUsedBinding = Cast<UMaterialInterface>(InEmitter->FindBinding(Entry.OverrideMaterials[i].UserParamBinding.Parameter));

OutMaterials.Add(ToRawPtr(OverrideMatUsedBinding));
}else
{
OutMaterials.Add(ToRawPtr(Entry.OverrideMaterials[i].ExplicitMat));
}

}
else if (SkeletalMaterials.IsValidIndex(i))
{
OutMaterials.Add(SkeletalMaterials[i].MaterialInterface);
}
}
}
}

}
bool UNiagaraSkeletalRendererProperties::PopulateRequiredBindings(FNiagaraParameterStore& InParameterStore)
{
bool bAnyAdded = Super::PopulateRequiredBindings(InParameterStore);

for (const FNiagaraVariableAttributeBinding* Binding : AttributeBindings)
{
if (Binding && Binding->CanBindToHostParameterMap())
{
InParameterStore.AddParameter(Binding->GetParamMapBindableVariable(), false);
bAnyAdded = true;
}
}
for (FNiagaraMaterialAttributeBinding& MaterialParamBinding : MaterialParameters.AttributeBindings)
{
InParameterStore.AddParameter(MaterialParamBinding.GetParamMapBindableVariable(), false);
bAnyAdded = true;
}
for (const FNiagaraSkeletalReference& Entry : SkeletalMeshes)
{
FNiagaraVariable Variable = Entry.SkeletalMeshUserParameterBinding.Parameter;
if (Variable.IsValid())
{
InParameterStore.AddParameter(Variable, false);
bAnyAdded = true;
}
}

return bAnyAdded;

}
void UNiagaraSkeletalRendererProperties::CacheFromCompiledData(const FNiagaraDataSetCompiledData* CompiledData)
{
Super::CacheFromCompiledData(CompiledData);
InitParticleDataSetAccessor(PositionAccessor,CompiledData,PositionBinding);
InitParticleDataSetAccessor(RotateAccessor,CompiledData,RotationBinding);
InitParticleDataSetAccessor(ScaleAccessor,CompiledData,PositionBinding);
InitParticleDataSetAccessor(AnimTimeAccessor,CompiledData,AnimTimeBinding);
InitParticleDataSetAccessor(VisTagAccessor,CompiledData,RendererVisibilityTagBinding);
InitParticleDataSetAccessor(AnimIndexAccessor,CompiledData,AnimIndexBinding);
InitParticleDataSetAccessor(EnabledAccessor,CompiledData,EnabledBinding);
UniqueIDAccessor.Init(CompiledData, FName("UniqueID"));
}

提醒下,这里的 GetUsedMaterials 输出的 OutMaterials 被一个 BaseMaterials_GT 变量接收,在 niagaraRenderer.cpp 里的 Initialize 里面,这个 BaseMaterials_GT 要附加给我们 skeletal 里面

# FNiagaraRenderer

Renderer 就简单很多,要重新的接口就很少

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
struct FNiagaraParticleData
{
public:
FNiagaraParticleData(const UNiagaraSkeletalRendererProperties* Properties,FNiagaraDataSet& Data,int32 ParticleIndex);
FNiagaraPosition Position;
FVector3f Rotate ;
FVector3f Scale ;
float SkeletalAnimTime ;
int VisTag;
int AnimIndex ;
int32 UniqueID ;
bool Enabled;

};
class FNiagaraRendererSkeletal : public FNiagaraRenderer
{
public:

FNiagaraRendererSkeletal(ERHIFeatureLevel::Type FeatureLevel, const UNiagaraRendererProperties *InProps, const FNiagaraEmitterInstance* Emitter);
virtual ~FNiagaraRendererSkeletal();

//FNiagaraRenderer interface
//释放内存,删除所发射的component
virtual void DestroyRenderState_Concurrent() override;
//这里发射我们component
virtual void PostSystemTick_GameThread(const UNiagaraRendererProperties* InProperties, const FNiagaraEmitterInstance* Emitter) override;
virtual void OnSystemComplete_GameThread(const UNiagaraRendererProperties* InProperties, const FNiagaraEmitterInstance* Emitter) override;
//FNiagaraRenderer interface END

private:
struct FComponentPoolEntry
{
TWeakObjectPtr<USkeletalMeshComponent> Component;
double LastActiveTime = 0.0;
int32 LastAssignedToParticleID = -1;
};


// if the niagara component is not attached to an actor, we need to spawn and keep track of a temporary actor
TWeakObjectPtr<AActor> SpawnedOwner;

void ResetComponentPool(bool bResetOwner);
// all of the spawned components
TArray<FComponentPoolEntry> ComponentPool;

void SetSkeletalMaterials(const UNiagaraSkeletalRendererProperties* Properties,USkeletalMeshComponent* SkeletalMeshComponent,const FNiagaraEmitterInstance* Emitter,FNiagaraParticleData* PerParticleData);

};

cpp 文件主要列 PostSystemTick_GameThread 函数,其他函数抄 UNiagaraGeometryCacheRendererProperties 就行

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
FNiagaraParticleData::FNiagaraParticleData(const UNiagaraSkeletalRendererProperties* Properties, FNiagaraDataSet& Data, int32 ParticleIndex)
{

FNiagaraDataSetReaderInt32<FNiagaraBool> EnabledReader = Properties->EnabledAccessor.GetReader(Data);
const FNiagaraDataSetReaderFloat<FNiagaraPosition> PositionAccessor = Properties->PositionAccessor.GetReader(Data);
const FNiagaraDataSetReaderFloat<FVector3f> RotateAccessor = Properties->RotateAccessor.GetReader(Data);
const FNiagaraDataSetReaderFloat<FVector3f> ScaleAccessor = Properties->ScaleAccessor.GetReader(Data);
const FNiagaraDataSetReaderFloat<float> SkeletalAnimTimeAccessor = Properties->AnimTimeAccessor.GetReader(Data);
const FNiagaraDataSetReaderInt32<int> VisTagAccessor = Properties->VisTagAccessor.GetReader(Data);
const FNiagaraDataSetReaderInt32<int> AnimIndexAccessor = Properties->AnimIndexAccessor.GetReader(Data);
const FNiagaraDataSetReaderInt32<int32> UniqueIDReaderAccessor = Properties->UniqueIDAccessor.GetReader(Data);


Enabled = EnabledReader.GetSafe(ParticleIndex,true);
Position = PositionAccessor.GetSafe(ParticleIndex,FNiagaraPosition(ForceInit));
Rotate = RotateAccessor.GetSafe(ParticleIndex,FVector3f::Zero());
Scale = ScaleAccessor.GetSafe(ParticleIndex,FVector3f::One());
SkeletalAnimTime = SkeletalAnimTimeAccessor.GetSafe(ParticleIndex,0.0f);
VisTag = VisTagAccessor.GetSafe(ParticleIndex,0);
AnimIndex = AnimIndexAccessor.GetSafe(ParticleIndex,0);
UniqueID = UniqueIDReaderAccessor.GetSafe(ParticleIndex,-1);

}

void FNiagaraRendererSkeletal::PostSystemTick_GameThread(const UNiagaraRendererProperties* InProperties, const FNiagaraEmitterInstance* Emitter)
{
FNiagaraSystemInstance* SystemInstance = Emitter->GetParentSystemInstance();

//Bail if we don't have the required attributes to render this emitter.
const UNiagaraSkeletalRendererProperties* Properties = CastChecked<const UNiagaraSkeletalRendererProperties>(InProperties);
if (!SystemInstance || !Properties || Properties->SkeletalMeshes.Num() == 0 || SimTarget == ENiagaraSimTarget::GPUComputeSim)
{
return;
}

#if WITH_EDITORONLY_DATA
if (SystemInstance->GetIsolateEnabled() && !Emitter->GetEmitterHandle().IsIsolated())
{
ResetComponentPool(true);
return;
}
#endif
USceneComponent* AttachComponent = SystemInstance->GetAttachComponent();
if (!AttachComponent)
{
// we can't attach the components anywhere, so just bail
return;
}

//const FNiagaraParameterStore& ParameterStore = Emitter->GetRendererBoundVariables();

FNiagaraDataSet& Data = Emitter->GetData();
FNiagaraDataBuffer& ParticleData = Data.GetCurrentDataChecked();

const bool bIsRendererEnabled = IsRendererEnabled(InProperties, Emitter);
//这里检测component pool每个component状态,例如看他们的粒子有没有死,及时清理掉
TMap<int32, int32> ParticlesWithComponents;
TArray<int32> FreeList;
if (Properties->bAssignComponentsOnParticleID && ComponentPool.Num() > 0)
{
FreeList.Reserve(ComponentPool.Num());

// Determine the slots that were assigned to particles last frame
TMap<int32, int32> UsedSlots;
UsedSlots.Reserve(ComponentPool.Num());
for (int32 EntryIndex = 0; EntryIndex < ComponentPool.Num(); ++EntryIndex)
{
FComponentPoolEntry& Entry = ComponentPool[EntryIndex];
if (Entry.LastAssignedToParticleID >= 0)
{
UsedSlots.Emplace(Entry.LastAssignedToParticleID, EntryIndex);
}
else
{
FreeList.Add(EntryIndex);
}
}

// Ensure the final list only contains particles that are alive and enabled
ParticlesWithComponents.Reserve(UsedSlots.Num());
for (uint32 ParticleIndex = 0; ParticleIndex < ParticleData.GetNumInstances(); ParticleIndex++)
{
FNiagaraParticleData PerParticleData = FNiagaraParticleData(Properties,Data,ParticleIndex);
int32 ParticleID = PerParticleData.UniqueID;
int32 PoolIndex;

if (UsedSlots.RemoveAndCopyValue(ParticleID, PoolIndex))
{
if (PerParticleData.Enabled)
{
ParticlesWithComponents.Emplace(ParticleID, PoolIndex);
}
else
{
// Particle has disabled components since last tick, ensure the component for this entry gets deactivated before re-use
USceneComponent* Component = ComponentPool[PoolIndex].Component.Get();
if (Component && Component->IsActive())
{
Component->Deactivate();
Component->SetVisibility(false, true);
}
FreeList.Add(PoolIndex);
ComponentPool[PoolIndex].LastAssignedToParticleID = -1;
}
}
}

// Any remaining in the used slots are now free to be reclaimed, due to their particles either dying or having their component disabled
for (TPair<int32, int32> UsedSlot : UsedSlots)
{
// Particle has died since last tick, ensure the component for this entry gets deactivated before re-use
USceneComponent* Component = ComponentPool[UsedSlot.Value].Component.Get();
if (Component && Component->IsActive())
{
Component->Deactivate();
Component->SetVisibility(false, true);
}
FreeList.Add(UsedSlot.Value);
ComponentPool[UsedSlot.Value].LastAssignedToParticleID = -1;
}
}

const int32 MaxComponents = Properties->ComponentCountLimit;
int32 ComponentCount = 0;

//循环每个粒子
for(uint32 ParticleIndex = 0;ParticleIndex<ParticleData.GetNumInstances();ParticleIndex++)
{
FNiagaraParticleData PerParticleData = FNiagaraParticleData(Properties,Data,ParticleIndex);
if (!bIsRendererEnabled || !PerParticleData.Enabled)
{
// Skip particles that don't want a component
continue;
}

int32 ParticleID = -1;
int32 PoolIndex = -1;
if (Properties->bAssignComponentsOnParticleID)
{
// Get the particle ID and see if we have any components already assigned to the particle
ParticleID = PerParticleData.UniqueID;
ParticlesWithComponents.RemoveAndCopyValue(ParticleID, PoolIndex);
}

if (PoolIndex == -1 && ComponentCount + ParticlesWithComponents.Num() >= MaxComponents)
{
// The pool is full and there aren't any unused slots to claim
continue;
}

// Acquire a component for this particle
USkeletalMeshComponent* SkeletalMeshComponent = nullptr;
if (PoolIndex == -1)
{
// Start by trying to pull from the pool
if (!Properties->bAssignComponentsOnParticleID)
{
// We can just take the next slot
PoolIndex = ComponentCount < ComponentPool.Num() ? ComponentCount : -1;
}
else if (FreeList.Num())
{
PoolIndex = FreeList.Pop(false);
}
}

if (PoolIndex >= 0)
{
SkeletalMeshComponent = ComponentPool[PoolIndex].Component.Get();
}

bool bCreateNewComponent = !SkeletalMeshComponent || SkeletalMeshComponent->HasAnyFlags(RF_BeginDestroyed | RF_FinishDestroyed);


if(!Properties->SkeletalMeshes.IsValidIndex(PerParticleData.VisTag)||!Properties->SkeletalMeshes[PerParticleData.VisTag].SkeletalMesh)
{
return;
}

if(bCreateNewComponent)
{
AActor* OwnerActor = SpawnedOwner.Get();
if (OwnerActor == nullptr)
{
OwnerActor = AttachComponent->GetOwner();
if (OwnerActor == nullptr)
{
// NOTE: This can happen with spawned systems
OwnerActor = AttachComponent->GetWorld()->SpawnActor<AActor>();
OwnerActor->SetFlags(RF_Transient);
SpawnedOwner = OwnerActor;
}
}


int32 AnimeIndex = FMath::Min(PerParticleData.AnimIndex,Properties->Animations.Num() - 1);
int32 SkeletalIndex = FMath::Min(PerParticleData.VisTag,Properties->Animations.Num() - 1);

SkeletalMeshComponent = NewObject<USkeletalMeshComponent>(OwnerActor);
SkeletalMeshComponent->SetFlags(RF_Transient);
SkeletalMeshComponent->SetupAttachment(AttachComponent);
SkeletalMeshComponent->RegisterComponent();
SkeletalMeshComponent->AddTickPrerequisiteComponent(AttachComponent);
SkeletalMeshComponent->SetSkeletalMesh(Properties->SkeletalMeshes[SkeletalIndex].SkeletalMesh);
SkeletalMeshComponent->OverrideAnimationData(Properties->Animations[AnimeIndex],true,false,0.0f);
SetSkeletalMaterials(Properties,SkeletalMeshComponent,Emitter,&PerParticleData);

if (Emitter->GetCachedEmitterData()->bLocalSpace)
{
SkeletalMeshComponent->SetAbsolute(false, false, false);
}
else
{
SkeletalMeshComponent->SetAbsolute(true, true, true);
}

if (PoolIndex >= 0)
{
// This should only happen if the component was destroyed externally
ComponentPool[PoolIndex].Component = SkeletalMeshComponent;

}
else
{
// Add a new pool entry
PoolIndex = ComponentPool.Num();
ComponentPool.AddDefaulted_GetRef().Component = SkeletalMeshComponent;

}
}

const FNiagaraLWCConverter LwcConverter = SystemInstance->GetLWCConverter(Emitter->GetCachedEmitterData()->bLocalSpace);
FVector Position = LwcConverter.ConvertSimulationPositionToWorld(PerParticleData.Position);

SkeletalMeshComponent->SetFlags(RF_Transient);
SkeletalMeshComponent->SetupAttachment(AttachComponent);
SkeletalMeshComponent->AddTickPrerequisiteComponent(AttachComponent);
//SkeletalMeshComponent->SetSkeletalMesh(Properties->SkeletalMeshes[VisTag].SkeletalMesh);

FTransform Transform(FRotator(PerParticleData.Rotate.X, PerParticleData.Rotate.Y, PerParticleData.Rotate.Z), Position, FVector(PerParticleData.Scale));
SkeletalMeshComponent->SetRelativeTransform(Transform);
SkeletalMeshComponent->SetVisibility(PerParticleData.Enabled);
//SkeletalMeshComponent->MeshObject->
SkeletalMeshComponent->SetActive(true);
SkeletalMeshComponent->SetPosition(PerParticleData.SkeletalAnimTime);

FComponentPoolEntry& PoolEntry = ComponentPool[PoolIndex];
PoolEntry.LastAssignedToParticleID = ParticleID;
++ComponentCount;

if (ComponentCount >= MaxComponents)
{
// We've hit our prescribed limit
break;
}
}

//当有粒子死了,就把他从pool里面释放掉
if (ComponentCount < ComponentPool.Num())
{
// go over the pooled components we didn't need this tick to see if we can destroy some and deactivate the rest
for (int32 PoolIndex = 0; PoolIndex < ComponentPool.Num(); ++PoolIndex)
{
FComponentPoolEntry& PoolEntry = ComponentPool[PoolIndex];
if (Properties->bAssignComponentsOnParticleID)
{
if (PoolEntry.LastAssignedToParticleID >= 0)
{
// This one's in use
continue;
}
}
else if (PoolIndex < ComponentCount)
{
continue;
}

USceneComponent* Component = PoolEntry.Component.Get();
if (!Component)
{
if (Component)
{
Component->DestroyComponent();
}

// destroy the component pool slot
ComponentPool.RemoveAtSwap(PoolIndex, 1, false);
--PoolIndex;
continue;
}
else if (Component->IsActive())
{
Component->Deactivate();
Component->SetVisibility(false, true);
}
}
}
}

发射 component 有个缺点就是不能用 particle color 和 dynamic parameter 还有 material random 这些,要是想用这些数据要用新创建自己的顶点工厂和 uniform buffer,因为 cpu 要声明 uniformbuffer 上传到 gpu,然后材质是从 uniformbuffer 中读取值,这样的话,只能像其他 niagara renderer,重载 GetDynamicMeshElements 这个函数重新建 Meshbatch。例如 decal renderer

我相信引擎这个引擎肯定会解决的

更新于

请我喝[茶]~( ̄▽ ̄)~*

Natsuneko 微信支付

微信支付

Natsuneko 支付宝

支付宝

Natsuneko 贝宝

贝宝