Files
UnrealEngine/Engine/Plugins/Experimental/MeshModelingToolsetExp/Source/MeshModelingToolsEditorOnlyExp/Private/ShapeSprayTool.cpp
2025-05-18 13:04:45 +08:00

256 lines
6.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ShapeSprayTool.h"
#include "InteractiveToolManager.h"
#include "ToolBuilderUtil.h"
#include "ToolSetupUtil.h"
#include "Generators/MinimalBoxMeshGenerator.h"
#include "ModelingObjectsCreationAPI.h"
#include "Selection/ToolSelectionUtil.h"
#include "DynamicMesh/MeshNormals.h"
#include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(ShapeSprayTool)
using namespace UE::Geometry;
// localization namespace
#define LOCTEXT_NAMESPACE "UShapeSprayTool"
/*
* ToolBuilder
*/
UMeshSurfacePointTool* UShapeSprayToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const
{
UShapeSprayTool* NewTool = NewObject<UShapeSprayTool>(SceneState.ToolManager);
NewTool->SetWorld(SceneState.World);
return NewTool;
}
/*
* Tool
*/
UShapeSprayToolProperties::UShapeSprayToolProperties()
{
DropSpeed = 0.5;
Color = FLinearColor(0.25f, 0.08f, 0.32f);
bRandomColor = false;
ObjectSize = 20.0;
NumSplats = 1;
}
UShapeSprayTool::UShapeSprayTool()
{
}
void UShapeSprayTool::SetWorld(UWorld* World)
{
this->TargetWorld = World;
}
void UShapeSprayTool::Setup()
{
UDynamicMeshBrushTool::Setup();
// add settings object
Settings = NewObject<UShapeSprayToolProperties>(this, TEXT("Settings"));
AddToolPropertySource(Settings);
UpdateShapeMesh();
Random.Initialize(31337);
// create dynamic mesh component to use for live preview
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
AccumMeshComponent = NewObject<UDynamicMeshComponent>(TargetComponent->GetOwnerActor(), "SprayMesh");
AccumMeshComponent->SetupAttachment(TargetComponent->GetOwnerActor()->GetRootComponent());
AccumMeshComponent->RegisterComponent();
UMaterialInterface* VtxColorMaterial = GetToolManager()->GetContextQueriesAPI()->GetStandardMaterial(EStandardToolContextMaterials::VertexColorMaterial);
check(VtxColorMaterial);
if (VtxColorMaterial)
{
AccumMeshComponent->SetMaterial(0, VtxColorMaterial);
AccumMeshComponent->GetMesh()->EnableVertexColors(FVector3f::One());
AccumMeshComponent->GetMesh()->EnableVertexNormals(FVector3f::UnitX());
}
}
void UShapeSprayTool::Shutdown(EToolShutdownType ShutdownType)
{
UDynamicMeshBrushTool::Shutdown(ShutdownType);
if (ShutdownType == EToolShutdownType::Accept)
{
EmitResult();
}
if (AccumMeshComponent != nullptr)
{
AccumMeshComponent->UnregisterComponent();
AccumMeshComponent->DestroyComponent();
AccumMeshComponent = nullptr;
}
}
void UShapeSprayTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
{
UDynamicMeshBrushTool::OnPropertyModified(PropertySet, Property);
if (PropertySet == Settings)
{
if (Settings->Material != nullptr && AccumMeshComponent->GetMaterial(0) != Settings->Material)
{
AccumMeshComponent->SetMaterial(0, Settings->Material);
}
}
}
void UShapeSprayTool::OnBeginDrag(const FRay& Ray)
{
UDynamicMeshBrushTool::OnBeginDrag(Ray);
}
void UShapeSprayTool::OnUpdateDrag(const FRay& Ray)
{
UDynamicMeshBrushTool::OnUpdateDrag(Ray);
FFrame3f WorldFrame((FVector3f)LastBrushStamp.WorldPosition, (FVector3f)LastBrushStamp.WorldNormal);
FTransform3d Transform(Cast<IPrimitiveComponentBackedTarget>(Target)->GetWorldTransform());
FDynamicMesh3* Mesh = AccumMeshComponent->GetMesh();
float Radius = LastBrushStamp.Radius;
float DiscardT = 0.8 + (1-Settings->DropSpeed)*0.19;
for (int k = 0; k < Settings->NumSplats; ++k)
{
if (Random.GetFraction() < DiscardT)
{
continue;
}
float Angle = Random.GetFraction() * FMathf::TwoPi;
float Rad = Random.GetFraction() * Radius;
FVector3f PlanePt(Rad*FMathf::Cos(Angle), Rad*FMathf::Sin(Angle), 0);
FVector3f WorldPt = WorldFrame.PointAt(PlanePt);
FVector3f SampleRayDir = WorldPt - (FVector3f)Ray.Origin;
UE::Geometry::Normalize(SampleRayDir);
FRay WorldRay((FVector)Ray.Origin, (FVector)SampleRayDir);
FHitResult Hit;
if (HitTest(WorldRay, Hit))
{
float ObjSize = (1.0f + (Random.GetFraction()-0.5f) ) * (Settings->ObjectSize);
FFrame3d SurfFrame(
Transform.InverseTransformPosition((FVector3d)Hit.ImpactPoint),
Transform.InverseTransformVector((FVector3d)Hit.Normal));
SplatShape(SurfFrame, ObjSize, Mesh);
}
}
AccumMeshComponent->NotifyMeshUpdated();
}
void UShapeSprayTool::SplatShape(const FFrame3d& LocalFrame, double Scale, FDynamicMesh3* TargetMesh)
{
FLinearColor UseColor = (Settings->bRandomColor) ? FLinearColor::MakeRandomColor() : Settings->Color;
FQuaternionf Rotationf = (FQuaternionf)LocalFrame.Rotation;
VertexMap.Reset();
VertexMap.SetNum(ShapeMesh.MaxVertexID());
for (int vid : ShapeMesh.VertexIndicesItr())
{
FVector3d Pos = Scale * ShapeMesh.GetVertex(vid);
Pos = LocalFrame.PointAt(Pos.X, Pos.Y, Pos.Z);
VertexMap[vid] = TargetMesh->AppendVertex(Pos);
FVector3f Normal = Rotationf * ShapeMesh.GetVertexNormal(vid);
TargetMesh->SetVertexNormal(VertexMap[vid], Normal);
TargetMesh->SetVertexColor(VertexMap[vid], (FVector3f)UseColor);
}
for (int tid : ShapeMesh.TriangleIndicesItr())
{
FIndex3i Tri = ShapeMesh.GetTriangle(tid);
TargetMesh->AppendTriangle(VertexMap[Tri.A], VertexMap[Tri.B], VertexMap[Tri.C]);
}
}
void UShapeSprayTool::OnEndDrag(const FRay& Ray)
{
UDynamicMeshBrushTool::OnEndDrag(Ray);
}
void UShapeSprayTool::UpdateShapeMesh()
{
ShapeMesh = FDynamicMesh3();
FMinimalBoxMeshGenerator BoxGen;
ShapeMesh.Copy(&BoxGen.Generate());
ShapeMesh.EnableVertexNormals(FVector3f::UnitX());
for (int vid : ShapeMesh.VertexIndicesItr())
{
FVector3d Pos = ShapeMesh.GetVertex(vid);
UE::Geometry::Normalize(Pos);
ShapeMesh.SetVertexNormal(vid, (FVector3f)Pos);
}
}
bool UShapeSprayTool::CanAccept() const
{
return AccumMeshComponent->GetMesh()->TriangleCount() > 0;
}
void UShapeSprayTool::EmitResult()
{
const FDynamicMesh3* Mesh = AccumMeshComponent->GetMesh();
FTransform3d UseTransform(Cast<IPrimitiveComponentBackedTarget>(Target)->GetOwnerActor()->GetTransform());
GetToolManager()->BeginUndoTransaction(LOCTEXT("EmitShapeSpray", "Create ShapeSpray"));
FCreateMeshObjectParams NewMeshObjectParams;
NewMeshObjectParams.TargetWorld = TargetWorld;
NewMeshObjectParams.Transform = (FTransform)UseTransform;
NewMeshObjectParams.BaseName = TEXT("Spray");
NewMeshObjectParams.Materials.Add( ToolSetupUtil::GetDefaultMaterial() );
NewMeshObjectParams.SetMesh(Mesh);
FCreateMeshObjectResult Result = UE::Modeling::CreateMeshObject(GetToolManager(), MoveTemp(NewMeshObjectParams));
if (Result.IsOK() && Result.NewActor != nullptr)
{
ToolSelectionUtil::SetNewActorSelection(GetToolManager(), Result.NewActor);
}
GetToolManager()->EndUndoTransaction();
}
#undef LOCTEXT_NAMESPACE