【目标】

植被工具-刷Actor

【思路】

1 添加类型FFoliageMeshInfo.AddInstance 的函数

2 添加Instance就直接SpawnActor

3 类结构

4 修改的函数

FEdModeFoliage.AddInstancesForBrush.

FEdModeFoliage.AddFoliageMesh

【步骤】

1 添加AcheType支持,添加FEdModeFoliage.AddFoliageAcheType 

2 添加一个AActor的表,用于记录ArcheType

  1. var    const native Map_MirrorFoliageArcheTypes{TMap<classAActor*,structFFoliageMeshInfo>};
 

3 MFoliageEditWindow.OnDrop,资源拖到面板上

  1. else
    {
    AActor*ArcheTypeActor=LoadObject<AActor>(NULL,*CurInfo.ObjectPathName, NULL, LOAD_None, NULL);
    if(ArcheTypeActor&&ArchetypeActor->HasAnyFlags(RF_ArchetypeObject))
    {
    FoliageEditSystem->AddFoliageAcheType(ArcheTypeActor);
    FoliageMeshesProperty->NotifyChanged();
    }
    }
 
MFoliageEditWindow.OnDragOver 过滤不支持的类型,添加Actor类型的支持
  1. for(TArray<FSelectedAssetInfo>::TConstIteratorDroppedAssetsIter(*(DroppedAssets.Get()));DroppedAssetsIter;++DroppedAssetsIter)
    {
    constFSelectedAssetInfo&CurInfo=*DroppedAssetsIter;
    if(CurInfo.ObjectClass!=UStaticMesh::StaticClass()&&CurInfo.ObjectClass!=AActor::StaticClass())
    {
    Args->Effects=DragDropEffects::None;
    break;
    }
还是拖不上去
在MFoliageEditWindow.OnDragEnter 中有
 
 
 
需要设置这个才能调试
 
 

4 MFoliageEditWindow.OnDragOver 改成 IsChildOf 判断

  1. voidOnDragOver(Object^Owner,DragEventArgs^Args)
    {
    Args->Effects=DragDropEffects::Copy;
    if(DroppedAssets.Get()!= NULL )
    {
    for(TArray<FSelectedAssetInfo>::TConstIteratorDroppedAssetsIter(*(DroppedAssets.Get()));DroppedAssetsIter;++DroppedAssetsIter)
    {
    constFSelectedAssetInfo&CurInfo=*DroppedAssetsIter;
    if(CurInfo.ObjectClass!=UStaticMesh::StaticClass()&&!CurInfo.ObjectClass->IsChildOf(AActor::StaticClass()))
    {
    Args->Effects=DragDropEffects::None;
    break;
    }
    }
    }
    Args->Handled= TRUE;
    }
 

5 目前拖上去没有效果显示,

MFoliageMeshWrapper

列表和FEdModeFoliage.GetFoliageMeshList 绑定了,所以显示了FFoliageMeshUIInfo列表

6 添加FFoliageArcheTypeUIInfo 继承于FFoliageMeshUIInfo 

  1.  structFFoliageResourceUIInfo:publicFFoliageMeshUIInfo
    {
    AActor*ArcheTypeActor;
    FFoliageResourceUIInfo(UStaticMesh*InStaticMesh,AActor*InArcheTypeActor,FFoliageMeshInfo*InMeshInfo)
    :FFoliageMeshUIInfo(InStaticMesh,InMeshInfo)
    :ArcheTypeActor(InArcheTypeActor)
    {}
    };
 

7 FEdModeFoliage.GetFoliageMeshList 返回值

  1.  TArray<structFFoliageResourceUIInfo>&GetFoliageMeshList();
 

8 FEdModeFoliage.FoliageMeshList 

  1. // Placed level data
    TArray<structFFoliageResourceUIInfo>FoliageMeshList;
 

9

  1.  IMPLEMENT_COMPARE_CONSTREF(FFoliageResourceUIInfo,FoliageEdMode,{return A.MeshInfo->Settings->DisplayOrder- B.MeshInfo->Settings->DisplayOrder;});
 

10 FEdModeFoliage.UpdateFoliageMeshList 

  1.  voidFEdModeFoliage::UpdateFoliageMeshList()
    {
    FoliageMeshList.Empty();
    AInstancedFoliageActor* IFA =AInstancedFoliageActor::GetInstancedFoliageActor();
    for(TMap<classUStaticMesh*,structFFoliageMeshInfo>::TIteratorMeshIt(IFA->FoliageMeshes);MeshIt;++MeshIt)
    {
    new(FoliageMeshList)FFoliageResourceUIInfo(MeshIt.Key(),NULL,&MeshIt.Value());
    }
    for(TMap<classAActor*,structFFoliageMeshInfo>::TIteratorArcheTypeIt(IFA->FoliageArcheTypes);ArcheTypeIt;++ArcheTypeIt)
    {
    new(FoliageArcheTypeList)FFoliageResourceUIInfo(NULL,ArcheTypeIt.Key(),&ArcheTypeIt.Value());
    }
    Sort<USE_COMPARE_CONSTREF(FFoliageResourceUIInfo,FoliageEdMode)>(&FoliageMeshList(),FoliageMeshList.Num());
    }
 

11 MFoliageMeshWrapper 类构造需要用新类FFoliageResourceUIInfo 

  1.  ref classMFoliageMeshWrapper:publicINotifyPropertyChanged
    {
    int index;
    FFoliageResourceUIInfo& mesh;
    UInstancedFoliageSettings* settings;
    BitmapSource^ bitmap;
    public:
    virtual event PropertyChangedEventHandler^PropertyChanged;
    MFoliageMeshWrapper( INT InIndex,FFoliageResourceUIInfo&InMesh)
    : index(InIndex)
    , mesh(InMesh)
    , settings(mesh.MeshInfo->Settings)
    {
    if(mesh.StaticMesh)
    bitmap =ThumbnailToolsCLR::GetBitmapSourceForObject(mesh.StaticMesh);
    elseif(mesh.ArcheTypeActor)
    bitmap =ThumbnailToolsCLR::GetBitmapSourceForObject(mesh.ArcheTypeActor);
    }
 

12

  1. typedefMEnumerableTArrayWrapper<MFoliageMeshWrapper,FFoliageResourceUIInfo>MFoliageMeshes;
 
终于看到效果了
 

13 跟FFoliageMeshUIInfo.StaticMesh 相关的修改

MFoliageMeshWrapper.StaticMeshName.get  获取名字的

  1.  propertyString^StaticMeshName{
    String^ get()
    {
    FStringName= mesh.StaticMesh? mesh.StaticMesh->GetName()? mesh.ArcheTypeActor->GetName();
    returnCLRTools::ToString(Name);
    }
    }
 
MFoliageEditWindow.FoliageMeshRemoveButton_Click 
  1.  // Remove the current mesh
    voidFoliageMeshRemoveButton_Click(Object^Owner,ExecutedRoutedEventArgs^Args)
    {
    MFoliageMeshWrapper^Mesh= safe_cast<MFoliageMeshWrapper^>(Args->Parameter);
    if(Mesh->GetStaticMesh())
    FoliageEditSystem->RemoveFoliageMesh(Mesh->GetStaticMesh());
    else
    FoliageEditSystem->RemoveFoliageAcheType(Mesh->GetArcheTypeActor());
    FoliageMeshesProperty->NotifyChanged();
    }
 
 

添加MFoliageMeshWrapper.GetStaticMesh

  1. AActor*GetStaticMesh(){return mesh.ArcheTypeActor;}
 

MFoliageEditWindow.FoliageMeshCopySettings_Click

  1. voidFoliageMeshUseSettings_Click(Object^Owner,ExecutedRoutedEventArgs^Args)
    {
    GCallbackEvent->Send(CALLBACK_LoadSelectedAssetsIfNeeded);
    USelection*SelectedSet=GEditor->GetSelectedSet(UInstancedFoliageSettings::StaticClass());
    UInstancedFoliageSettings*SelectedSettings=Cast<UInstancedFoliageSettings>(SelectedSet->GetTop(UInstancedFoliageSettings::StaticClass()));
    if(SelectedSettings!= NULL )
    {
    MFoliageMeshWrapper^Mesh= safe_cast<MFoliageMeshWrapper^>(Args->Parameter);
    if(Mesh->GetStaticMesh())
    FoliageEditSystem->ReplaceSettingsObject(Mesh->GetStaticMesh(),SelectedSettings);
    else
    FoliageEditSystem->ReplaceSettingsObject(Mesh->GetArcheTypeActor(),SelectedSettings);
 
MFoliageEditWindow.FoliageMeshReplaceButton_Click 
  1. if(SelectedActor&&SelectedActor->HasAnyFlags(RF_ArchetypeObject))
    {
    MFoliageMeshWrapper^Mesh= safe_cast<MFoliageMeshWrapper^>(Args->Parameter);
    FoliageEditSystem->ReplaceArchtypeActor(Mesh->GetArcheTypeActor(),SelectedActor);
    FoliageMeshesProperty->NotifyChanged();
    }
 

13 FEdModeFoliage.ReplaceArchtypeActor 

  1.  voidFEdModeFoliage::ReplaceArchtypeActor(AActor*OldArcheType,AActor*NewArcheType)
    {
    AInstancedFoliageActor* IFA =AInstancedFoliageActor::GetInstancedFoliageActor();
    FFoliageMeshInfo*OldMeshInfo= IFA->FoliageArcheTypes.Find(OldArcheType);
    if(OldMeshInfo!= NULL &&OldArcheType!=NewArcheType&&NewArcheType->HasAnyFlags(RF_ArchetypeObject))
    {
    INT InstancesNum=OldMeshInfo->Instances.Num()-OldMeshInfo->FreeInstanceIndices.Num();
    // Look for the new mesh in the mesh list, and either create a new mesh or merge the instances.
    FFoliageMeshInfo*NewMeshInfo= IFA->FoliageArcheTypes.Find(NewArcheType);
    if(NewMeshInfo== NULL )
    {
    if(InstancesNum>&&
    appMsgf(AMT_YesNo,LocalizeSecure(LocalizeUnrealEd("FoliageMode_ReplaceMesh"),InstancesNum,*OldArcheType->GetName(),*NewArcheType->GetName()))!= ART_Yes )
    {
    return;
    }
    GEditor->BeginTransaction(*LocalizeUnrealEd("FoliageMode_ChangeStaticMeshTransaction"));
    IFA->Modify();
    NewMeshInfo= IFA->AddArcheType(NewArcheType);
    NewMeshInfo->Settings->DisplayOrder=OldMeshInfo->Settings->DisplayOrder;
    NewMeshInfo->Settings->ShowNothing=OldMeshInfo->Settings->ShowNothing;
    NewMeshInfo->Settings->ShowPaintSettings=OldMeshInfo->Settings->ShowPaintSettings;
    NewMeshInfo->Settings->ShowInstanceSettings=OldMeshInfo->Settings->ShowInstanceSettings;
    }
    else
    if(InstancesNum>&&
    appMsgf(AMT_YesNo,LocalizeSecure(LocalizeUnrealEd("FoliageMode_ReplaceMeshMerge"),InstancesNum,*OldArcheType->GetName(),*NewArcheType->GetName()))!= ART_Yes )
    {
    return;
    }
    else
    {
    GEditor->BeginTransaction(*LocalizeUnrealEd("FoliageMode_ChangeStaticMeshTransaction"));
    IFA->Modify();
    }
    if(InstancesNum>)
    {
    // copy instances from old to new.
    for( INT Idx=;Idx<OldMeshInfo->Instances.Num();Idx++)
    {
    if(OldMeshInfo->Instances(Idx).ClusterIndex!=-)
    {
    NewMeshInfo->AddInstanceAT( IFA,NewArcheType,OldMeshInfo->Instances(Idx));
    }
    }
    }
    // Remove the old mesh.
    IFA->RemoveArcheType(OldArcheType);
    GEditor->EndTransaction();
    // Update mesh list.
    UpdateFoliageMeshList();
    }
    }
 

14 添加FFoliageMeshInfo.AddInstanceAT

  1.  voidFFoliageMeshInfo::AddInstanceAT(classAInstancedFoliageActor*InIFA,classAActor*InActor,constFFoliageInstance&InNewInstance)
    {
    }
 

目前是刷不上Actor模型吗,主要是FEdModeFoliage.ApplyBrush 函数里没有处理

15 修改FEdModeFoliage.ApplyBrush 

  1.  // ArcheType
    for(TMap<classAActor*,structFFoliageMeshInfo>::TIteratorArcheTypeIt(IFA->FoliageArcheTypes);ArcheTypeIt;++ArcheTypeIt)
    {
    FFoliageMeshInfo&MeshInfo=ArcheTypeIt.Value();
    UInstancedFoliageSettings*MeshSettings=MeshInfo.Settings;
    if(MeshSettings->IsSelected)
    {
    // Find the instances already in the area.
    TArray<INT>Instances;
    FSphereBrushSphere(BrushLocation,UISettings.GetRadius());
    MeshInfo.GetInstancesInsideSphere(BrushSphere,Instances);
    if(UISettings.GetLassoSelectToolSelected())
    {
    // Shift unpaints
    MeshInfo.SelectInstances( IFA,!IsShiftDown(ViewportClient->Viewport),Instances);
    }
    else
    if(UISettings.GetReapplyToolSelected())
    {
    if(MeshSettings->ReapplyDensity)
    {
    // Adjust instance density
    FMeshInfoSnapshot*SnapShot=InstanceATSnapshot.Find(ArcheTypeIt.Key());
    if(SnapShot)
    {
    // Use snapshot to determine number of instances at the start of the brush stroke
    INT NewInstanceCount= appRound((FLOAT)SnapShot->CountInstancesInsideSphere(BrushSphere)*MeshSettings->ReapplyDensityAmount);
    if(MeshSettings->ReapplyDensityAmount>.f&&NewInstanceCount>Instances.Num())
    {
    AddInstancesATForBrush( IFA,ArcheTypeIt.Key(),MeshInfo,NewInstanceCount,Instances,Pressure);
    }
    else
    if(MeshSettings->ReapplyDensityAmount<.f&&NewInstanceCount<Instances.Num())
    {
    RemoveInstancesForBrush( IFA,MeshInfo,NewInstanceCount,Instances,Pressure);
    }
    }
    }
    // Reapply any settings checked by the user
    ReapplyInstancesForBrush( IFA,MeshInfo,Instances);
    }
    else
    if(UISettings.GetPaintToolSelected())
    {
    // Shift unpaints
    if(IsShiftDown(ViewportClient->Viewport))
    {
    INT DesiredInstanceCount= appRound(BrushArea*MeshSettings->Density*UISettings.GetUnpaintDensity()/(.f*.f));
    if(DesiredInstanceCount<Instances.Num())
    {
    RemoveInstancesForBrush( IFA,MeshInfo,DesiredInstanceCount,Instances,Pressure);
    }
    }
    else
    {
    // This is the total set of instances disregarding parameters like slope, height or layer.
    FLOAT DesiredInstanceCountFloat=BrushArea*MeshSettings->Density*UISettings.GetPaintDensity()/(.f*.f);
    // Allow a single instance with a random chance, if the brush is smaller than the density
    INT DesiredInstanceCount=DesiredInstanceCountFloat>.f? appRound(DesiredInstanceCountFloat): appFrand()<DesiredInstanceCountFloat?:;
    AddInstancesATForBrush( IFA,ArcheTypeIt.Key(),MeshInfo,DesiredInstanceCount,Instances,Pressure);
    }
    }
    }
    }
 

16 添加Instance的函数FEdModeFoliage.AddInstancesATForBrush 

  1.  /** Add instances inside the brush to match DesiredInstanceCount */
    voidFEdModeFoliage::AddInstancesATForBrush(AInstancedFoliageActor* IFA,AActor*AchetypeActor,FFoliageMeshInfo&MeshInfo, INT DesiredInstanceCount,TArray<INT>&ExistingInstances, FLOAT Pressure)
    {
    UInstancedFoliageSettings*MeshSettings=MeshInfo.Settings;
    if(DesiredInstanceCount>ExistingInstances.Num())
    {
    INT ExistingInstanceBuckets[NUM_INSTANCE_BUCKETS];
    appMemzero(ExistingInstanceBuckets,sizeof(ExistingInstanceBuckets));
    // Cache store mapping between component and weight data
    TMap<ULandscapeComponent*,TArray<BYTE>>*LandscapeLayerCache= NULL;
    FNameLandscapeLayerName=MeshSettings->LandscapeLayer;
    if(LandscapeLayerName!= NAME_None )
    {
    LandscapeLayerCache=&LandscapeLayerCaches.FindOrAdd(LandscapeLayerName);
    // Find the landscape weights of existing ExistingInstances
    for( INT Idx=;Idx<ExistingInstances.Num();Idx++)
    {
    FFoliageInstance&Instance=MeshInfo.Instances(ExistingInstances(Idx));
    ULandscapeHeightfieldCollisionComponent*HitLandscapeCollision=Cast<ULandscapeHeightfieldCollisionComponent>(Instance.Base);
    if(HitLandscapeCollision)
    {
    ULandscapeComponent*HitLandscape=HitLandscapeCollision->GetLandscapeComponent();
    if(HitLandscape)
    {
    TArray<BYTE>*LayerCache=&LandscapeLayerCache->FindOrAdd(HitLandscape);
    FLOAT HitWeight=HitLandscape->GetLayerWeightAtLocation(Instance.Location,LandscapeLayerName,LayerCache);
    // Add count to bucket.
    ExistingInstanceBuckets[appRound(HitWeight*(FLOAT)(NUM_INSTANCE_BUCKETS-))]++;
    }
    }
    }
    }
    else
    {
    // When not tied to a layer, put all the ExistingInstances in the last bucket.
    ExistingInstanceBuckets[NUM_INSTANCE_BUCKETS-]=ExistingInstances.Num();
    }
    // We calculate a set of potential ExistingInstances for the brush area.
    TArray<FPotentialInstance>PotentialInstanceBuckets[NUM_INSTANCE_BUCKETS];
    appMemzero(PotentialInstanceBuckets,sizeof(PotentialInstanceBuckets));
    // Quick lookup of potential instance locations, used for overlapping check.
    TArray<FVector>PotentialInstanceLocations;
    FFoliageInstanceHashPotentialInstanceHash();// use 128x128 cell size, as the brush radius is typically small.
    PotentialInstanceLocations.Empty(DesiredInstanceCount);
    // Radius where we expect to have a single instance, given the density rules
    const FLOAT DensityCheckRadius=Max<FLOAT>( appSqrt((.f*.f)/(PI *MeshSettings->Density)),MeshSettings->Radius);
    for( INT DesiredIdx=;DesiredIdx<DesiredInstanceCount;DesiredIdx++)
    {
    FVectorStart,End;
    GetRandomVectorInBrush(Start,End);
    FCheckResultHit;
    if(!GWorld->SingleLineCheck(Hit, NULL,End,Start, TRACE_World | TRACE_Level,FVector(.f,.f,.f), NULL))
    {
    // Check filters
    if((Hit.Component&&
    (Hit.Component->GetOutermost()!=GWorld->CurrentLevel->GetOutermost()||
    (!UISettings.bFilterLandscape &&Hit.Component->IsA(ULandscapeHeightfieldCollisionComponent::StaticClass()))||
    (!UISettings.bFilterStaticMesh &&Hit.Component->IsA(UStaticMeshComponent::StaticClass()))||
    (!UISettings.bFilterTerrain &&Hit.Component->IsA(UTerrainComponent::StaticClass()))))||
    (Hit.Actor&&Hit.Actor->IsA(AWorldInfo::StaticClass())&&(!UISettings.bFilterBSP ||GWorld->Levels(Hit.LevelIndex)!=GWorld->CurrentLevel)))
    {
    continue;
    }
    if(!CheckLocationForPotentialInstance(MeshInfo,MeshSettings,DensityCheckRadius,Hit.Location,Hit.Normal,PotentialInstanceLocations,PotentialInstanceHash))
    {
    continue;
    }
    // Check landscape layer
    FLOAT HitWeight=.f;
    if(LandscapeLayerName!= NAME_None )
    {
    ULandscapeHeightfieldCollisionComponent*HitLandscapeCollision=Cast<ULandscapeHeightfieldCollisionComponent>(Hit.Component);
    if(HitLandscapeCollision)
    {
    ULandscapeComponent*HitLandscape=HitLandscapeCollision->GetLandscapeComponent();
    if(HitLandscape)
    {
    TArray<BYTE>*LayerCache=&LandscapeLayerCache->FindOrAdd(HitLandscape);
    HitWeight=HitLandscape->GetLayerWeightAtLocation(Hit.Location,LandscapeLayerName,LayerCache);
    // Reject instance randomly in proportion to weight
    if(HitWeight<= appFrand())
    {
    continue;
    }
    }
    }
    }
    new(PotentialInstanceBuckets[appRound(HitWeight*(FLOAT)(NUM_INSTANCE_BUCKETS-))])FPotentialInstance(Hit.Location,Hit.Normal,Hit.Component,HitWeight);
    }
    }
    for( INT BucketIdx=;BucketIdx< NUM_INSTANCE_BUCKETS;BucketIdx++)
    {
    TArray<FPotentialInstance>&PotentialInstances=PotentialInstanceBuckets[BucketIdx];
    FLOAT BucketFraction=(FLOAT)(BucketIdx+)/(FLOAT)NUM_INSTANCE_BUCKETS;
    // We use the number that actually succeeded in placement (due to parameters) as the target
    // for the number that should be in the brush region.
    INT AdditionalInstances=Clamp<INT>( appRound(BucketFraction*(FLOAT)(PotentialInstances.Num()-ExistingInstanceBuckets[BucketIdx])*Pressure),,PotentialInstances.Num());
    for( INT Idx=;Idx<AdditionalInstances;Idx++)
    {
    FFoliageInstanceInst=PotentialInstances(Idx).PlaceInstance(MeshSettings);
    MeshInfo.AddInstanceAT( IFA,AchetypeActor,Inst);
    }
    }
    }
    }
 
17 在FFoliageInstanceCluster 上添加Actor列表成员
  1.  structFFoliageInstanceCluster
    {
    UInstancedStaticMeshComponent*ClusterComponent;
    TArray<AActor*>ActorInstances;
    FFoliageInstanceCluster.GetInstanceCount
    INT GetInstanceCount()
    {
    INT TotalCount=;
    if(ClusterComponent)
    TotalCount+=ClusterComponent->PerInstanceSMData.Num();
    TotalCount+=ActorInstances.Num();
    }
 
 

18 添加FFoliageMeshInfo.AddInstanceAT 

  1.  voidFFoliageMeshInfo::AddInstanceAT(classAInstancedFoliageActor*InIFA,classAActor*InArchetypeActor,constFFoliageInstance&InNewInstance)
    {
    InIFA->Modify();
    // Add the instance taking either a free slot or adding a new item.
    INT InstanceIndex=FreeInstanceIndices.Num()>?FreeInstanceIndices.Pop():Instances.Add();
    FFoliageInstance&AddedInstance=Instances(InstanceIndex);
    AddedInstance=InNewInstance;
    // Add the instance to the hash
    InstanceHash->InsertInstance(InNewInstance.Location,InstanceIndex);
    FFoliageComponentHashInfo&ComponentHashInfo=ComponentHash.FindOrAddKey(InNewInstance.Base);
    ComponentHashInfo.Instances.Add(InstanceIndex);
    // Find the best cluster to allocate the instance to.
    FFoliageInstanceCluster*BestCluster= NULL;
    INT BestClusterIndex= INDEX_NONE;
    FLOAT BestClusterDistSq= FLT_MAX;
    INT MaxInstancesPerCluster=Settings->MaxInstancesPerCluster;
    FLOAT MaxClusterRadiusSq=Square(Settings->MaxClusterRadius);
    for( INT ClusterIdx=;ClusterIdx<InstanceClusters.Num();ClusterIdx++)
    {
    FFoliageInstanceCluster&Cluster=InstanceClusters(ClusterIdx);
    if(Cluster.InstanceIndices.Num()<MaxInstancesPerCluster)
    {
    FLOAT DistSq=(Cluster.Bounds.Origin-InNewInstance.Location).SizeSquared();
    if(DistSq<BestClusterDistSq&&DistSq<MaxClusterRadiusSq)
    {
    BestCluster=&Cluster;
    BestClusterIndex=ClusterIdx;
    BestClusterDistSq=DistSq;
    }
    }
    }
    // Calculate transform for the instance
    FMatrixInstanceTransform=InNewInstance.GetInstanceTransform();
    if(BestCluster== NULL )
    {
    BestClusterIndex=InstanceClusters.Num();
    BestCluster=new(InstanceClusters)FFoliageInstanceCluster(
    NULL,
    FBoxSphereBounds()// LWF_TODO
    );
    //ApplyInstancedFoliageSettings( BestCluster->ClusterComponent );
    }
    else
    {
    // BestCluster->ClusterComponent->Modify();
    // BestCluster->ClusterComponent->InvalidateLightingCache();
    // BestCluster->Bounds = BestCluster->Bounds + InMesh->Bounds.TransformBy(InstanceTransform);
    }
    BestCluster->InstanceIndices.AddItem(InstanceIndex);
    // Save the cluster index
    AddedInstance.ClusterIndex=BestClusterIndex;
    // Add the instance to the ActorList
    AActor* pActor =GWorld->SpawnActor(InArchetypeActor->GetClass(), NAME_None,InNewInstance.Location,InNewInstance.Rotation,InArchetypeActor);// Spawn Actor
    BestCluster->ActorInstances.AddItem(pActor);
    // FInstancedStaticMeshInstanceData* NewInstanceData = new(BestCluster->ClusterComponent->PerInstanceSMData) FInstancedStaticMeshInstanceData();
    if(BestCluster->ClusterComponent->SelectedInstances.Num()>)
    {
    BestCluster->ClusterComponent->SelectedInstances.AddItem(FALSE);
    }
    // NewInstanceData->Transform = InstanceTransform;
    // NewInstanceData->LightmapUVBias = FVector2D( -1.0f, -1.0f );
    // NewInstanceData->ShadowmapUVBias = FVector2D( -1.0f, -1.0f );
    // BestCluster->ClusterComponent->bNeedsReattach = TRUE;
    #if _DEBUG
    CheckValid();
    #endif
    InIFA->ConditionalUpdateComponents();
    }
 

18 修改所有FFoliageInstanceCluster.ClusterComponent 的地方

太多地方了

FFoliageMeshInfo.CheckValid

终于能刷出东西来了

 

UE3植被工具-支持刷Actor)的更多相关文章

  1. WordPress让文本小工具支持简码

    WordPress 的 “文本” 小工具是非常常用的,可以添加一些自定义的文本或者 Html 代码.但很多时候,我们需要在文本小工具里使用简码来添加一些更加丰富的内容. 默认情况下,文本小工具是不支持 ...

  2. LiveBlox无需代码的开发工具--支持win macos ubuntu等开发环境--

    LiveBlox无需代码的开发工具-支持windows macos ubuntu. 强大 灵活 易于使用 视频简介:LiveBlox Develop Technology Without Coding ...

  3. 文档资源搜索小工具 - 支持PDF,DOC,PPT,XLS

    最近做了一个文档搜索小工具,当然不是网盘搜索工具,这个工具支持四种文件格式搜索(pdf,doc,ppt,xls),你只需要在搜索框中输入你想要搜索资源的关键词,点击搜索按钮即可获取相关资源,点击下载按 ...

  4. 3-WIN10系统及开发工具支持

    本篇博客对应视频讲解 回顾 上一讲说了编程的方向和技术流派以及选择入门语言的建议.当我们决定我们的选择之后呢,我们就要学习和进行实践操作了.但在实践之前,我们仍然需要做好相应的准备,这也就是今天要讲的 ...

  5. WinSetupFromUSB - 超简单制作多合一系统安装启动U盘的工具 (支持Win/PE/Linux启动盘)

    很多同学都喜欢将电脑凌乱不堪的系统彻底重装以获得一个"全新的开始",但你会发现如今很多电脑都已经没有光驱了,因此制作一个U盘版的系统安装启动盘备用是非常必要的. 我们之前推荐过 I ...

  6. 【C#附源码】数据库文档生成工具支持(Excel+Html)

    [2015] 很多时候,我们在生成数据库文档时,使用某些工具,可效果总不理想,不是内容不详细,就是表现效果一般般.很多还是word.html的.看着真是别扭.本人习惯用Excel,所以闲暇时,就简单的 ...

  7. 【C#附源码】数据库文档生成工具支持(Excel+Htm)

    数据库文档生成工具是用C#开发的基于NPOI组件的小工具.软件源码大小不到10MB.支持生成Excel 和Html 两种文档形式.了解更多,请访问:http://www.oschina.net/cod ...

  8. 全新WayOS 配置文件保存工具支持蓝色界面路由版本

    一直以来都有群里的朋友要求我弄一个支持蓝色界面路由的参数备份工具,也一直拖了大半年 昨天忙到4点多,早上又因为一些小的BUG被用户电话叫起,干脆就帮你们整一个这个工具了 功能还是一样,支持各种参数的保 ...

  9. 通过自研数据库画像工具支持“去O”评估

    “去O”,是近些年来一直很火的一个话题,随之也产生了各种疑惑,包括现有数据库评估.技术选型等.去O是项系统工程,需要做好充分的评估.本文通过自研工具,生成数据库画像,为去O评估提供一手数据,希望给大家 ...

随机推荐

  1. git server服务器搭建

    http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/00137583770360579 ...

  2. Unity的安装和破解

    网址:unity3d.com/cn/   unity的破解软件可以去unity圣典的网站上下载: 点击资源库,在资源库中找 下载过程中有时会提示需要对应的VS版本,忽略掉这个错误,并不需要最新的VS, ...

  3. 《zw版·delphi与Halcon系列原创教程》THOperatorSetX版hello,zw

    <zw版·delphi与Halcon系列原创教程>THOperatorSetX版hello,zw 下面介绍v3版的hello,zw. Halcon两大核心控件,THImagex.THOpe ...

  4. 构建高性能的ASP.NET应用程序

    看见大标题的时候,也许各位看官会自然而然的联想到如何在设计阶段考虑系统性能问题,如何编写高性能的程序代码.关于这一点,大家可以在MSDN和相关网站上找到非常多的介绍,不过大多是防患于未难,提供的是在设 ...

  5. imail 删除历史邮件命令

    删除旧的邮件(immsgexp.exe)Immsgexp.exe 可以让管理员删除指定天数的旧的邮件.基本语法 immsgexp -t startdirectory -d #of_days_to_sa ...

  6. Linux+I2C总线分析(主要是probe的方式)

    Linux I2C 总线浅析 ㈠ Overview Linux的I2C体系结构分为3个组成部分: ·I2C核心: I2C核心提供了I2C总线驱动和设备驱动的注册.注销方法,I2C通信方法(即“algo ...

  7. Java 菜鸟学习之 script脚本语句

    脚本语句  一般分为  顺序语句   分支语句   循环语句 顺序语句  就是从上到下依次读取 分支语句 是if语句 if(判断条件) { 满足条件执行的语句 } else { 不满足条件执行的语句 ...

  8. Eclipse默认标签TODO,XXX,FIXME和自定义标签[转]

    http://www.blogjava.net/Guides/archive/2011/11/14/363686.html   Eclipse中的一些特殊的注释技术包括:    1.    // TO ...

  9. jQuery 遍历(上)

    目录: 一:定义二:遍历 DOM三:jQuery 参考手册 - 遍历 定义:什么是遍历?jQuery 遍历,意为"移动",用于根据其相对于其他元素的关系来"查找" ...

  10. C/C++ 宏中的 #、#@、##的作用

    宏中的# 功能是将其后面的宏参数进行字符串化操作(Stringizing operator), 简单说就是在它引用的宏变量的左右各加上一个双引号. #define STRING(x) #x 下面二条语 ...