Compare commits
5 Commits
3ea9686964
...
e647723a7a
| Author | SHA1 | Date | |
|---|---|---|---|
| e647723a7a | |||
| dd16f0123a | |||
| 33d0ff62de | |||
| 8bde527782 | |||
| 08d12c4d47 |
@@ -6,7 +6,8 @@
|
||||
"Bash(\"E:\\MiceCola\\work\\UE_5_6\\Engine\\Build\\BatchFiles\\Build.bat\" BladeSmithEditor Win64 Development -Project=\"E:\\MiceCola\\work\\BladeSmith\\BladeSmith.uproject\" -WaitMutex -FromMsBuild)",
|
||||
"Bash(dir:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\n实现DynamicIronBlock温度体素系统基础功能\n\n新增功能:\n- 添加FTemperatureVoxel结构体(温度、塑性系数、表面标志)\n- 实现自适应温度网格(5mm体素精度,基于BoxSize计算分辨率)\n- 实现三线性插值温度/塑性系数采样\n- 实现黑体辐射温度可视化(顶点颜色)\n- 添加HeatRegion蓝图测试函数\n- 添加OnHammerImpact锤击形变函数(体积守恒)\n\n技术细节:\n- Phase 1: CPU侧温度网格初始化,支持10cm-100cm+可变尺寸\n- Phase 3: 温度可视化系统(20°C灰色 → 1200°C白色)\n- 内存占用:30×30×30cm ≈ 1.7MB(60×60×60体素)\n\n待实现:Phase 2 GPU热传导计算\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")"
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\n实现DynamicIronBlock温度体素系统基础功能\n\n新增功能:\n- 添加FTemperatureVoxel结构体(温度、塑性系数、表面标志)\n- 实现自适应温度网格(5mm体素精度,基于BoxSize计算分辨率)\n- 实现三线性插值温度/塑性系数采样\n- 实现黑体辐射温度可视化(顶点颜色)\n- 添加HeatRegion蓝图测试函数\n- 添加OnHammerImpact锤击形变函数(体积守恒)\n\n技术细节:\n- Phase 1: CPU侧温度网格初始化,支持10cm-100cm+可变尺寸\n- Phase 3: 温度可视化系统(20°C灰色 → 1200°C白色)\n- 内存占用:30×30×30cm ≈ 1.7MB(60×60×60体素)\n\n待实现:Phase 2 GPU热传导计算\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
|
||||
"Bash(git commit:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATedilij_002Easm_002Fl_003AC_0021_003FUsers_003Fstarm_003FAppData_003FLocal_003FTemp_003FSandboxFiles_003FKotyxod_003FTedilij_002Easm/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATextureDefines_002Eh_002Fl_003AD_0021_003FProgram_0020Files_003FEpic_0020Games_003FUE_005F5_002E6_003FEngine_003FSource_003FRuntime_003FEngine_003FClasses_003FEngine_003FTextureDefines_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AShaderParameterStruct_002Eh_002Fl_003AD_0021_003FProgram_0020Files_003FEpic_0020Games_003FUE_005F5_002E6_003FEngine_003FSource_003FRuntime_003FRenderCore_003FPublic_003FShaderParameterStruct_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATuvased_002Easm_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdministrator_003FAppData_003FLocal_003FTemp_003FSandboxFiles_003FKonywix_003FTuvased_002Easm/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnrealMathUtility_002Eh_002Fl_003AD_0021_003FProgram_0020Files_003FEpic_0020Games_003FUE_005F5_002E6_003FEngine_003FSource_003FRuntime_003FCore_003FPublic_003FMath_003FUnrealMathUtility_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_002Eh_002Fl_003AD_0021_003FProgram_0020Files_003FEpic_0020Games_003FUE_005F5_002E6_003FEngine_003FSource_003FRuntime_003FCore_003FPublic_003FContainers_003FArray_002Eh/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||
@@ -38,6 +38,7 @@ r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
|
||||
|
||||
r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
|
||||
r.DefaultFeature.AutoExposure=False
|
||||
r.Lumen.TraceMeshSDFs=1
|
||||
|
||||
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -78,9 +78,9 @@ void ADynamicIronBlock::Tick(float DeltaTime)
|
||||
// 重置计时器
|
||||
TimeSinceLastUpdate = 0.0f;
|
||||
|
||||
// 下载数据并更新顶点颜色(可选,性能开销较大)
|
||||
// DownloadTemperatureDataFromGPU();
|
||||
// UpdateVertexColors();
|
||||
// 从GPU下载温度数据并更新顶点颜色
|
||||
DownloadTemperatureDataFromGPU();
|
||||
// 注意:UpdateVertexColors()会在DownloadTemperatureDataFromGPU的回调中异步调用
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,6 +481,61 @@ void ADynamicIronBlock::HeatRegion(FVector WorldCenter, float Radius, float Temp
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Heated %d voxels in region (center: %s, radius: %.2f)"),
|
||||
AffectedVoxels, *LocalCenter.ToString(), Radius);
|
||||
|
||||
// 调试:输出距离Hit点最近的4个体素的温度
|
||||
struct FVoxelDebugInfo
|
||||
{
|
||||
FIntVector Coord;
|
||||
float Distance;
|
||||
float Temperature;
|
||||
};
|
||||
TArray<FVoxelDebugInfo> NearestVoxels;
|
||||
|
||||
// 查找最近的体素
|
||||
for (int32 Z = MinZ; Z <= MaxZ; ++Z)
|
||||
{
|
||||
for (int32 Y = MinY; Y <= MaxY; ++Y)
|
||||
{
|
||||
for (int32 X = MinX; X <= MaxX; ++X)
|
||||
{
|
||||
FVector VoxelCenter = GridOrigin + FVector(
|
||||
(X + 0.5f) * VoxelSizeCM.X,
|
||||
(Y + 0.5f) * VoxelSizeCM.Y,
|
||||
(Z + 0.5f) * VoxelSizeCM.Z
|
||||
);
|
||||
float Distance = FVector::Dist(VoxelCenter, LocalCenter);
|
||||
|
||||
int32 Index = GetTemperatureIndex(X, Y, Z);
|
||||
FVoxelDebugInfo Info;
|
||||
Info.Coord = FIntVector(X, Y, Z);
|
||||
Info.Distance = Distance;
|
||||
Info.Temperature = TemperatureGrid[Index].Temperature;
|
||||
|
||||
NearestVoxels.Add(Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按距离排序
|
||||
NearestVoxels.Sort([](const FVoxelDebugInfo& A, const FVoxelDebugInfo& B) {
|
||||
return A.Distance < B.Distance;
|
||||
});
|
||||
|
||||
// 输出最近的4个体素
|
||||
UE_LOG(LogTemp, Warning, TEXT("=== Nearest 4 Voxels to Hit Point ==="));
|
||||
for (int32 i = 0; i < FMath::Min(4, NearestVoxels.Num()); ++i)
|
||||
{
|
||||
const FVoxelDebugInfo& Info = NearestVoxels[i];
|
||||
UE_LOG(LogTemp, Warning, TEXT(" Voxel[%d,%d,%d] Distance=%.2fcm Temperature=%.1f°C"),
|
||||
Info.Coord.X, Info.Coord.Y, Info.Coord.Z, Info.Distance, Info.Temperature);
|
||||
}
|
||||
|
||||
// 将更新后的温度数据上传到GPU
|
||||
if (bGPUBuffersReady.load(std::memory_order_acquire))
|
||||
{
|
||||
UploadTemperatureDataToGPU();
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Temperature data uploaded to GPU after HeatRegion"));
|
||||
}
|
||||
|
||||
// 更新顶点颜色以显示温度变化
|
||||
UpdateVertexColors();
|
||||
}
|
||||
@@ -848,6 +903,9 @@ void ADynamicIronBlock::DownloadTemperatureDataFromGPU()
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Temperature data downloaded from GPU"));
|
||||
|
||||
// 更新顶点颜色以反映新的温度
|
||||
UpdateVertexColors();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -856,6 +914,8 @@ void ADynamicIronBlock::DispatchTemperatureCompute(float DeltaTime)
|
||||
{
|
||||
if (!TemperatureBufferA.IsValid() || !TemperatureBufferB.IsValid())
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("DynamicIronBlock: Temperature buffers not valid! A=%d B=%d"),
|
||||
TemperatureBufferA.IsValid(), TemperatureBufferB.IsValid());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -867,30 +927,22 @@ void ADynamicIronBlock::DispatchTemperatureCompute(float DeltaTime)
|
||||
float ForgeMin = ForgeTemperatureMin;
|
||||
float ForgeMax = ForgeTemperatureMax;
|
||||
|
||||
// Ping-Pong缓冲区引用
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Dispatching temperature compute - Resolution=(%d,%d,%d), DeltaTime=%.4f"),
|
||||
Resolution.X, Resolution.Y, Resolution.Z, DeltaTime);
|
||||
|
||||
// Ping-Pong缓冲区引用(捕获到lambda传递给RDG)
|
||||
FBufferRHIRef InputBuffer = bUseBufferA ? TemperatureBufferA : TemperatureBufferB;
|
||||
FBufferRHIRef OutputBuffer = bUseBufferA ? TemperatureBufferB : TemperatureBufferA;
|
||||
FBufferRHIRef FlagsBuffer = SurfaceVoxelFlagsBuffer;
|
||||
FBufferRHIRef PlasticityBuf = PlasticityBuffer;
|
||||
|
||||
// 创建SRV/UAV(在游戏线程创建,捕获到lambda)
|
||||
FShaderResourceViewRHIRef InputSRV = TemperatureSRV_A;
|
||||
FShaderResourceViewRHIRef InputSRV_B = TemperatureSRV_B;
|
||||
FShaderResourceViewRHIRef FlagsSRV = SurfaceVoxelSRV;
|
||||
FUnorderedAccessViewRHIRef OutputUAV_A = TemperatureUAV_A;
|
||||
FUnorderedAccessViewRHIRef OutputUAV_B = TemperatureUAV_B;
|
||||
FUnorderedAccessViewRHIRef PlasticityUAV_Ref = PlasticityUAV;
|
||||
|
||||
bool bReadFromA = bUseBufferA;
|
||||
|
||||
// 切换缓冲区(下次Tick使用新的缓冲区)
|
||||
bUseBufferA = !bUseBufferA;
|
||||
|
||||
// 调度GPU计算(直接使用RHI,不用RDG)
|
||||
// 调度GPU计算(使用RDG正确的资源管理)
|
||||
ENQUEUE_RENDER_COMMAND(DispatchTemperatureCS)(
|
||||
[Resolution, Conductivity, Cooling, Ambient, ForgeMin, ForgeMax, DeltaTime,
|
||||
InputBuffer, OutputBuffer, FlagsBuffer, PlasticityBuf,
|
||||
InputSRV, InputSRV_B, FlagsSRV, OutputUAV_A, OutputUAV_B, PlasticityUAV_Ref, bReadFromA]
|
||||
InputBuffer, OutputBuffer, FlagsBuffer, PlasticityBuf]
|
||||
(FRHICommandListImmediate& RHICmdList)
|
||||
{
|
||||
// 检查ShaderMap是否有效
|
||||
@@ -909,21 +961,30 @@ void ADynamicIronBlock::DispatchTemperatureCompute(float DeltaTime)
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用RDG(简化版本 - 创建临时缓冲区)
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock [RenderThread]: Shader valid, creating RDG..."));
|
||||
|
||||
// 使用RDG
|
||||
FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("TemperatureConduction"));
|
||||
|
||||
// 创建RDG缓冲区描述
|
||||
FRDGBufferDesc TempBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(float), InputBuffer->GetSize() / sizeof(float));
|
||||
FRDGBufferDesc FlagsBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), FlagsBuffer->GetSize() / sizeof(uint32));
|
||||
// 将外部RHI缓冲区正确注册到RDG系统
|
||||
// 创建缓冲区描述符
|
||||
const uint32 NumElements = InputBuffer->GetSize() / sizeof(float);
|
||||
FRDGBufferDesc TempBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(float), NumElements);
|
||||
FRDGBufferDesc FlagsBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), NumElements);
|
||||
|
||||
// 创建RDG缓冲区
|
||||
FRDGBufferRef InputTempRDG = GraphBuilder.CreateBuffer(TempBufferDesc, TEXT("InputTemperatureBuffer"));
|
||||
FRDGBufferRef OutputTempRDG = GraphBuilder.CreateBuffer(TempBufferDesc, TEXT("OutputTemperatureBuffer"));
|
||||
FRDGBufferRef FlagsRDG = GraphBuilder.CreateBuffer(FlagsBufferDesc, TEXT("SurfaceVoxelFlagsBuffer"));
|
||||
FRDGBufferRef PlasticityRDG = GraphBuilder.CreateBuffer(TempBufferDesc, TEXT("PlasticityBuffer"));
|
||||
// 创建FRDGPooledBuffer并注册
|
||||
TRefCountPtr<FRDGPooledBuffer> InputPooledBuffer = new FRDGPooledBuffer(RHICmdList, InputBuffer, TempBufferDesc, NumElements, TEXT("InputTemperatureBuffer"));
|
||||
TRefCountPtr<FRDGPooledBuffer> OutputPooledBuffer = new FRDGPooledBuffer(RHICmdList, OutputBuffer, TempBufferDesc, NumElements, TEXT("OutputTemperatureBuffer"));
|
||||
TRefCountPtr<FRDGPooledBuffer> FlagsPooledBuffer = new FRDGPooledBuffer(RHICmdList, FlagsBuffer, FlagsBufferDesc, NumElements, TEXT("SurfaceVoxelFlagsBuffer"));
|
||||
TRefCountPtr<FRDGPooledBuffer> PlasticityPooledBuffer = new FRDGPooledBuffer(RHICmdList, PlasticityBuf, TempBufferDesc, NumElements, TEXT("PlasticityBuffer"));
|
||||
|
||||
// TODO: 添加数据复制Pass(从RHI到RDG)
|
||||
// 暂时跳过,shader会处理未初始化数据
|
||||
FRDGBufferRef InputTempRDG = GraphBuilder.RegisterExternalBuffer(InputPooledBuffer);
|
||||
FRDGBufferRef OutputTempRDG = GraphBuilder.RegisterExternalBuffer(OutputPooledBuffer);
|
||||
FRDGBufferRef FlagsRDG = GraphBuilder.RegisterExternalBuffer(FlagsPooledBuffer);
|
||||
FRDGBufferRef PlasticityRDG = GraphBuilder.RegisterExternalBuffer(PlasticityPooledBuffer);
|
||||
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock [RenderThread]: External RHI buffers registered to RDG, setting shader parameters..."));
|
||||
|
||||
// 设置Shader参数
|
||||
FTemperatureComputeShader::FParameters* PassParameters = GraphBuilder.AllocParameters<FTemperatureComputeShader::FParameters>();
|
||||
@@ -935,10 +996,13 @@ void ADynamicIronBlock::DispatchTemperatureCompute(float DeltaTime)
|
||||
PassParameters->ForgeTemperatureMax = ForgeMax;
|
||||
PassParameters->DeltaTime = DeltaTime;
|
||||
|
||||
PassParameters->InputTemperatureBuffer = GraphBuilder.CreateSRV(InputTempRDG, PF_R32_FLOAT);
|
||||
PassParameters->SurfaceVoxelFlagsBuffer = GraphBuilder.CreateSRV(FlagsRDG, PF_R32_UINT);
|
||||
PassParameters->OutputTemperatureBuffer = GraphBuilder.CreateUAV(OutputTempRDG, PF_R32_FLOAT);
|
||||
PassParameters->OutputPlasticityBuffer = GraphBuilder.CreateUAV(PlasticityRDG, PF_R32_FLOAT);
|
||||
// 创建SRV和UAV(使用正确注册的RDG资源)
|
||||
PassParameters->InputTemperatureBuffer = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(InputTempRDG, PF_R32_FLOAT));
|
||||
PassParameters->SurfaceVoxelFlagsBuffer = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(FlagsRDG, PF_R32_UINT));
|
||||
PassParameters->OutputTemperatureBuffer = GraphBuilder.CreateUAV(FRDGBufferUAVDesc(OutputTempRDG, PF_R32_FLOAT));
|
||||
PassParameters->OutputPlasticityBuffer = GraphBuilder.CreateUAV(FRDGBufferUAVDesc(PlasticityRDG, PF_R32_FLOAT));
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock [RenderThread]: Shader parameters set, calculating group count..."));
|
||||
|
||||
// 计算线程组数量
|
||||
FIntVector GroupSize = FTemperatureComputeShader::GetGroupSize();
|
||||
@@ -948,6 +1012,9 @@ void ADynamicIronBlock::DispatchTemperatureCompute(float DeltaTime)
|
||||
FMath::DivideAndRoundUp(Resolution.Z, GroupSize.Z)
|
||||
);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock [RenderThread]: GroupCount=(%d,%d,%d), adding compute pass..."),
|
||||
GroupCount.X, GroupCount.Y, GroupCount.Z);
|
||||
|
||||
// 添加计算Pass
|
||||
FComputeShaderUtils::AddPass(
|
||||
GraphBuilder,
|
||||
@@ -957,13 +1024,12 @@ void ADynamicIronBlock::DispatchTemperatureCompute(float DeltaTime)
|
||||
GroupCount
|
||||
);
|
||||
|
||||
// TODO: 添加数据复制Pass(从RDG回RHI)
|
||||
// 暂时跳过,需要实现正确的缓冲区提取
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock [RenderThread]: Compute pass added, executing RDG..."));
|
||||
|
||||
// 执行RDG
|
||||
// 执行RDG,自动处理资源转换和同步
|
||||
GraphBuilder.Execute();
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("DynamicIronBlock: Temperature compute dispatched (%d x %d x %d groups) - Data copy not yet implemented"),
|
||||
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Temperature compute dispatched successfully (%d x %d x %d groups)"),
|
||||
GroupCount.X, GroupCount.Y, GroupCount.Z);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ private:
|
||||
|
||||
bool bUseBufferA = true; // Ping-Pong标志
|
||||
float TimeSinceLastUpdate = 0.0f; // 距离上次温度更新的时间
|
||||
float TemperatureUpdateInterval = 0.1f; // 温度更新间隔(秒)
|
||||
float TemperatureUpdateInterval = 1.0f; // 温度更新间隔(秒)
|
||||
|
||||
// GPU缓冲区是否已准备好(线程安全标志)
|
||||
std::atomic<bool> bGPUBuffersReady{false};
|
||||
|
||||
@@ -29,7 +29,7 @@ DynMaterial(nullptr), DynCalMaterial(nullptr)
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
|
||||
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent"));
|
||||
StaticMeshComp->AttachToComponent(RootComponent,FAttachmentTransformRules::SnapToTargetIncludingScale);
|
||||
//StaticMeshComp->AttachToComponent(RootComponent,FAttachmentTransformRules::SnapToTargetIncludingScale);
|
||||
//RootComponent = StaticMeshComp;
|
||||
}
|
||||
void AIronBlock::PostInitProperties()
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 290 KiB |
Reference in New Issue
Block a user