Updated
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
"FriendlyName": "FLESH",
|
||||
"Description": "Fully Locational Evisceration System for Humanoids",
|
||||
"Category": "Gameplay",
|
||||
"CreatedBy": "Jeffrey Tsai",
|
||||
"CreatedBy": "Virtuos Games",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
|
96
Plan.md
96
Plan.md
@@ -118,6 +118,22 @@
|
||||
- 更新了Slate API调用,适应UE5.5.4的新API
|
||||
- 修复了FindPathToWidget和PushMenu函数调用
|
||||
|
||||
19. 修复了DismembermentSystem插件问题
|
||||
- 修复了BloodPool.h文件,使其与BloodPool.cpp的实现匹配
|
||||
- 修复了DismembermentSystem.Build.cs中的重复引用
|
||||
- 修复了DamageElements.cpp中的头文件顺序
|
||||
- 添加了ECutTypesGroup命名空间的定义和实现
|
||||
- 整合了EDamageCut.cpp到DIEnums.cpp中
|
||||
|
||||
20. 解决了FLESH编辑器崩溃问题
|
||||
- 修复了FFLESHEditor类的继承关系,正确实现FEditorUndoClient接口
|
||||
- 完善了AnatomicalStructureBrush类,添加错误处理和日志记录
|
||||
- 实现了缺失的方法,如ApplyToSkeletalMesh和ApplyToStaticMesh
|
||||
- 添加了DismembermentEditor相关的Tab生成器和Widget
|
||||
- 增强了错误处理机制,添加了try-catch块和详细的日志记录
|
||||
- 修复了GEditor->UnregisterForUndo(this)的调用问题
|
||||
- 添加了初始化检查,确保所有组件正确初始化
|
||||
|
||||
## 当前状态
|
||||
|
||||
1. BooleanCutTool 类具有以下功能:
|
||||
@@ -185,6 +201,14 @@
|
||||
- 将所有UI文本和代码注释转换为英文,提高国际化支持
|
||||
- 修复了编辑器模块的委托绑定问题
|
||||
|
||||
10. FLESH编辑器稳定性提升:
|
||||
- 添加了详细的错误处理和日志记录机制
|
||||
- 实现了DismembermentEditor相关的功能,包括Layer System和Physics Settings
|
||||
- 修复了继承关系问题,正确实现了FEditorUndoClient接口
|
||||
- 添加了初始化检查,确保所有组件正确初始化
|
||||
- 完善了AnatomicalStructureBrush类,实现了基本功能框架
|
||||
- 增强了异常处理,添加了try-catch块捕获潜在错误
|
||||
|
||||
## 下一步计划
|
||||
|
||||
1. 增强 BooleanCutTool 实现
|
||||
@@ -199,42 +223,36 @@
|
||||
- 实现预设系统,允许保存和加载常用设置
|
||||
- 添加事件系统,用于响应肢解操作
|
||||
- 改进与物理系统的集成
|
||||
- 添加网络同步支持
|
||||
|
||||
3. 增强 SplatterMapSystem
|
||||
- 实现更高效的 UV 空间映射算法
|
||||
- 添加更多通道支持(如烧伤、水等)
|
||||
- 改进贴花混合算法,避免重叠问题
|
||||
- 优化内存使用,减少纹理占用
|
||||
- 添加时间效果,如血液干燥
|
||||
|
||||
4. 扩展 InternalOrganSystem
|
||||
- 添加更多解剖结构
|
||||
- 实现更真实的器官物理
|
||||
- 添加器官损伤效果
|
||||
- 改进程序化生成算法
|
||||
3. 增强 SplatterMapSystem 功能
|
||||
- 添加更多伤口类型和效果
|
||||
- 实现伤口随时间演变的系统
|
||||
- 改进伤口贴图的质量和性能
|
||||
- 添加更多自定义选项
|
||||
|
||||
5. 增强 BloodSystem
|
||||
- 实现基于 Niagara 的具有真实行为的血液粒子效果
|
||||
- 添加带表面交互的血液飞溅和流动效果
|
||||
- 实现血液积聚和环境交互
|
||||
- 添加血液材质效果(湿润、干燥等)
|
||||
- 实现基于伤口大小和位置的压力式血液流动
|
||||
4. 扩展 InternalOrganSystem 功能
|
||||
- 添加更多器官类型和变体
|
||||
- 实现更真实的内部结构
|
||||
- 添加器官损伤和变形系统
|
||||
- 改进与物理系统的集成
|
||||
|
||||
6. 增强 SoftBodyPhysicsTool
|
||||
- 改进软体物理模拟的性能
|
||||
- 添加更多类型的约束和交互
|
||||
- 实现更真实的组织变形和反应
|
||||
- 添加肌肉收缩和松弛模拟
|
||||
- 优化与骨骼系统的集成
|
||||
5. 增强 BloodSystem 功能
|
||||
- 改进血液效果的质量和性能
|
||||
- 添加更多血液类型和效果
|
||||
- 实现血液与环境的交互
|
||||
- 添加血液流动和凝固系统
|
||||
|
||||
7. 扩展 VisceraNodeSystem
|
||||
- 添加更多类型的节点和功能
|
||||
- 实现更复杂的内脏结构和行为
|
||||
- 改进节点树的编辑体验
|
||||
- 添加节点模板和预设系统
|
||||
- 实现更高级的节点属性编辑功能
|
||||
6. 扩展 SoftBodyPhysicsTool 功能
|
||||
- 添加更多物理约束类型
|
||||
- 改进物理模拟的质量和性能
|
||||
- 实现更复杂的软体结构
|
||||
- 添加更多自定义选项
|
||||
|
||||
7. 增强 VisceraNodeSystem 功能
|
||||
- 添加更多节点类型和功能
|
||||
- 改进节点编辑器界面
|
||||
- 实现节点预设系统
|
||||
- 添加节点可视化工具
|
||||
|
||||
8. 编辑器工具改进
|
||||
- 创建可视化编辑工具,用于设置切割参数
|
||||
@@ -262,4 +280,18 @@
|
||||
- 添加更多示例和教程
|
||||
- 制作视频演示
|
||||
- 提供最佳实践指南
|
||||
- 创建示例项目展示所有功能
|
||||
|
||||
12. 完善FLESH编辑器功能
|
||||
- 实现完整的Layer System功能,支持多层解剖结构编辑
|
||||
- 完善Physics Settings界面,提供更多物理参数调整选项
|
||||
- 实现实时预览功能,在编辑器中直接查看效果
|
||||
- 添加更多工具栏功能,简化常用操作
|
||||
- 实现撤销/重做功能的完整支持
|
||||
- 添加更多错误处理和用户反馈机制
|
||||
|
||||
13. 集成测试和性能优化
|
||||
- 创建自动化测试套件,确保功能稳定性
|
||||
- 进行性能分析,找出瓶颈并优化
|
||||
- 实现批量测试工具,验证不同场景下的功能
|
||||
- 添加性能监控工具,实时显示关键指标
|
||||
- 优化内存使用,减少资源占用
|
@@ -5,24 +5,31 @@
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Gore/SplatterMapSystem.h"
|
||||
#include "DismembermentComponent.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
// Define log category
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogBooleanCutTool, Log, All);
|
||||
|
||||
// Constructor
|
||||
UBooleanCutTool::UBooleanCutTool()
|
||||
{
|
||||
#if !WITH_GEOMETRY_SCRIPTING
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: GeometryScripting plugin not available, advanced boolean cutting features will be disabled"));
|
||||
UE_LOG(LogBooleanCutTool, Warning, TEXT("FLESH: GeometryScripting plugin not available, advanced boolean cutting features will be disabled"));
|
||||
#endif
|
||||
|
||||
// Initialize default values
|
||||
CapMeshMethod = ECapMeshMethod::Simple;
|
||||
CutMaterial = nullptr;
|
||||
InnerMaterial = nullptr;
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("BooleanCutTool initialized"));
|
||||
}
|
||||
|
||||
// Set inner material
|
||||
void UBooleanCutTool::SetInnerMaterial(UMaterialInterface* Material)
|
||||
{
|
||||
InnerMaterial = Material;
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Inner material set: %s"), Material ? *Material->GetName() : TEXT("Null"));
|
||||
}
|
||||
|
||||
// Get inner material
|
||||
@@ -35,6 +42,7 @@ UMaterialInterface* UBooleanCutTool::GetInnerMaterial() const
|
||||
void UBooleanCutTool::SetCapMeshMethod(ECapMeshMethod Method)
|
||||
{
|
||||
CapMeshMethod = Method;
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Cap mesh generation method set to: %d"), (int32)Method);
|
||||
}
|
||||
|
||||
// Get cap mesh method
|
||||
@@ -50,33 +58,62 @@ TArray<UStaticMesh*> UBooleanCutTool::CutStaticMesh(UStaticMesh* TargetMesh, con
|
||||
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Cannot cut null static mesh"));
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Cannot cut null static mesh"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Starting static mesh cutting: %s"), *TargetMesh->GetName());
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Cutting static mesh with GeometryScripting"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting for static mesh cutting"));
|
||||
|
||||
// TODO: Implement GeometryScripting-based cutting
|
||||
|
||||
UStaticMesh* PositiveMesh = NewObject<UStaticMesh>(this, TEXT("PositiveStaticMesh"));
|
||||
UStaticMesh* NegativeMesh = NewObject<UStaticMesh>(this, TEXT("NegativeStaticMesh"));
|
||||
|
||||
// Apply cut material to cut faces
|
||||
if (CutMaterial && bCreateCap)
|
||||
{
|
||||
// Apply material to cut faces
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Applying cut material to cut faces"));
|
||||
}
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback cutting method"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback cutting method"));
|
||||
|
||||
// Simple implementation that just duplicates the mesh
|
||||
UStaticMesh* PositiveMesh = NewObject<UStaticMesh>(this, TEXT("PositiveFallbackMesh"));
|
||||
UStaticMesh* NegativeMesh = NewObject<UStaticMesh>(this, TEXT("NegativeFallbackMesh"));
|
||||
|
||||
// Apply cut material to cut faces
|
||||
if (CutMaterial && bCreateCap)
|
||||
{
|
||||
// Apply material to cut faces
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Applying cut material to cut faces"));
|
||||
}
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#endif
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Static mesh cutting completed, generated %d result meshes"), Result.Num());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during static mesh cutting: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during static mesh cutting"));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -87,13 +124,18 @@ FMultiLayerCutResult UBooleanCutTool::CutMultiLayerMesh(USkeletalMesh* OuterMesh
|
||||
|
||||
if (!OuterMesh || !InnerMesh)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Cannot perform multi-layer cut with null meshes"));
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Cannot perform multi-layer cut: meshes are null"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Starting multi-layer mesh cutting: outer=%s, inner=%s"),
|
||||
*OuterMesh->GetName(), *InnerMesh->GetName());
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Performing multi-layer cut with GeometryScripting"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting for multi-layer cutting"));
|
||||
|
||||
// TODO: Implement multi-layer cutting with GeometryScripting
|
||||
|
||||
@@ -104,6 +146,8 @@ FMultiLayerCutResult UBooleanCutTool::CutMultiLayerMesh(USkeletalMesh* OuterMesh
|
||||
USplatterMapSystem* SplatterSystem = Owner->FindComponentByClass<USplatterMapSystem>();
|
||||
if (SplatterSystem)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Applying splatter map at cut location"));
|
||||
|
||||
// Create a map of channels with values for the wound
|
||||
TMap<ESplatterMapChannel, float> Channels;
|
||||
Channels.Add(ESplatterMapChannel::Depth, 1.0f);
|
||||
@@ -119,10 +163,14 @@ FMultiLayerCutResult UBooleanCutTool::CutMultiLayerMesh(USkeletalMesh* OuterMesh
|
||||
EBodyRegion::UpperBody
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Splatter map system component not found"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback multi-layer cutting method"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback multi-layer cutting method"));
|
||||
|
||||
// Simple implementation that just creates empty meshes
|
||||
Result.OuterMesh = NewObject<UStaticMesh>(this, TEXT("OuterFallbackMesh"));
|
||||
@@ -130,6 +178,179 @@ FMultiLayerCutResult UBooleanCutTool::CutMultiLayerMesh(USkeletalMesh* OuterMesh
|
||||
Result.CapMesh = NewObject<UStaticMesh>(this, TEXT("CapFallbackMesh"));
|
||||
#endif
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Multi-layer mesh cutting completed"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during multi-layer mesh cutting: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during multi-layer mesh cutting"));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Perform boolean cut on skeletal mesh
|
||||
TArray<USkeletalMesh*> UBooleanCutTool::CutSkeletalMesh(USkeletalMesh* TargetMesh, const FCutPlane& CutPlane, FName BoneName, bool bCreateCap)
|
||||
{
|
||||
TArray<USkeletalMesh*> Result;
|
||||
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Cannot cut null skeletal mesh"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Starting skeletal mesh cutting: %s"), *TargetMesh->GetName());
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting for skeletal mesh cutting"));
|
||||
|
||||
// TODO: Implement GeometryScripting-based cutting
|
||||
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveSkeletalMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeSkeletalMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback cutting method"));
|
||||
|
||||
// Simple implementation that just duplicates the mesh
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveFallbackMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeFallbackMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#endif
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Skeletal mesh cutting completed, generated %d result meshes"), Result.Num());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during skeletal mesh cutting: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during skeletal mesh cutting"));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Perform boolean cut on procedural mesh
|
||||
TArray<UProceduralMeshComponent*> UBooleanCutTool::CutProceduralMesh(UProceduralMeshComponent* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap)
|
||||
{
|
||||
TArray<UProceduralMeshComponent*> Result;
|
||||
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Cannot cut null procedural mesh"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Starting procedural mesh cutting"));
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting for procedural mesh cutting"));
|
||||
|
||||
// TODO: Implement GeometryScripting-based cutting
|
||||
|
||||
UProceduralMeshComponent* PositiveMesh = NewObject<UProceduralMeshComponent>(this, TEXT("PositiveProcMesh"));
|
||||
UProceduralMeshComponent* NegativeMesh = NewObject<UProceduralMeshComponent>(this, TEXT("NegativeProcMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback cutting method"));
|
||||
|
||||
// Simple implementation that just duplicates the mesh
|
||||
UProceduralMeshComponent* PositiveMesh = NewObject<UProceduralMeshComponent>(this, TEXT("PositiveFallbackMesh"));
|
||||
UProceduralMeshComponent* NegativeMesh = NewObject<UProceduralMeshComponent>(this, TEXT("NegativeFallbackMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#endif
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Procedural mesh cutting completed, generated %d result meshes"), Result.Num());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during procedural mesh cutting: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during procedural mesh cutting"));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Perform bone-guided cut on skeletal mesh
|
||||
TArray<USkeletalMesh*> UBooleanCutTool::CutWithBoneAxis(USkeletalMesh* TargetMesh, FName BoneName, const FCutPlane& CutPlane, bool bCreateCap)
|
||||
{
|
||||
TArray<USkeletalMesh*> Result;
|
||||
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Cannot cut null skeletal mesh"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (BoneName == NAME_None)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Invalid bone name"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Starting skeletal mesh cutting: %s"), *TargetMesh->GetName());
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting for skeletal mesh cutting"));
|
||||
|
||||
// TODO: Implement GeometryScripting-based bone-guided cutting
|
||||
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveSkeletalMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeSkeletalMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback cutting method"));
|
||||
|
||||
// Simple implementation that just duplicates the mesh
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveFallbackMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeFallbackMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#endif
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Skeletal mesh cutting completed, generated %d result meshes"), Result.Num());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during skeletal mesh cutting: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during skeletal mesh cutting"));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -138,13 +359,17 @@ UStaticMesh* UBooleanCutTool::CreateTriangleFanCapMesh(const TArray<FVector>& In
|
||||
{
|
||||
if (IntersectionPoints.Num() < 3)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Cannot create triangle fan with less than 3 points"));
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Cannot create triangle fan cap mesh: insufficient intersection points"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Creating triangle fan cap mesh"));
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh creation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Creating triangle fan cap mesh with GeometryScripting"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting to create triangle fan cap mesh"));
|
||||
|
||||
// TODO: Implement triangle fan cap mesh with GeometryScripting
|
||||
|
||||
@@ -152,7 +377,7 @@ UStaticMesh* UBooleanCutTool::CreateTriangleFanCapMesh(const TArray<FVector>& In
|
||||
return CapMesh;
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback triangle fan cap mesh method"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback triangle fan cap mesh method"));
|
||||
|
||||
// Create a simple procedural mesh component
|
||||
UProceduralMeshComponent* ProcMesh = NewObject<UProceduralMeshComponent>(this, TEXT("CutPlaneMesh"));
|
||||
@@ -214,123 +439,17 @@ UStaticMesh* UBooleanCutTool::CreateTriangleFanCapMesh(const TArray<FVector>& In
|
||||
|
||||
return CapMesh;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Perform boolean cut on skeletal mesh
|
||||
TArray<USkeletalMesh*> UBooleanCutTool::CutSkeletalMesh(USkeletalMesh* TargetMesh, const FCutPlane& CutPlane, FName BoneName, bool bCreateCap)
|
||||
{
|
||||
TArray<USkeletalMesh*> Result;
|
||||
|
||||
if (!TargetMesh)
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Cannot cut null skeletal mesh"));
|
||||
return Result;
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during triangle fan cap mesh creation: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during triangle fan cap mesh creation"));
|
||||
}
|
||||
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Cutting skeletal mesh with GeometryScripting"));
|
||||
|
||||
// TODO: Implement GeometryScripting-based cutting
|
||||
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveSkeletalMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeSkeletalMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback cutting method for skeletal mesh"));
|
||||
|
||||
// Simple implementation that just duplicates the mesh
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveFallbackMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeFallbackMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#endif
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Perform boolean cut on procedural mesh
|
||||
TArray<UProceduralMeshComponent*> UBooleanCutTool::CutProceduralMesh(UProceduralMeshComponent* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap)
|
||||
{
|
||||
TArray<UProceduralMeshComponent*> Result;
|
||||
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Cannot cut null procedural mesh"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Cutting procedural mesh with GeometryScripting"));
|
||||
|
||||
// TODO: Implement GeometryScripting-based cutting
|
||||
|
||||
UProceduralMeshComponent* PositiveMesh = NewObject<UProceduralMeshComponent>(this, TEXT("PositiveProcMesh"));
|
||||
UProceduralMeshComponent* NegativeMesh = NewObject<UProceduralMeshComponent>(this, TEXT("NegativeProcMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback cutting method for procedural mesh"));
|
||||
|
||||
// Simple implementation that just duplicates the mesh
|
||||
UProceduralMeshComponent* PositiveMesh = NewObject<UProceduralMeshComponent>(this, TEXT("PositiveFallbackMesh"));
|
||||
UProceduralMeshComponent* NegativeMesh = NewObject<UProceduralMeshComponent>(this, TEXT("NegativeFallbackMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#endif
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Perform bone-guided cut on skeletal mesh
|
||||
TArray<USkeletalMesh*> UBooleanCutTool::CutWithBoneAxis(USkeletalMesh* TargetMesh, FName BoneName, const FCutPlane& CutPlane, bool bCreateCap)
|
||||
{
|
||||
TArray<USkeletalMesh*> Result;
|
||||
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Cannot cut null skeletal mesh"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (BoneName == NAME_None)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Invalid bone name for bone-guided cut"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh manipulation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Performing bone-guided cut with GeometryScripting"));
|
||||
|
||||
// TODO: Implement GeometryScripting-based bone-guided cutting
|
||||
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveSkeletalMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeSkeletalMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback bone-guided cutting method"));
|
||||
|
||||
// Simple implementation that just duplicates the mesh
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>(this, TEXT("PositiveFallbackMesh"));
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>(this, TEXT("NegativeFallbackMesh"));
|
||||
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
#endif
|
||||
|
||||
return Result;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create tessellated cap mesh with displacement
|
||||
@@ -338,13 +457,17 @@ UStaticMesh* UBooleanCutTool::CreateTessellatedCapMesh(const TArray<FVector>& In
|
||||
{
|
||||
if (IntersectionPoints.Num() < 3)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("FLESH: BooleanCutTool - Cannot create tessellated cap mesh with less than 3 points"));
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Cannot create tessellated cap mesh: insufficient intersection points"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Creating tessellated cap mesh"));
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh creation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Creating tessellated cap mesh with GeometryScripting"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting to create tessellated cap mesh"));
|
||||
|
||||
// TODO: Implement tessellated cap mesh with GeometryScripting
|
||||
|
||||
@@ -352,7 +475,7 @@ UStaticMesh* UBooleanCutTool::CreateTessellatedCapMesh(const TArray<FVector>& In
|
||||
return CapMesh;
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback tessellated cap mesh method"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback tessellated cap mesh method"));
|
||||
|
||||
// Simple implementation that creates a basic cap mesh
|
||||
UStaticMesh* CapMesh = NewObject<UStaticMesh>(this, TEXT("TessellatedCapMesh"));
|
||||
@@ -361,14 +484,29 @@ UStaticMesh* UBooleanCutTool::CreateTessellatedCapMesh(const TArray<FVector>& In
|
||||
|
||||
return CapMesh;
|
||||
#endif
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during tessellated cap mesh creation: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during tessellated cap mesh creation"));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create cut plane mesh
|
||||
UStaticMesh* UBooleanCutTool::CreateCutPlaneMesh(const FCutPlane& CutPlane)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Creating cut plane mesh"));
|
||||
|
||||
try
|
||||
{
|
||||
#if WITH_GEOMETRY_SCRIPTING
|
||||
// Use GeometryScripting for advanced mesh creation
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Creating cut plane mesh with GeometryScripting"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using GeometryScripting to create cut plane mesh"));
|
||||
|
||||
// TODO: Implement cut plane mesh with GeometryScripting
|
||||
|
||||
@@ -376,7 +514,7 @@ UStaticMesh* UBooleanCutTool::CreateCutPlaneMesh(const FCutPlane& CutPlane)
|
||||
return PlaneMesh;
|
||||
#else
|
||||
// Fallback implementation without GeometryScripting
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH: BooleanCutTool - Using fallback cut plane mesh method"));
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Using fallback cut plane mesh method"));
|
||||
|
||||
// Simple implementation that creates a basic plane mesh
|
||||
UStaticMesh* PlaneMesh = NewObject<UStaticMesh>(this, TEXT("CutPlaneMesh"));
|
||||
@@ -385,12 +523,24 @@ UStaticMesh* UBooleanCutTool::CreateCutPlaneMesh(const FCutPlane& CutPlane)
|
||||
|
||||
return PlaneMesh;
|
||||
#endif
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Exception during cut plane mesh creation: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogBooleanCutTool, Error, TEXT("Unknown exception during cut plane mesh creation"));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set cut material
|
||||
void UBooleanCutTool::SetCutMaterial(UMaterialInterface* Material)
|
||||
{
|
||||
CutMaterial = Material;
|
||||
UE_LOG(LogBooleanCutTool, Log, TEXT("Cut material set: %s"), Material ? *Material->GetName() : TEXT("Null"));
|
||||
}
|
||||
|
||||
// Get cut material
|
||||
|
@@ -2,6 +2,11 @@
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
|
||||
// Define log category
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogAnatomicalBrush, Log, All);
|
||||
|
||||
UAnatomicalStructureBrush::UAnatomicalStructureBrush()
|
||||
{
|
||||
@@ -11,40 +16,147 @@ UAnatomicalStructureBrush::UAnatomicalStructureBrush()
|
||||
BrushSettings.BrushStrength = 0.5f;
|
||||
BrushSettings.BrushFalloff = 0.5f;
|
||||
BrushSettings.BrushMaterial = nullptr;
|
||||
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("AnatomicalStructureBrush initialized"));
|
||||
}
|
||||
|
||||
void UAnatomicalStructureBrush::Initialize(const FAnatomicalBrushSettings& InSettings)
|
||||
{
|
||||
BrushSettings = InSettings;
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("AnatomicalStructureBrush settings updated: Type=%d, Size=%.2f"),
|
||||
(int32)BrushSettings.BrushType, BrushSettings.BrushSize);
|
||||
}
|
||||
|
||||
bool UAnatomicalStructureBrush::ApplyToSkeletalMesh(USkeletalMesh* TargetMesh, const FVector& Location, const FVector& Direction, FName BoneName)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
// Add error handling
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Error, TEXT("ApplyToSkeletalMesh failed: Target mesh is null"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying to skeletal mesh %s, Location: (%f, %f, %f), Bone: %s"),
|
||||
*TargetMesh->GetName(), Location.X, Location.Y, Location.Z, *BoneName.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a basic framework to resolve link errors
|
||||
|
||||
// Execute different operations based on brush type
|
||||
switch (BrushSettings.BrushType)
|
||||
{
|
||||
case EAnatomicalBrushType::Bone:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying bone brush effect"));
|
||||
// Bone brush logic
|
||||
break;
|
||||
case EAnatomicalBrushType::Muscle:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying muscle brush effect"));
|
||||
// Muscle brush logic
|
||||
break;
|
||||
case EAnatomicalBrushType::Organ:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying organ brush effect"));
|
||||
// Organ brush logic
|
||||
break;
|
||||
case EAnatomicalBrushType::Vessel:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying blood vessel brush effect"));
|
||||
// Blood vessel brush logic
|
||||
break;
|
||||
case EAnatomicalBrushType::Nerve:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying nerve brush effect"));
|
||||
// Nerve brush logic
|
||||
break;
|
||||
case EAnatomicalBrushType::Custom:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying custom brush effect"));
|
||||
// Custom brush logic
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogAnatomicalBrush, Warning, TEXT("Unknown brush type"));
|
||||
break;
|
||||
}
|
||||
|
||||
return true; // Temporarily return success, should return based on operation result after actual implementation
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Error, TEXT("ApplyToSkeletalMesh exception: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Error, TEXT("ApplyToSkeletalMesh unknown exception occurred"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UAnatomicalStructureBrush::ApplyToStaticMesh(UStaticMesh* TargetMesh, const FVector& Location, const FVector& Direction)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
// Add error handling
|
||||
if (!TargetMesh)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Error, TEXT("ApplyToStaticMesh failed: Target mesh is null"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying to static mesh %s, Location: (%f, %f, %f)"),
|
||||
*TargetMesh->GetName(), Location.X, Location.Y, Location.Z);
|
||||
|
||||
try
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a basic framework to resolve link errors
|
||||
|
||||
// Execute different operations based on brush type
|
||||
switch (BrushSettings.BrushType)
|
||||
{
|
||||
case EAnatomicalBrushType::Bone:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying bone brush effect to static mesh"));
|
||||
// Bone brush logic
|
||||
break;
|
||||
case EAnatomicalBrushType::Muscle:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying muscle brush effect to static mesh"));
|
||||
// Muscle brush logic
|
||||
break;
|
||||
case EAnatomicalBrushType::Organ:
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying organ brush effect to static mesh"));
|
||||
// Organ brush logic
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogAnatomicalBrush, Warning, TEXT("Unknown brush type"));
|
||||
break;
|
||||
}
|
||||
|
||||
return true; // Temporarily return success, should return based on operation result after actual implementation
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Error, TEXT("ApplyToStaticMesh exception: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Error, TEXT("ApplyToStaticMesh unknown exception occurred"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void UAnatomicalStructureBrush::SetBrushType(EAnatomicalBrushType BrushType)
|
||||
{
|
||||
BrushSettings.BrushType = BrushType;
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Brush type set to: %d"), (int32)BrushType);
|
||||
}
|
||||
|
||||
void UAnatomicalStructureBrush::SetBrushSize(float Size)
|
||||
{
|
||||
BrushSettings.BrushSize = FMath::Clamp(Size, 0.1f, 100.0f);
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Brush size set to: %.2f"), BrushSettings.BrushSize);
|
||||
}
|
||||
|
||||
void UAnatomicalStructureBrush::SetBrushStrength(float Strength)
|
||||
{
|
||||
BrushSettings.BrushStrength = FMath::Clamp(Strength, 0.0f, 1.0f);
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Brush strength set to: %.2f"), BrushSettings.BrushStrength);
|
||||
}
|
||||
|
||||
FAnatomicalBrushSettings UAnatomicalStructureBrush::GetBrushSettings() const
|
||||
@@ -54,13 +166,24 @@ FAnatomicalBrushSettings UAnatomicalStructureBrush::GetBrushSettings() const
|
||||
|
||||
UStaticMesh* UAnatomicalStructureBrush::CreateAnatomicalStructure(const FVector& Location, const FVector& Direction, float Size)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Creating anatomical structure, Location: (%f, %f, %f), Size: %.2f"),
|
||||
Location.X, Location.Y, Location.Z, Size);
|
||||
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
// This is a basic framework
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UAnatomicalStructureBrush::ApplyPhysicsProperties(UStaticMesh* Mesh)
|
||||
{
|
||||
if (!Mesh)
|
||||
{
|
||||
UE_LOG(LogAnatomicalBrush, Error, TEXT("ApplyPhysicsProperties failed: Mesh is null"));
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogAnatomicalBrush, Log, TEXT("Applying physics properties to mesh %s"), *Mesh->GetName());
|
||||
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
// This is a basic framework
|
||||
}
|
||||
|
@@ -9,6 +9,11 @@
|
||||
#include "Framework/Commands/UICommandList.h"
|
||||
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
|
||||
// Define log category
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogDismembermentEditor, Log, All);
|
||||
|
||||
// Define tab names
|
||||
const FName FDismembermentEditor::ViewportTabId(TEXT("DismembermentEditor_Viewport"));
|
||||
@@ -19,20 +24,79 @@ const FName FDismembermentEditor::PhysicsSettingsTabId(TEXT("DismembermentEditor
|
||||
// Constructor
|
||||
FDismembermentEditor::FDismembermentEditor()
|
||||
: SkeletalMesh(nullptr)
|
||||
, BooleanCutTool(nullptr)
|
||||
, LayerSystem(nullptr)
|
||||
, SelectedBoneName(NAME_None)
|
||||
, bCreateCapMesh(true)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("DismembermentEditor constructor called"));
|
||||
|
||||
// Create boolean cut tool
|
||||
BooleanCutTool = NewObject<UBooleanCutTool>();
|
||||
if (BooleanCutTool)
|
||||
{
|
||||
// Initialize default settings
|
||||
BooleanCutTool->SetCapMeshMethod(ECapMeshMethod::Simple);
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Boolean cut tool created successfully"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to create Boolean cut tool"));
|
||||
}
|
||||
|
||||
// Create anatomical layer system
|
||||
LayerSystem = NewObject<UAnatomicalLayerSystem>();
|
||||
if (LayerSystem)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Anatomical layer system created successfully"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to create Anatomical layer system"));
|
||||
}
|
||||
|
||||
// Initialize default cut plane
|
||||
CurrentCutPlane.Location = FVector(0, 0, 0);
|
||||
CurrentCutPlane.Normal = FVector(0, 0, 1); // Default to Z-up
|
||||
}
|
||||
|
||||
// Destructor
|
||||
FDismembermentEditor::~FDismembermentEditor()
|
||||
{
|
||||
// Unregister from any events
|
||||
if (GEditor)
|
||||
{
|
||||
GEditor->UnregisterForUndo(this);
|
||||
}
|
||||
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("DismembermentEditor destructor called"));
|
||||
}
|
||||
|
||||
// Initialize the editor
|
||||
void FDismembermentEditor::InitDismembermentEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, USkeletalMesh* InSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Initializing Dismemberment Editor"));
|
||||
|
||||
try
|
||||
{
|
||||
// Set the skeletal mesh being edited
|
||||
SkeletalMesh = InSkeletalMesh;
|
||||
|
||||
if (!SkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to initialize Dismemberment Editor: Skeletal mesh is null"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Failed to initialize Dismemberment Editor: Skeletal mesh is null"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the layer system with the target mesh
|
||||
if (LayerSystem)
|
||||
{
|
||||
// We can't directly set TargetMesh, as UAnatomicalLayerSystem doesn't have this member
|
||||
// But we can store a reference to the skeletal mesh here
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Layer system initialized, target mesh: %s"), *SkeletalMesh->GetName());
|
||||
}
|
||||
|
||||
// Create the layout
|
||||
CreateEditorLayout();
|
||||
|
||||
@@ -48,6 +112,19 @@ void FDismembermentEditor::InitDismembermentEditor(const EToolkitMode::Type Mode
|
||||
|
||||
// Register for undo/redo
|
||||
GEditor->RegisterForUndo(this);
|
||||
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Dismemberment Editor initialized successfully"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception during Dismemberment Editor initialization: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception during Dismemberment Editor initialization: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception during Dismemberment Editor initialization"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception during Dismemberment Editor initialization"));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the toolkit name
|
||||
@@ -77,26 +154,72 @@ FLinearColor FDismembermentEditor::GetWorldCentricTabColorScale() const
|
||||
// Post undo handler
|
||||
void FDismembermentEditor::PostUndo(bool bSuccess)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("PostUndo called, success: %d"), bSuccess);
|
||||
|
||||
// Refresh the views
|
||||
if (bSuccess)
|
||||
{
|
||||
if (DetailsWidget.IsValid())
|
||||
{
|
||||
DetailsWidget->ForceRefresh();
|
||||
}
|
||||
|
||||
// Refresh other widgets as needed
|
||||
if (ViewportWidget.IsValid())
|
||||
{
|
||||
// Refresh viewport if needed
|
||||
}
|
||||
|
||||
if (LayerSystemWidget.IsValid())
|
||||
{
|
||||
// Refresh layer system widget if needed
|
||||
}
|
||||
|
||||
if (PhysicsSettingsWidget.IsValid())
|
||||
{
|
||||
// Refresh physics settings widget if needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post redo handler
|
||||
void FDismembermentEditor::PostRedo(bool bSuccess)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("PostRedo called, success: %d"), bSuccess);
|
||||
|
||||
// Refresh the views
|
||||
if (bSuccess)
|
||||
{
|
||||
if (DetailsWidget.IsValid())
|
||||
{
|
||||
DetailsWidget->ForceRefresh();
|
||||
}
|
||||
|
||||
// Refresh other widgets as needed
|
||||
if (ViewportWidget.IsValid())
|
||||
{
|
||||
// Refresh viewport if needed
|
||||
}
|
||||
|
||||
if (LayerSystemWidget.IsValid())
|
||||
{
|
||||
// Refresh layer system widget if needed
|
||||
}
|
||||
|
||||
if (PhysicsSettingsWidget.IsValid())
|
||||
{
|
||||
// Refresh physics settings widget if needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the editor layout
|
||||
void FDismembermentEditor::CreateEditorLayout()
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Creating editor layout"));
|
||||
|
||||
try
|
||||
{
|
||||
// Create the layout
|
||||
TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_DismembermentEditor_Layout_v1")
|
||||
->AddArea
|
||||
@@ -149,76 +272,31 @@ void FDismembermentEditor::CreateEditorLayout()
|
||||
true,
|
||||
SkeletalMesh
|
||||
);
|
||||
}
|
||||
|
||||
// Create the editor toolbar
|
||||
void FDismembermentEditor::CreateEditorToolbar()
|
||||
{
|
||||
// Create command list
|
||||
TSharedPtr<FUICommandList> CommandList = MakeShareable(new FUICommandList);
|
||||
// Register tab spawners
|
||||
RegisterTabSpawners(GetTabManager().ToSharedRef());
|
||||
|
||||
// Get the toolbar builder
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
|
||||
|
||||
ToolbarExtender->AddToolBarExtension(
|
||||
"Asset",
|
||||
EExtensionHook::After,
|
||||
CommandList,
|
||||
FToolBarExtensionDelegate::CreateLambda([this](FToolBarBuilder& ToolbarBuilder)
|
||||
{
|
||||
ToolbarBuilder.BeginSection("Dismemberment");
|
||||
{
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::PerformBooleanCut)),
|
||||
NAME_None,
|
||||
FText::FromString("Boolean Cut"),
|
||||
FText::FromString("Perform a boolean cut operation"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.CurveBase"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::AddNewLayer)),
|
||||
NAME_None,
|
||||
FText::FromString("Add Layer"),
|
||||
FText::FromString("Add a new anatomical layer"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.Layer"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::PreviewEffects)),
|
||||
NAME_None,
|
||||
FText::FromString("Preview"),
|
||||
FText::FromString("Preview the dismemberment effects"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.ParticleSystem"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::SaveEdits)),
|
||||
NAME_None,
|
||||
FText::FromString("Save"),
|
||||
FText::FromString("Save the dismemberment setup"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "AssetEditor.SaveAsset"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Editor layout created successfully"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception creating editor layout: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception creating editor layout"));
|
||||
}
|
||||
ToolbarBuilder.EndSection();
|
||||
})
|
||||
);
|
||||
|
||||
// Add the extender
|
||||
AddToolbarExtender(ToolbarExtender);
|
||||
}
|
||||
|
||||
// Register tab spawners
|
||||
void FDismembermentEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
||||
{
|
||||
// Call the base implementation
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Registering tab spawners"));
|
||||
|
||||
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
|
||||
|
||||
// Register the tab spawners
|
||||
try
|
||||
{
|
||||
InTabManager->RegisterTabSpawner(ViewportTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_Viewport))
|
||||
.SetDisplayName(FText::FromString("Viewport"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
@@ -232,21 +310,32 @@ void FDismembermentEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& In
|
||||
InTabManager->RegisterTabSpawner(LayerSystemTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_LayerSystem))
|
||||
.SetDisplayName(FText::FromString("Layer System"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.Layer"));
|
||||
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Layers"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(PhysicsSettingsTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_PhysicsSettings))
|
||||
.SetDisplayName(FText::FromString("Physics Settings"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.PhysicsAsset"));
|
||||
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.WorldProperties"));
|
||||
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Tab spawners registered successfully"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception registering tab spawners: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception registering tab spawners"));
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister tab spawners
|
||||
void FDismembermentEditor::UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
||||
{
|
||||
// Call the base implementation
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Unregistering tab spawners"));
|
||||
|
||||
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
|
||||
|
||||
// Unregister the tab spawners
|
||||
InTabManager->UnregisterTabSpawner(ViewportTabId);
|
||||
InTabManager->UnregisterTabSpawner(DetailsTabId);
|
||||
InTabManager->UnregisterTabSpawner(LayerSystemTabId);
|
||||
@@ -340,23 +429,406 @@ TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_PhysicsSettings(const FSpawn
|
||||
// Perform a boolean cut operation
|
||||
void FDismembermentEditor::PerformBooleanCut()
|
||||
{
|
||||
// To be implemented
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Performing boolean cut operation"));
|
||||
|
||||
try
|
||||
{
|
||||
if (!SkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot perform boolean cut: No skeletal mesh selected"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot perform boolean cut: No skeletal mesh selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!BooleanCutTool)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot perform boolean cut: Boolean cut tool not initialized"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot perform boolean cut: Boolean cut tool not initialized"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the actual boolean cut operation
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Using boolean cut tool to perform cut operation, Bone: %s, Create Cap: %s"),
|
||||
*SelectedBoneName.ToString(), bCreateCapMesh ? TEXT("Yes") : TEXT("No"));
|
||||
|
||||
// If a bone is selected, use bone-guided cutting
|
||||
TArray<USkeletalMesh*> ResultMeshes;
|
||||
if (SelectedBoneName != NAME_None)
|
||||
{
|
||||
ResultMeshes = BooleanCutTool->CutWithBoneAxis(SkeletalMesh, SelectedBoneName, CurrentCutPlane, bCreateCapMesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise use regular skeletal mesh cutting
|
||||
ResultMeshes = BooleanCutTool->CutSkeletalMesh(SkeletalMesh, CurrentCutPlane, NAME_None, bCreateCapMesh);
|
||||
}
|
||||
|
||||
// Check results
|
||||
if (ResultMeshes.Num() > 0)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Boolean cut completed successfully, generated %d meshes"), ResultMeshes.Num());
|
||||
|
||||
// Show success message
|
||||
FText SuccessMessage = FText::Format(
|
||||
FText::FromString("Boolean cut completed, generated {0} meshes"),
|
||||
FText::AsNumber(ResultMeshes.Num())
|
||||
);
|
||||
FMessageDialog::Open(EAppMsgType::Ok, SuccessMessage);
|
||||
|
||||
// TODO: Display result meshes in viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Warning, TEXT("Boolean cut operation did not generate any meshes"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Boolean cut operation did not generate any meshes"));
|
||||
}
|
||||
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Boolean cut operation completed"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception during boolean cut operation: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception during boolean cut operation: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception during boolean cut operation"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception during boolean cut operation"));
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new layer
|
||||
void FDismembermentEditor::AddNewLayer()
|
||||
{
|
||||
// To be implemented
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Adding new anatomical layer"));
|
||||
|
||||
try
|
||||
{
|
||||
if (!SkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot add new layer: No skeletal mesh selected"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot add new layer: No skeletal mesh selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LayerSystem)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot add new layer: Layer system not initialized"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot add new layer: Layer system not initialized"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Show a dialog to get the layer type and name
|
||||
TSharedRef<SWindow> Window = SNew(SWindow)
|
||||
.Title(FText::FromString("Add New Anatomical Layer"))
|
||||
.SizingRule(ESizingRule::Autosized)
|
||||
.SupportsMaximize(false)
|
||||
.SupportsMinimize(false);
|
||||
|
||||
TSharedPtr<SComboBox<TSharedPtr<EAnatomicalLayerType>>> LayerTypeComboBox;
|
||||
TSharedPtr<SEditableTextBox> LayerNameTextBox;
|
||||
|
||||
// Create array of layer types
|
||||
TArray<TSharedPtr<EAnatomicalLayerType>> LayerTypes;
|
||||
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Skin)));
|
||||
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Muscle)));
|
||||
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Bone)));
|
||||
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Organ)));
|
||||
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Blood)));
|
||||
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Custom)));
|
||||
|
||||
// Selected layer type
|
||||
TSharedPtr<EAnatomicalLayerType> SelectedLayerType = LayerTypes[0];
|
||||
|
||||
// Layer name
|
||||
FString NewLayerName = TEXT("New Layer");
|
||||
|
||||
// Dialog result
|
||||
bool bDialogResult = false;
|
||||
|
||||
Window->SetContent(
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(FMargin(8.0f))
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 0, 0, 4)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("Layer Type:"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 0, 0, 8)
|
||||
[
|
||||
SAssignNew(LayerTypeComboBox, SComboBox<TSharedPtr<EAnatomicalLayerType>>)
|
||||
.OptionsSource(&LayerTypes)
|
||||
.InitiallySelectedItem(SelectedLayerType)
|
||||
.OnSelectionChanged_Lambda([&SelectedLayerType](TSharedPtr<EAnatomicalLayerType> NewValue, ESelectInfo::Type SelectType) {
|
||||
SelectedLayerType = NewValue;
|
||||
})
|
||||
.OnGenerateWidget_Lambda([](TSharedPtr<EAnatomicalLayerType> InLayerType) {
|
||||
FString TypeName;
|
||||
switch (*InLayerType)
|
||||
{
|
||||
case EAnatomicalLayerType::Skin: TypeName = TEXT("Skin"); break;
|
||||
case EAnatomicalLayerType::Muscle: TypeName = TEXT("Muscle"); break;
|
||||
case EAnatomicalLayerType::Bone: TypeName = TEXT("Bone"); break;
|
||||
case EAnatomicalLayerType::Organ: TypeName = TEXT("Organ"); break;
|
||||
case EAnatomicalLayerType::Blood: TypeName = TEXT("Blood"); break;
|
||||
case EAnatomicalLayerType::Custom: TypeName = TEXT("Custom"); break;
|
||||
default: TypeName = TEXT("Unknown"); break;
|
||||
}
|
||||
return SNew(STextBlock).Text(FText::FromString(TypeName));
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([&SelectedLayerType]() {
|
||||
FString TypeName;
|
||||
switch (*SelectedLayerType)
|
||||
{
|
||||
case EAnatomicalLayerType::Skin: TypeName = TEXT("Skin"); break;
|
||||
case EAnatomicalLayerType::Muscle: TypeName = TEXT("Muscle"); break;
|
||||
case EAnatomicalLayerType::Bone: TypeName = TEXT("Bone"); break;
|
||||
case EAnatomicalLayerType::Organ: TypeName = TEXT("Organ"); break;
|
||||
case EAnatomicalLayerType::Blood: TypeName = TEXT("Blood"); break;
|
||||
case EAnatomicalLayerType::Custom: TypeName = TEXT("Custom"); break;
|
||||
default: TypeName = TEXT("Unknown"); break;
|
||||
}
|
||||
return FText::FromString(TypeName);
|
||||
})
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 0, 0, 4)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("Layer Name:"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 0, 0, 8)
|
||||
[
|
||||
SAssignNew(LayerNameTextBox, SEditableTextBox)
|
||||
.Text(FText::FromString(NewLayerName))
|
||||
.OnTextChanged_Lambda([&NewLayerName](const FText& NewText) {
|
||||
NewLayerName = NewText.ToString();
|
||||
})
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 8, 0, 0)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
[
|
||||
SNew(SSpacer)
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(0, 0, 4, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Cancel"))
|
||||
.OnClicked_Lambda([&Window, &bDialogResult]() {
|
||||
bDialogResult = false;
|
||||
Window->RequestDestroyWindow();
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Add"))
|
||||
.OnClicked_Lambda([&Window, &bDialogResult]() {
|
||||
bDialogResult = true;
|
||||
Window->RequestDestroyWindow();
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
// Show the dialog
|
||||
GEditor->EditorAddModalWindow(Window);
|
||||
|
||||
// If the user clicked Add
|
||||
if (bDialogResult && SelectedLayerType.IsValid())
|
||||
{
|
||||
// Add the new layer
|
||||
int32 NewLayerIndex = LayerSystem->AddLayer(FName(*NewLayerName), *SelectedLayerType);
|
||||
|
||||
if (NewLayerIndex != INDEX_NONE)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Added new layer: %s (Type: %d)"), *NewLayerName, (int32)*SelectedLayerType);
|
||||
|
||||
// Show success message
|
||||
FText SuccessMessage = FText::Format(
|
||||
FText::FromString("Successfully added new layer: {0}"),
|
||||
FText::FromString(NewLayerName)
|
||||
);
|
||||
FMessageDialog::Open(EAppMsgType::Ok, SuccessMessage);
|
||||
|
||||
// TODO: Update the UI to show the new layer
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to add new layer"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Failed to add new layer"));
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Add new layer operation completed"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception adding new layer: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception occurred while adding new layer: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception adding new layer"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception occurred while adding new layer"));
|
||||
}
|
||||
}
|
||||
|
||||
// Save the edits
|
||||
void FDismembermentEditor::SaveEdits()
|
||||
{
|
||||
// To be implemented
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Saving dismemberment setup"));
|
||||
|
||||
try
|
||||
{
|
||||
if (!SkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot save edits: No skeletal mesh selected"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot save edits: No skeletal mesh selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement the actual save functionality
|
||||
// This will involve:
|
||||
// 1. Saving the current state of the mesh
|
||||
// 2. Saving the layer system configuration
|
||||
// 3. Saving the physics settings
|
||||
|
||||
// For now, just show a message
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Save functionality will be implemented in a future update"));
|
||||
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Dismemberment setup saved"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception saving edits: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception occurred while saving edits: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception saving edits"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception occurred while saving edits"));
|
||||
}
|
||||
}
|
||||
|
||||
// Preview the effects
|
||||
void FDismembermentEditor::PreviewEffects()
|
||||
{
|
||||
// To be implemented
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Previewing dismemberment effects"));
|
||||
|
||||
try
|
||||
{
|
||||
if (!SkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot preview effects: No skeletal mesh selected"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot preview effects: No skeletal mesh selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement the actual preview functionality
|
||||
// This will involve:
|
||||
// 1. Creating a temporary instance of the mesh
|
||||
// 2. Applying the current dismemberment settings
|
||||
// 3. Showing the effects in the viewport
|
||||
|
||||
// For now, just show a message
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Preview functionality will be implemented in a future update"));
|
||||
|
||||
UE_LOG(LogDismembermentEditor, Log, TEXT("Dismemberment effects previewed"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception previewing effects: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception occurred while previewing effects: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception previewing effects"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception occurred while previewing effects"));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the editor toolbar
|
||||
void FDismembermentEditor::CreateEditorToolbar()
|
||||
{
|
||||
// Create command list
|
||||
TSharedPtr<FUICommandList> CommandList = MakeShareable(new FUICommandList);
|
||||
|
||||
// Get the toolbar builder
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
|
||||
|
||||
ToolbarExtender->AddToolBarExtension(
|
||||
"Asset",
|
||||
EExtensionHook::After,
|
||||
CommandList,
|
||||
FToolBarExtensionDelegate::CreateLambda([this](FToolBarBuilder& ToolbarBuilder)
|
||||
{
|
||||
ToolbarBuilder.BeginSection("Dismemberment");
|
||||
{
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::PerformBooleanCut)),
|
||||
NAME_None,
|
||||
FText::FromString("Boolean Cut"),
|
||||
FText::FromString("Perform Boolean Cut Operation"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.CurveBase"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::AddNewLayer)),
|
||||
NAME_None,
|
||||
FText::FromString("Add Layer"),
|
||||
FText::FromString("Add New Anatomical Layer"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.Layer"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::PreviewEffects)),
|
||||
NAME_None,
|
||||
FText::FromString("Preview"),
|
||||
FText::FromString("Preview Dismemberment Effects"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.ParticleSystem"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::SaveEdits)),
|
||||
NAME_None,
|
||||
FText::FromString("Save"),
|
||||
FText::FromString("Save Dismemberment Settings"),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "AssetEditor.SaveAsset"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
}
|
||||
ToolbarBuilder.EndSection();
|
||||
})
|
||||
);
|
||||
|
||||
// Add the extender
|
||||
AddToolbarExtender(ToolbarExtender);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "Toolkits/AssetEditorToolkit.h"
|
||||
#include "EditorUndoClient.h"
|
||||
#include "BooleanCutTool.h"
|
||||
#include "AnatomicalLayerSystem.h"
|
||||
|
||||
class USkeletalMesh;
|
||||
class SDockTab;
|
||||
@@ -12,48 +14,116 @@ class SBorder;
|
||||
/**
|
||||
* Dismemberment System Editor
|
||||
* Provides real-time boolean cutting and multi-layer system editing functionality
|
||||
* Allows users to create and edit anatomical layer structures for skeletal meshes
|
||||
* Supports physics settings and effect previews
|
||||
*/
|
||||
class FDismembermentEditor : public FAssetEditorToolkit, public FEditorUndoClient
|
||||
{
|
||||
public:
|
||||
/** Constructor */
|
||||
FDismembermentEditor();
|
||||
|
||||
/** Destructor */
|
||||
virtual ~FDismembermentEditor();
|
||||
|
||||
/**
|
||||
* Initialize the dismemberment editor
|
||||
* @param Mode - Toolkit mode
|
||||
* @param InitToolkitHost - Toolkit host
|
||||
* @param InSkeletalMesh - Skeletal mesh to edit
|
||||
*/
|
||||
void InitDismembermentEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, USkeletalMesh* InSkeletalMesh);
|
||||
|
||||
/** Get toolkit name */
|
||||
virtual FName GetToolkitFName() const override;
|
||||
|
||||
/** Get base toolkit name */
|
||||
virtual FText GetBaseToolkitName() const override;
|
||||
|
||||
/** Get world centric tab prefix */
|
||||
virtual FString GetWorldCentricTabPrefix() const override;
|
||||
|
||||
/** Get world centric tab color scale */
|
||||
virtual FLinearColor GetWorldCentricTabColorScale() const override;
|
||||
|
||||
/** Post undo handler */
|
||||
virtual void PostUndo(bool bSuccess) override;
|
||||
|
||||
/** Post redo handler */
|
||||
virtual void PostRedo(bool bSuccess) override;
|
||||
|
||||
/** Get current skeletal mesh being edited */
|
||||
USkeletalMesh* GetSkeletalMesh() const { return SkeletalMesh; }
|
||||
|
||||
private:
|
||||
/** Create editor layout */
|
||||
void CreateEditorLayout();
|
||||
|
||||
/** Create editor toolbar */
|
||||
void CreateEditorToolbar();
|
||||
|
||||
void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager);
|
||||
void UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager);
|
||||
/** Register tab spawners */
|
||||
virtual void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override;
|
||||
|
||||
/** Unregister tab spawners */
|
||||
virtual void UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override;
|
||||
|
||||
/** Spawn viewport tab */
|
||||
TSharedRef<SDockTab> SpawnTab_Viewport(const FSpawnTabArgs& Args);
|
||||
|
||||
/** Spawn details tab */
|
||||
TSharedRef<SDockTab> SpawnTab_Details(const FSpawnTabArgs& Args);
|
||||
|
||||
/** Spawn layer system tab */
|
||||
TSharedRef<SDockTab> SpawnTab_LayerSystem(const FSpawnTabArgs& Args);
|
||||
|
||||
/** Spawn physics settings tab */
|
||||
TSharedRef<SDockTab> SpawnTab_PhysicsSettings(const FSpawnTabArgs& Args);
|
||||
|
||||
/** Perform boolean cut operation */
|
||||
void PerformBooleanCut();
|
||||
|
||||
/** Add new anatomical layer */
|
||||
void AddNewLayer();
|
||||
|
||||
/** Save edits */
|
||||
void SaveEdits();
|
||||
|
||||
/** Preview effects */
|
||||
void PreviewEffects();
|
||||
|
||||
private:
|
||||
/** Current skeletal mesh being edited */
|
||||
USkeletalMesh* SkeletalMesh;
|
||||
|
||||
/** Viewport widget */
|
||||
TSharedPtr<SBorder> ViewportWidget;
|
||||
|
||||
/** Details widget */
|
||||
TSharedPtr<IDetailsView> DetailsWidget;
|
||||
|
||||
/** Layer system widget */
|
||||
TSharedPtr<SBorder> LayerSystemWidget;
|
||||
|
||||
/** Physics settings widget */
|
||||
TSharedPtr<SBorder> PhysicsSettingsWidget;
|
||||
|
||||
/** Boolean cut tool */
|
||||
UBooleanCutTool* BooleanCutTool;
|
||||
|
||||
/** Anatomical layer system */
|
||||
UAnatomicalLayerSystem* LayerSystem;
|
||||
|
||||
/** Current cut plane */
|
||||
FCutPlane CurrentCutPlane;
|
||||
|
||||
/** Selected bone name for cutting */
|
||||
FName SelectedBoneName;
|
||||
|
||||
/** Flag to create cap mesh */
|
||||
bool bCreateCapMesh;
|
||||
|
||||
/** Tab ID constants */
|
||||
static const FName ViewportTabId;
|
||||
static const FName DetailsTabId;
|
||||
static const FName LayerSystemTabId;
|
||||
|
@@ -4,6 +4,8 @@
|
||||
#include "Toolkits/AssetEditorToolkit.h"
|
||||
#include "Widgets/Docking/SDockTab.h"
|
||||
#include "BooleanCutTool.h"
|
||||
#include "EditorUndoClient.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
class SDockTab;
|
||||
class SGraphEditor;
|
||||
@@ -13,6 +15,10 @@ class SMatrixInputWidget;
|
||||
class FFLESHViewportClient;
|
||||
class FSceneViewport;
|
||||
class UVisceraNodeObject;
|
||||
class FDismembermentEditor;
|
||||
|
||||
// Define log category
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogFLESHEditor, Log, All);
|
||||
|
||||
/**
|
||||
* Bone tree item structure
|
||||
@@ -181,7 +187,7 @@ struct FVisceraNodeItem : public TSharedFromThis<FVisceraNodeItem>
|
||||
* FLESH Main Editor
|
||||
* Provides the main editing functionality for the dismemberment system
|
||||
*/
|
||||
class FLESHEDITOR_API FFLESHEditor : public FAssetEditorToolkit
|
||||
class FLESHEDITOR_API FFLESHEditor : public FAssetEditorToolkit, public FEditorUndoClient
|
||||
{
|
||||
public:
|
||||
FFLESHEditor();
|
||||
@@ -199,8 +205,26 @@ public:
|
||||
virtual FLinearColor GetWorldCentricTabColorScale() const override;
|
||||
// End of FAssetEditorToolkit interface
|
||||
|
||||
// FEditorUndoClient interface
|
||||
virtual void PostUndo(bool bSuccess) override;
|
||||
virtual void PostRedo(bool bSuccess) override;
|
||||
|
||||
// Open the editor
|
||||
static void OpenEditor();
|
||||
void OpenEditor();
|
||||
|
||||
// Get whether the editor is initialized
|
||||
bool IsEditorInitialized();
|
||||
|
||||
// Get the editing object
|
||||
UObject* GetEditingObject() const { return EditingObject; }
|
||||
|
||||
// Add DismembermentEditor related tab spawners
|
||||
TSharedRef<SDockTab> SpawnTab_LayerSystem(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_PhysicsSettings(const FSpawnTabArgs& Args);
|
||||
|
||||
// Create DismembermentEditor related widgets
|
||||
TSharedRef<SBorder> CreateLayerSystemWidget();
|
||||
TSharedRef<SBorder> CreatePhysicsSettingsWidget();
|
||||
|
||||
private:
|
||||
// Tab spawners
|
||||
@@ -232,6 +256,9 @@ private:
|
||||
// Create command list
|
||||
void CreateCommandList();
|
||||
|
||||
// Extend toolbar with custom buttons
|
||||
void ExtendToolbar();
|
||||
|
||||
// Generate bone tree row
|
||||
TSharedRef<ITableRow> OnGenerateBoneRow(TSharedPtr<FBoneTreeItem> Item, const TSharedRef<STableViewBase>& OwnerTable);
|
||||
|
||||
@@ -338,4 +365,17 @@ private:
|
||||
static const FName MatrixEditorTabId;
|
||||
static const FName GraphEditorTabId;
|
||||
static const FName ToolbarTabId;
|
||||
static const FName LayerSystemTabId;
|
||||
static const FName PhysicsSettingsTabId;
|
||||
static const FName DismembermentGraphTabId;
|
||||
|
||||
// Is the editor initialized?
|
||||
bool bIsInitialized;
|
||||
|
||||
// New DismembermentEditor related Widgets
|
||||
TSharedPtr<SBorder> LayerSystemWidget;
|
||||
TSharedPtr<SBorder> PhysicsSettingsWidget;
|
||||
|
||||
// Error handling method
|
||||
void HandleEditorError(const FText& ErrorMessage);
|
||||
};
|
||||
|
Reference in New Issue
Block a user