mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 17:56:04 +08:00
269 lines
8.5 KiB
C++
269 lines
8.5 KiB
C++
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
|
|
|
|
#include "RuntimeMeshComponentEditorPrivatePCH.h"
|
|
#include "RuntimeMeshComponentDetails.h"
|
|
#include "RuntimeMeshComponent.h"
|
|
|
|
#include "DlgPickAssetPath.h"
|
|
#include "IAssetTools.h"
|
|
#include "AssetToolsModule.h"
|
|
#include "AssetRegistryModule.h"
|
|
#include "PhysicsEngine/PhysicsSettings.h"
|
|
#include "PhysicsEngine/BodySetup.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "RuntimeMeshComponentDetails"
|
|
|
|
TSharedRef<IDetailCustomization> FRuntimeMeshComponentDetails::MakeInstance()
|
|
{
|
|
return MakeShareable(new FRuntimeMeshComponentDetails);
|
|
}
|
|
|
|
void FRuntimeMeshComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
|
|
{
|
|
IDetailCategoryBuilder& RuntimeMeshCategory = DetailBuilder.EditCategory("RuntimeMesh");
|
|
|
|
const FText ConvertToStaticMeshText = LOCTEXT("ConvertToStaticMesh", "Create StaticMesh");
|
|
|
|
// Cache set of selected things
|
|
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 18
|
|
SelectedObjectsList = DetailBuilder.GetDetailsView().GetSelectedObjects();
|
|
#else
|
|
SelectedObjectsList = DetailBuilder.GetDetailsView()->GetSelectedObjects();
|
|
#endif
|
|
|
|
RuntimeMeshCategory.AddCustomRow(ConvertToStaticMeshText, false)
|
|
.NameContent()
|
|
[
|
|
SNullWidget::NullWidget
|
|
]
|
|
.ValueContent()
|
|
.VAlign(VAlign_Center)
|
|
.MaxDesiredWidth(250)
|
|
[
|
|
SNew(SButton)
|
|
.VAlign(VAlign_Center)
|
|
.ToolTipText(LOCTEXT("ConvertToStaticMeshTooltip", "Create a new StaticMesh asset using current geometry from this RuntimeMeshComponent. Does not modify instance."))
|
|
.OnClicked(this, &FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh)
|
|
.IsEnabled(this, &FRuntimeMeshComponentDetails::ConvertToStaticMeshEnabled)
|
|
.Content()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(ConvertToStaticMeshText)
|
|
]
|
|
];
|
|
}
|
|
|
|
URuntimeMeshComponent* FRuntimeMeshComponentDetails::GetFirstSelectedRuntimeMeshComp() const
|
|
{
|
|
// Find first selected valid RuntimeMeshComp
|
|
URuntimeMeshComponent* RuntimeMeshComp = nullptr;
|
|
for (const TWeakObjectPtr<UObject>& Object : SelectedObjectsList)
|
|
{
|
|
URuntimeMeshComponent* TestRuntimeComp = Cast<URuntimeMeshComponent>(Object.Get());
|
|
// See if this one is good
|
|
if (TestRuntimeComp != nullptr && !TestRuntimeComp->IsTemplate())
|
|
{
|
|
RuntimeMeshComp = TestRuntimeComp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return RuntimeMeshComp;
|
|
}
|
|
|
|
|
|
bool FRuntimeMeshComponentDetails::ConvertToStaticMeshEnabled() const
|
|
{
|
|
return GetFirstSelectedRuntimeMeshComp() != nullptr;
|
|
}
|
|
|
|
|
|
FReply FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh()
|
|
{
|
|
// Find first selected RuntimeMeshComp
|
|
URuntimeMeshComponent* RuntimeMeshComp = GetFirstSelectedRuntimeMeshComp();
|
|
if (RuntimeMeshComp != nullptr)
|
|
{
|
|
FString NewNameSuggestion = FString(TEXT("RuntimeMeshComp"));
|
|
FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
|
|
FString Name;
|
|
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
|
|
AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
|
|
|
|
TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget =
|
|
SNew(SDlgPickAssetPath)
|
|
.Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location"))
|
|
.DefaultAssetPath(FText::FromString(PackageName));
|
|
|
|
if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
|
|
{
|
|
// Get the full name of where we want to create the physics asset.
|
|
FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
|
|
FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
|
|
|
|
// Check if the user inputed a valid asset name, if they did not, give it the generated default name
|
|
if (MeshName == NAME_None)
|
|
{
|
|
// Use the defaults that were already generated.
|
|
UserPackageName = PackageName;
|
|
MeshName = *Name;
|
|
}
|
|
|
|
// Raw mesh data we are filling in
|
|
FRawMesh RawMesh;
|
|
|
|
// Unique Materials to apply to new mesh
|
|
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
|
|
TArray<FStaticMaterial> Materials;
|
|
#else
|
|
TArray<UMaterialInterface*> Materials;
|
|
#endif
|
|
|
|
bool bUseHighPrecisionTangents = false;
|
|
bool bUseFullPrecisionUVs = false;
|
|
|
|
const int32 NumSections = RuntimeMeshComp->GetNumSections();
|
|
int32 VertexBase = 0;
|
|
for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
|
|
{
|
|
const IRuntimeMeshVerticesBuilder* Vertices;
|
|
const FRuntimeMeshIndicesBuilder* Indices;
|
|
RuntimeMeshComp->GetSectionMesh(SectionIdx, Vertices, Indices);
|
|
|
|
if (Vertices->HasHighPrecisionNormals())
|
|
{
|
|
bUseHighPrecisionTangents = true;
|
|
}
|
|
if (Vertices->HasHighPrecisionUVs())
|
|
{
|
|
bUseFullPrecisionUVs = true;
|
|
}
|
|
|
|
// Copy verts
|
|
Vertices->Seek(-1);
|
|
while (Vertices->MoveNext() < Vertices->Length())
|
|
{
|
|
RawMesh.VertexPositions.Add(Vertices->GetPosition());
|
|
}
|
|
|
|
// Copy 'wedge' info
|
|
Indices->Seek(0);
|
|
while (Indices->HasRemaining())
|
|
{
|
|
int32 Index = Indices->ReadOne();
|
|
|
|
RawMesh.WedgeIndices.Add(Index + VertexBase);
|
|
|
|
|
|
Vertices->Seek(Index);
|
|
|
|
FVector TangentX = Vertices->GetTangent();
|
|
FVector TangentZ = Vertices->GetNormal();
|
|
FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * Vertices->GetNormal().W;
|
|
|
|
RawMesh.WedgeTangentX.Add(TangentX);
|
|
RawMesh.WedgeTangentY.Add(TangentY);
|
|
RawMesh.WedgeTangentZ.Add(TangentZ);
|
|
|
|
for (int UVIndex = 0; UVIndex < 8; UVIndex++)
|
|
{
|
|
if (!Vertices->HasUVComponent(UVIndex))
|
|
{
|
|
break;
|
|
}
|
|
RawMesh.WedgeTexCoords[UVIndex].Add(Vertices->GetUV(UVIndex));
|
|
}
|
|
|
|
RawMesh.WedgeColors.Add(Vertices->GetColor());
|
|
}
|
|
|
|
// Find a material index for this section.
|
|
UMaterialInterface* Material = RuntimeMeshComp->GetMaterial(SectionIdx);
|
|
|
|
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
|
|
int32 MaterialIndex = Materials.AddUnique(FStaticMaterial(Material));
|
|
#else
|
|
int32 MaterialIndex = Materials.AddUnique(Material);
|
|
#endif
|
|
|
|
|
|
// copy face info
|
|
int32 NumTris = Indices->Length() / 3;
|
|
for (int32 TriIdx=0; TriIdx < NumTris; TriIdx++)
|
|
{
|
|
// Set the face material
|
|
RawMesh.FaceMaterialIndices.Add(MaterialIndex);
|
|
|
|
RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
|
|
}
|
|
|
|
// Update offset for creating one big index/vertex buffer
|
|
VertexBase += Vertices->Length();
|
|
}
|
|
|
|
// If we got some valid data.
|
|
if (RawMesh.VertexPositions.Num() >= 3 && RawMesh.WedgeIndices.Num() >= 3)
|
|
{
|
|
// Then find/create it.
|
|
UPackage* Package = CreatePackage(NULL, *UserPackageName);
|
|
check(Package);
|
|
|
|
// Create StaticMesh object
|
|
UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
|
|
StaticMesh->InitResources();
|
|
|
|
StaticMesh->LightingGuid = FGuid::NewGuid();
|
|
|
|
// Add source to new StaticMesh
|
|
FStaticMeshSourceModel* SrcModel = new (StaticMesh->SourceModels) FStaticMeshSourceModel();
|
|
SrcModel->BuildSettings.bRecomputeNormals = false;
|
|
SrcModel->BuildSettings.bRecomputeTangents = false;
|
|
SrcModel->BuildSettings.bRemoveDegenerates = false;
|
|
SrcModel->BuildSettings.bUseHighPrecisionTangentBasis = bUseHighPrecisionTangents;
|
|
SrcModel->BuildSettings.bUseFullPrecisionUVs = bUseFullPrecisionUVs;
|
|
SrcModel->BuildSettings.bGenerateLightmapUVs = true;
|
|
SrcModel->BuildSettings.SrcLightmapIndex = 0;
|
|
SrcModel->BuildSettings.DstLightmapIndex = 1;
|
|
SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
|
|
|
|
// Set the materials used for this static mesh
|
|
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
|
|
StaticMesh->StaticMaterials = Materials;
|
|
int32 NumMaterials = StaticMesh->StaticMaterials.Num();
|
|
#else
|
|
StaticMesh->Materials = Materials;
|
|
int32 NumMaterials = StaticMesh->Materials.Num();
|
|
#endif
|
|
|
|
// Set up the SectionInfoMap to enable collision
|
|
for (int32 SectionIdx = 0; SectionIdx < NumMaterials; SectionIdx++)
|
|
{
|
|
FMeshSectionInfo Info = StaticMesh->SectionInfoMap.Get(0, SectionIdx);
|
|
Info.MaterialIndex = SectionIdx;
|
|
Info.bEnableCollision = true;
|
|
StaticMesh->SectionInfoMap.Set(0, SectionIdx, Info);
|
|
}
|
|
|
|
// Configure body setup for working collision.
|
|
StaticMesh->CreateBodySetup();
|
|
StaticMesh->BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
|
|
|
|
// Build mesh from source
|
|
StaticMesh->Build(false);
|
|
|
|
// Make package dirty.
|
|
StaticMesh->MarkPackageDirty();
|
|
|
|
StaticMesh->PostEditChange();
|
|
|
|
// Notify asset registry of new asset
|
|
FAssetRegistryModule::AssetCreated(StaticMesh);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE |