diff --git a/.gitignore b/.gitignore index 5b7725c..2cadcec 100644 --- a/.gitignore +++ b/.gitignore @@ -249,3 +249,9 @@ Plugins/**/Intermediate/* # Cache files for the editor to use DerivedDataCache/* +# Workshop output and machine-local tools +renders/ +Content/Workshop/Generated/ +*.blend1 +*.blend2 + diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini index 457e312..dc8fecf 100644 --- a/Config/DefaultEditor.ini +++ b/Config/DefaultEditor.ini @@ -1,6 +1,3 @@ -[/Script/AdvancedPreviewScene.SharedProfiles] -+Profiles=(ProfileName="Epic Headquarters",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=1.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=1.000000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,bShowGrid=False,EnvironmentColor=(R=0.200000,G=0.200000,B=0.200000,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",EnvironmentFloorMeshPath="/Engine/EditorMeshes/AssetViewer/Floor_Mesh.Floor_Mesh",EnvironmentFloorRotation=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),EnvironmentFloorScale=(X=4.000000,Y=4.000000,Z=1.000000),EnvironmentFloorMaterialPath="/Engine/EngineMaterials/M_Grid.M_Grid",bPostProcessingEnabled=True,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_TonemappingMethod=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_MegaLightsFrontLayerTranslucency=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_VignetteType=False,bOverride_VignetteCenter=False,bOverride_VignetteColor=False,bOverride_VignetteSize=False,bOverride_VignetteSoftness=False,bOverride_VignetteTexture=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainScale=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldEnabled=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenAmbientOcclusionIntensity=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,TonemappingMethod=Filmic,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenAmbientOcclusionIntensity=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,MegaLightsFrontLayerTranslucency=False,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,VignetteType=CosineFourthLaw,VignetteCenter=(X=0.500000,Y=0.500000),VignetteColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000),VignetteSize=(X=1.000000,Y=1.000000),VignetteSoftness=5.000000,VignetteTexture=None,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainScale=(X=1.000000,Y=1.000000),FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldEnabled=True,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=True,bShowMeshEdges=False) -+Profiles=(ProfileName="Grey Wireframe",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=1.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=1.000000,bRotateLightingRig=False,bShowEnvironment=False,bShowFloor=False,bShowGrid=False,EnvironmentColor=(R=0.039216,G=0.039216,B=0.039216,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",EnvironmentFloorMeshPath="/Engine/EditorMeshes/AssetViewer/Floor_Mesh.Floor_Mesh",EnvironmentFloorRotation=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),EnvironmentFloorScale=(X=4.000000,Y=4.000000,Z=1.000000),EnvironmentFloorMaterialPath="/Engine/EngineMaterials/M_Grid.M_Grid",bPostProcessingEnabled=False,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_TonemappingMethod=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_MegaLightsFrontLayerTranslucency=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_VignetteType=False,bOverride_VignetteCenter=False,bOverride_VignetteColor=False,bOverride_VignetteSize=False,bOverride_VignetteSoftness=False,bOverride_VignetteTexture=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainScale=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldEnabled=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenAmbientOcclusionIntensity=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,TonemappingMethod=Filmic,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenAmbientOcclusionIntensity=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,MegaLightsFrontLayerTranslucency=False,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,VignetteType=CosineFourthLaw,VignetteCenter=(X=0.500000,Y=0.500000),VignetteColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000),VignetteSize=(X=1.000000,Y=1.000000),VignetteSoftness=5.000000,VignetteTexture=None,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainScale=(X=1.000000,Y=1.000000),FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldEnabled=True,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=False,bShowMeshEdges=True) -+Profiles=(ProfileName="Grey Ambient",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=4.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=2.000000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,bShowGrid=False,EnvironmentColor=(R=0.200000,G=0.200000,B=0.200000,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/T_GreyAmbient",EnvironmentFloorMeshPath="/Engine/EditorMeshes/AssetViewer/Floor_Mesh.Floor_Mesh",EnvironmentFloorRotation=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),EnvironmentFloorScale=(X=4.000000,Y=4.000000,Z=1.000000),EnvironmentFloorMaterialPath="/Engine/EngineMaterials/M_Grid.M_Grid",bPostProcessingEnabled=False,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_TonemappingMethod=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_MegaLightsFrontLayerTranslucency=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_VignetteType=False,bOverride_VignetteCenter=False,bOverride_VignetteColor=False,bOverride_VignetteSize=False,bOverride_VignetteSoftness=False,bOverride_VignetteTexture=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainScale=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldEnabled=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenAmbientOcclusionIntensity=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,TonemappingMethod=Filmic,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenAmbientOcclusionIntensity=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,MegaLightsFrontLayerTranslucency=False,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,VignetteType=CosineFourthLaw,VignetteCenter=(X=0.500000,Y=0.500000),VignetteColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000),VignetteSize=(X=1.000000,Y=1.000000),VignetteSoftness=5.000000,VignetteTexture=None,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainScale=(X=1.000000,Y=1.000000),FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldEnabled=True,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=False,bShowMeshEdges=False) -Version=ApplyNewGridMaterial - +[/Script/PythonScriptPlugin.PythonScriptPluginSettings] +bDeveloperMode=True +bRemoteExecution=False diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index 0975f90..7f2b579 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -1,94 +1,27 @@ - - [/Script/EngineSettings.GameMapsSettings] -GameDefaultMap=/Engine/Maps/Templates/OpenWorld +EditorStartupMap=/Engine/Maps/Entry +GameDefaultMap=/Engine/Maps/Entry [/Script/Engine.RendererSettings] r.AllowStaticLighting=False -r.Shadow.Virtual.Enable=1 - r.GenerateMeshDistanceFields=True - r.DynamicGlobalIlluminationMethod=1 - r.ReflectionMethod=1 - -r.SkinCache.CompileShaders=True - -r.RayTracing=True - -r.RayTracing.RayTracingProxies.ProjectEnabled=True - -r.Substrate=True - -r.Substrate.ProjectGBufferFormat=0 - +r.Shadow.Virtual.Enable=1 +r.RayTracing=False r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True -r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8 - -r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8 - [/Script/WindowsTargetPlatform.WindowsTargetSettings] DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 -DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 --D3D12TargetedShaderFormats=PCD3D_SM5 +D3D12TargetedShaderFormats=PCD3D_SM6 --D3D11TargetedShaderFormats=PCD3D_SM5 -+D3D11TargetedShaderFormats=PCD3D_SM5 -Compiler=Default -AudioSampleRate=48000 -AudioCallbackBufferFrameSize=1024 -AudioNumBuffersToEnqueue=1 -AudioMaxChannels=0 -AudioNumSourceWorkers=4 -SpatializationPlugin= -SourceDataOverridePlugin= -ReverbPlugin= -OcclusionPlugin= -CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) -CacheSizeKB=65536 -MaxChunkSizeOverrideKB=0 -bResampleForDevice=False -MaxSampleRate=48000.000000 -HighSampleRate=32000.000000 -MedSampleRate=24000.000000 -LowSampleRate=12000.000000 -MinSampleRate=8000.000000 -CompressionQualityModifier=1.000000 -AutoStreamingThreshold=0.000000 -SoundCueCookQualityIndex=-1 [/Script/LinuxTargetPlatform.LinuxTargetSettings] --TargetedRHIs=SF_VULKAN_SM5 +TargetedRHIs=SF_VULKAN_SM6 -[/Script/MacTargetPlatform.MacTargetSettings] --TargetedRHIs=SF_METAL_SM5 -+TargetedRHIs=SF_METAL_SM6 - -[/Script/HardwareTargeting.HardwareTargetingSettings] -TargetedHardwareClass=Desktop -AppliedTargetedHardwareClass=Desktop -DefaultGraphicsPerformance=Maximum -AppliedDefaultGraphicsPerformance=Maximum - -[/Script/WorldPartitionEditor.WorldPartitionEditorSettings] -CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' - -[/Script/Engine.UserInterfaceSettings] -bAuthorizeAutomaticWidgetVariableCreation=False -FontDPIPreset=Standard -FontDPI=72 - -[/Script/Engine.Engine] -+ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/ExampleProject") -+ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/ExampleProject") - [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True bAllowNetworkConnection=True -SecurityToken=72FA219A42EF2331665DBAA277560BD5 +SecurityToken=83CD9B53427AF31BB12CAE94F92BE8C6 bIncludeInShipping=False bAllowExternalStartInShipping=False bCompileAFSProject=False diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index b65ed3e..ee310e6 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -1,12 +1,4 @@ - - -[/Script/CommonUI.CommonUISettings] -CommonButtonAcceptKeyHandling=TriggerClick - -[ConsoleVariables] -CommonUI.CheckKeyboardFocusAndParentage=1 -CommonUI.DisallowUserFocusedWidgetForPendingFocusRecipient=1 -CommonUI.FallbackToDesiredOnAutoRestoreFailure=1 - [/Script/EngineSettings.GeneralProjectSettings] ProjectID=30537DB4496937438003FCA5BA29D5EF +ProjectName=Python Workshop +Description=Godzinny warsztat renderowania z Pythonem w UE 5.7 diff --git a/Content/Blueprints/BP_Enemy.uasset b/Content/Blueprints/BP_Enemy.uasset deleted file mode 100644 index d71cd68..0000000 Binary files a/Content/Blueprints/BP_Enemy.uasset and /dev/null differ diff --git a/Content/Blueprints/BP_GUN.uasset b/Content/Blueprints/BP_GUN.uasset deleted file mode 100644 index b90010f..0000000 Binary files a/Content/Blueprints/BP_GUN.uasset and /dev/null differ diff --git a/Content/Blueprints/BP_Player.uasset b/Content/Blueprints/BP_Player.uasset deleted file mode 100644 index 2190415..0000000 Binary files a/Content/Blueprints/BP_Player.uasset and /dev/null differ diff --git a/Content/Effects/NS_Particles.uasset b/Content/Effects/NS_Particles.uasset deleted file mode 100644 index 9a0f49c..0000000 Binary files a/Content/Effects/NS_Particles.uasset and /dev/null differ diff --git a/Content/ImportedAssets/M_Master.uasset b/Content/ImportedAssets/M_Master.uasset deleted file mode 100644 index 0928f27..0000000 Binary files a/Content/ImportedAssets/M_Master.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/BP_RobotPolice.uasset b/Content/ImportedAssets/RobotPolice/BP_RobotPolice.uasset deleted file mode 100644 index d37a496..0000000 Binary files a/Content/ImportedAssets/RobotPolice/BP_RobotPolice.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/MI_RobotPolice.uasset b/Content/ImportedAssets/RobotPolice/MI_RobotPolice.uasset deleted file mode 100644 index c73e835..0000000 Binary files a/Content/ImportedAssets/RobotPolice/MI_RobotPolice.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/RobotPolice.uasset b/Content/ImportedAssets/RobotPolice/RobotPolice.uasset deleted file mode 100644 index 2be607b..0000000 Binary files a/Content/ImportedAssets/RobotPolice/RobotPolice.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/RobotPolice_Anim.uasset b/Content/ImportedAssets/RobotPolice/RobotPolice_Anim.uasset deleted file mode 100644 index 30a3825..0000000 Binary files a/Content/ImportedAssets/RobotPolice/RobotPolice_Anim.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/RobotPolice_Skeleton.uasset b/Content/ImportedAssets/RobotPolice/RobotPolice_Skeleton.uasset deleted file mode 100644 index 5603a7d..0000000 Binary files a/Content/ImportedAssets/RobotPolice/RobotPolice_Skeleton.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/T_RobotPolice_BC.uasset b/Content/ImportedAssets/RobotPolice/T_RobotPolice_BC.uasset deleted file mode 100644 index 3ce0fe8..0000000 Binary files a/Content/ImportedAssets/RobotPolice/T_RobotPolice_BC.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/T_RobotPolice_E.uasset b/Content/ImportedAssets/RobotPolice/T_RobotPolice_E.uasset deleted file mode 100644 index 3199cef..0000000 Binary files a/Content/ImportedAssets/RobotPolice/T_RobotPolice_E.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/T_RobotPolice_M.uasset b/Content/ImportedAssets/RobotPolice/T_RobotPolice_M.uasset deleted file mode 100644 index 3ec0de5..0000000 Binary files a/Content/ImportedAssets/RobotPolice/T_RobotPolice_M.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/T_RobotPolice_N.uasset b/Content/ImportedAssets/RobotPolice/T_RobotPolice_N.uasset deleted file mode 100644 index 3be6c42..0000000 Binary files a/Content/ImportedAssets/RobotPolice/T_RobotPolice_N.uasset and /dev/null differ diff --git a/Content/ImportedAssets/RobotPolice/T_RobotPolice_R.uasset b/Content/ImportedAssets/RobotPolice/T_RobotPolice_R.uasset deleted file mode 100644 index 259e7db..0000000 Binary files a/Content/ImportedAssets/RobotPolice/T_RobotPolice_R.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Rocks/SM_rock1.uasset b/Content/ImportedAssets/Rocks/SM_rock1.uasset deleted file mode 100755 index a905747..0000000 Binary files a/Content/ImportedAssets/Rocks/SM_rock1.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/Material_001.uasset b/Content/ImportedAssets/Trees/Material_001.uasset deleted file mode 100755 index 4e18932..0000000 Binary files a/Content/ImportedAssets/Trees/Material_001.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/Material_002.uasset b/Content/ImportedAssets/Trees/Material_002.uasset deleted file mode 100755 index 6a995c8..0000000 Binary files a/Content/ImportedAssets/Trees/Material_002.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/Material_003.uasset b/Content/ImportedAssets/Trees/Material_003.uasset deleted file mode 100755 index 9074f2e..0000000 Binary files a/Content/ImportedAssets/Trees/Material_003.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/Material_004.uasset b/Content/ImportedAssets/Trees/Material_004.uasset deleted file mode 100755 index 3701e3c..0000000 Binary files a/Content/ImportedAssets/Trees/Material_004.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/Material_005.uasset b/Content/ImportedAssets/Trees/Material_005.uasset deleted file mode 100755 index abd2222..0000000 Binary files a/Content/ImportedAssets/Trees/Material_005.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/SM_tree01.uasset b/Content/ImportedAssets/Trees/SM_tree01.uasset deleted file mode 100755 index e01ae40..0000000 Binary files a/Content/ImportedAssets/Trees/SM_tree01.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/SM_tree02.uasset b/Content/ImportedAssets/Trees/SM_tree02.uasset deleted file mode 100755 index a57f664..0000000 Binary files a/Content/ImportedAssets/Trees/SM_tree02.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/SM_tree03.uasset b/Content/ImportedAssets/Trees/SM_tree03.uasset deleted file mode 100755 index bddce8e..0000000 Binary files a/Content/ImportedAssets/Trees/SM_tree03.uasset and /dev/null differ diff --git a/Content/ImportedAssets/Trees/SM_tree04.uasset b/Content/ImportedAssets/Trees/SM_tree04.uasset deleted file mode 100755 index 88daca0..0000000 Binary files a/Content/ImportedAssets/Trees/SM_tree04.uasset and /dev/null differ diff --git a/Content/ImportedAssets/pilot/MI_pilot.uasset b/Content/ImportedAssets/pilot/MI_pilot.uasset deleted file mode 100644 index b9e8741..0000000 Binary files a/Content/ImportedAssets/pilot/MI_pilot.uasset and /dev/null differ diff --git a/Content/ImportedAssets/pilot/T_pilot_BC.uasset b/Content/ImportedAssets/pilot/T_pilot_BC.uasset deleted file mode 100644 index 802b286..0000000 Binary files a/Content/ImportedAssets/pilot/T_pilot_BC.uasset and /dev/null differ diff --git a/Content/ImportedAssets/pilot/pilot.uasset b/Content/ImportedAssets/pilot/pilot.uasset deleted file mode 100644 index cc2ded1..0000000 Binary files a/Content/ImportedAssets/pilot/pilot.uasset and /dev/null differ diff --git a/Content/ImportedAssets/pilot/pilot_Anim.uasset b/Content/ImportedAssets/pilot/pilot_Anim.uasset deleted file mode 100644 index 6c5e830..0000000 Binary files a/Content/ImportedAssets/pilot/pilot_Anim.uasset and /dev/null differ diff --git a/Content/ImportedAssets/pilot/pilot_Skeleton.uasset b/Content/ImportedAssets/pilot/pilot_Skeleton.uasset deleted file mode 100644 index 7c9bbac..0000000 Binary files a/Content/ImportedAssets/pilot/pilot_Skeleton.uasset and /dev/null differ diff --git a/Content/Maps/PvPArena.umap b/Content/Maps/PvPArena.umap deleted file mode 100644 index fd6f30d..0000000 Binary files a/Content/Maps/PvPArena.umap and /dev/null differ diff --git a/Content/Maps/Testmap.umap b/Content/Maps/Testmap.umap deleted file mode 100644 index 2a6c520..0000000 Binary files a/Content/Maps/Testmap.umap and /dev/null differ diff --git a/Content/Maps/TurntableStage_BP_RobotPolice.umap b/Content/Maps/TurntableStage_BP_RobotPolice.umap deleted file mode 100755 index 4f240e8..0000000 Binary files a/Content/Maps/TurntableStage_BP_RobotPolice.umap and /dev/null differ diff --git a/Content/Materials/MI_PlayerhairBlue.uasset b/Content/Materials/MI_PlayerhairBlue.uasset deleted file mode 100644 index a0098b6..0000000 Binary files a/Content/Materials/MI_PlayerhairBlue.uasset and /dev/null differ diff --git a/Content/Materials/M_EnemyMaterial.uasset b/Content/Materials/M_EnemyMaterial.uasset deleted file mode 100644 index 267f978..0000000 Binary files a/Content/Materials/M_EnemyMaterial.uasset and /dev/null differ diff --git a/Content/Materials/M_EnemyMaterial1.uasset b/Content/Materials/M_EnemyMaterial1.uasset deleted file mode 100644 index af4a26a..0000000 Binary files a/Content/Materials/M_EnemyMaterial1.uasset and /dev/null differ diff --git a/Content/Materials/M_NewMaterial.uasset b/Content/Materials/M_NewMaterial.uasset deleted file mode 100644 index 9158e8a..0000000 Binary files a/Content/Materials/M_NewMaterial.uasset and /dev/null differ diff --git a/Content/Materials/M_NewMaterial1.uasset b/Content/Materials/M_NewMaterial1.uasset deleted file mode 100644 index 03b7746..0000000 Binary files a/Content/Materials/M_NewMaterial1.uasset and /dev/null differ diff --git a/Content/Materials/M_NewMaterial2.uasset b/Content/Materials/M_NewMaterial2.uasset deleted file mode 100644 index 52a2735..0000000 Binary files a/Content/Materials/M_NewMaterial2.uasset and /dev/null differ diff --git a/Content/Materials/M_Playerhair.uasset b/Content/Materials/M_Playerhair.uasset deleted file mode 100644 index 7ccdc69..0000000 Binary files a/Content/Materials/M_Playerhair.uasset and /dev/null differ diff --git a/Content/NewTextureRenderTarget2D.uasset b/Content/NewTextureRenderTarget2D.uasset deleted file mode 100755 index 49f8fb2..0000000 Binary files a/Content/NewTextureRenderTarget2D.uasset and /dev/null differ diff --git a/Content/Python/ue_workshop/__init__.py b/Content/Python/ue_workshop/__init__.py new file mode 100644 index 0000000..3df02ba --- /dev/null +++ b/Content/Python/ue_workshop/__init__.py @@ -0,0 +1,15 @@ +"""Python rendering workshop for Unreal Engine 5.7.""" +import importlib + +__all__ = ["Scene", "Sequence", "Renderer", "camera", "lighting", "materials", "assets", "blender", "render", "video"] + + +def __getattr__(name): + # Plain Python launchers can import paths/video/blender without importing unreal. + if name in {"Scene", "Sequence"}: + return getattr(importlib.import_module("." + name.lower(), __name__), name) + if name == "Renderer": + return importlib.import_module(".render", __name__).Renderer + if name in __all__: + return importlib.import_module("." + name, __name__) + raise AttributeError(name) diff --git a/Content/Python/ue_workshop/_gltf.py b/Content/Python/ue_workshop/_gltf.py new file mode 100644 index 0000000..f70771f --- /dev/null +++ b/Content/Python/ue_workshop/_gltf.py @@ -0,0 +1,105 @@ +"""Synchronize basic glTF PBR properties preserved by UE mesh reimports.""" +import json +import base64 +import re +import struct + +import unreal + +from .paths import PROJECT + + +def link_textures(source, mesh, destination): + if source.suffix.lower() == ".glb": + data = source.read_bytes() + size, kind = struct.unpack_from("= {minimum}.") + + @classmethod + def from_preset(cls, name="preview", **overrides): + if name not in PRESETS: + raise ValueError("Nieznany preset. Dostepne: " + ", ".join(PRESETS)) + settings = dict(zip(("width", "height", "samples", "warmup"), PRESETS[name])) + settings.update(overrides) + return cls(**settings) + + def render(self, sequence, *, single_frame=None): + self._validate() + return _start(sequence, self, single_frame=single_frame) + + +def is_rendering(): + return _active is not None + + +def _write_status(data): + target = os.environ.get("WORKSHOP_STATUS") + if target: + path = Path(target) + temporary = path.with_suffix(".tmp") + temporary.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") + temporary.replace(path) + + +def _on_error(executor, pipeline, fatal, message): + if _active is not None: + _active["errors"].append(str(message)) + unreal.log_error("[Workshop] MRQ: " + str(message)) + + +def _on_finished(executor, success): + global _active + state = _active + if state is None: + return + result = {"success": False, "output": str(state["output"]), "settings": state["settings"]} + try: + if not success or state["errors"]: + raise RuntimeError("Render nie powiodl sie: " + "; ".join(state["errors"])) + frames = state["output"] / "frames" + count = len(list(frames.glob("frame_*.png"))) + if count != state["frames"]: + raise RuntimeError(f"Zapisano {count} klatek zamiast {state['frames']}.") + if state["mp4"]: + report("encoding") + result["video"] = str(encode(frames, state["fps"], ffmpeg=state["ffmpeg"], + expected_frames=state["frames"])) + result["success"] = True + unreal.log("[Workshop] Gotowe: " + str(state["output"])) + except Exception as exc: + result["error"] = str(exc) + unreal.log_error("[Workshop] " + str(exc)) + finally: + (state["output"] / "result.json").write_text(json.dumps(result, indent=2), encoding="utf-8") + _write_status(result) + _active = None + if os.environ.get("WORKSHOP_BATCH") == "1": + report("closing") + unreal.EditorPythonScripting.set_keep_python_script_alive(False) + + +def _begin_render(delta_seconds): + """Start after the script and pending Slate window updates have returned.""" + if _active is None: + return + _active["ticks"] -= 1 + if _active["ticks"] > 0: + return + unreal.unregister_slate_post_tick_callback(_active.pop("tick_handle")) + try: + subsystem = unreal.get_editor_subsystem(unreal.MoviePipelineQueueSubsystem) + report("rendering", output=str(_active["output"]), frames=_active["frames"]) + # This installs the Sequencer playback-context guard needed during PIE. + subsystem.render_queue_instance_with_executor_instance(_active["queue"], _active["executor"]) + except Exception as exc: + _active["errors"].append(str(exc)) + _on_finished(_active["executor"], False) + + +def start(sequence, preset="preview", mp4=True, ffmpeg=None, single_frame=None): + """Shortcut preserved for existing workshop scripts.""" + return Renderer.from_preset(preset, mp4=mp4, ffmpeg=ffmpeg).render(sequence, single_frame=single_frame) + + +def _start(sequence, renderer, single_frame=None): + global _active + if _active is not None: + raise RuntimeError("Trwa juz render warsztatowy.") + if single_frame is not None and not 0 <= single_frame < sequence.frames: + raise ValueError("Klatka poza zakresem sekwencji.") + mp4 = renderer.mp4 and single_frame is None + ffmpeg_path = executable("ffmpeg", renderer.ffmpeg) if mp4 else None + subsystem = unreal.get_editor_subsystem(unreal.MoviePipelineQueueSubsystem) + if subsystem.is_rendering(): + raise RuntimeError("Movie Render Queue jest zajete.") + report("preparing_render") + # Own queue: do not clear the user's Movie Render Queue. + queue = unreal.MoviePipelineQueue() + job = queue.allocate_new_job(unreal.MoviePipelineExecutorJob) + sequence.save(open_editor=False) + unreal.LevelSequenceEditorBlueprintLibrary.close_level_sequence() + job.map = unreal.SoftObjectPath(sequence.scene.map_path) + job.sequence = unreal.SoftObjectPath(sequence.asset.get_path_name()) + job.job_name = sequence.scene.name + width, height, samples, warmup = renderer.width, renderer.height, renderer.samples, renderer.warmup + stamp = datetime.now().strftime("%Y%m%d_%H%M%S") + "_" + uuid.uuid4().hex[:6] + output = PROJECT / "renders" / sequence.scene.name / stamp + (output / "frames").mkdir(parents=True) + config = job.get_configuration() + settings = config.find_or_add_setting_by_class(unreal.MoviePipelineOutputSetting) + settings.output_directory = unreal.DirectoryPath(str(output / "frames")) + settings.file_name_format = "frame_{frame_number_rel}" + settings.zero_pad_frame_numbers = 4 + settings.output_resolution = unreal.IntPoint(width, height) + settings.use_custom_frame_rate = True + settings.output_frame_rate = unreal.FrameRate(sequence.fps, 1) + settings.use_custom_playback_range = True + settings.custom_start_frame = single_frame if single_frame is not None else 0 + settings.custom_end_frame = single_frame + 1 if single_frame is not None else sequence.frames + settings.flush_disk_writes_per_shot = True + config.find_or_add_setting_by_class(unreal.MoviePipelineImageSequenceOutput_PNG) + config.find_or_add_setting_by_class(unreal.MoviePipelineDeferredPassBase) + aa = config.find_or_add_setting_by_class(unreal.MoviePipelineAntiAliasingSetting) + aa.spatial_sample_count = samples + aa.temporal_sample_count = 1 + aa.engine_warm_up_count = warmup + aa.render_warm_up_count = warmup + aa.render_warm_up_frames = True + executor = unreal.MoviePipelinePIEExecutor() + executor.on_executor_errored_delegate.add_callable_unique(_on_error) + executor.on_executor_finished_delegate.add_callable_unique(_on_finished) + _active = dict(executor=executor, queue=queue, output=output, errors=[], fps=sequence.fps, + frames=1 if single_frame is not None else sequence.frames, + mp4=mp4, ffmpeg=ffmpeg_path, ticks=2, + settings=dict(width=width, height=height, samples=samples, warmup=warmup, fps=sequence.fps)) + try: + _write_status({"state": "rendering", "output": str(output)}) + _active["tick_handle"] = unreal.register_slate_post_tick_callback(_begin_render) + except Exception: + _active = None + raise + unreal.log("[Workshop] Render: " + str(output)) + return output diff --git a/Content/Python/ue_workshop/scene.py b/Content/Python/ue_workshop/scene.py new file mode 100644 index 0000000..1a3407d --- /dev/null +++ b/Content/Python/ue_workshop/scene.py @@ -0,0 +1,86 @@ +"""An isolated generated map, simple meshes and a ready-to-use character.""" +import unreal + +from .paths import GENERATED, slug + + +class Scene: + """Owns one map. Building again replaces only that generated map's actors.""" + + def __init__(self, name="MyShot"): + from . import render + if render.is_rendering() or unreal.get_editor_subsystem(unreal.MoviePipelineQueueSubsystem).is_rendering(): + raise RuntimeError("Poczekaj na zakonczenie renderu przed przebudowa sceny.") + # Discard Sequencer's live bindings before replacing their actors. + current_sequence = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence() + if current_sequence and current_sequence.get_path_name().startswith(GENERATED + "/"): + unreal.LevelSequenceEditorBlueprintLibrary.close_level_sequence() + self.name = slug(name) + self.map_path = f"{GENERATED}/{name}/Map" + self.subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) + editor = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem) + current = editor.get_current_level() + current_path = current.get_outer().get_path_name().split(".")[0] if current else "" + if current_path == self.map_path: + # This map is wholly generated by this exercise, never a user's other map. + for actor in self.subsystem.get_all_level_actors(): + if "Workshop" in [str(tag) for tag in actor.tags]: + self.subsystem.destroy_actor(actor) + else: + if unreal.EditorLoadingAndSavingUtils.get_dirty_map_packages(): + raise RuntimeError("Zapisz zmiany aktualnej mapy przed utworzeniem sceny warsztatowej.") + if unreal.EditorAssetLibrary.does_asset_exist(self.map_path): + if not editor.load_level(self.map_path): + raise RuntimeError("Nie mozna otworzyc " + self.map_path) + for actor in self.subsystem.get_all_level_actors(): + if "Workshop" in [str(tag) for tag in actor.tags]: + self.subsystem.destroy_actor(actor) + elif not editor.new_level(self.map_path): + raise RuntimeError("Nie mozna utworzyc " + self.map_path) + + def spawn(self, actor_class, name, location=(0, 0, 0), rotation=(0, 0, 0)): + actor = self.subsystem.spawn_actor_from_class(actor_class, unreal.Vector(*location), + unreal.Rotator(*rotation)) + if actor is None: + raise RuntimeError("Nie mozna utworzyc aktora " + name) + actor.set_actor_label(name) + actor.tags = ["Workshop"] + actor.set_folder_path("Workshop") + return actor + + def mesh(self, asset, name="Model", location=(0, 0, 0), scale=(1, 1, 1), material=None): + mesh = unreal.load_asset(asset) if isinstance(asset, str) else asset + if not isinstance(mesh, unreal.StaticMesh): + raise TypeError(f"Oczekiwano StaticMesh: {asset}") + actor = self.spawn(unreal.StaticMeshActor, name, location) + actor.static_mesh_component.set_mobility(unreal.ComponentMobility.MOVABLE) + actor.static_mesh_component.set_static_mesh(mesh) + actor.set_actor_scale3d(unreal.Vector(*scale)) + if material is not None: + actor.static_mesh_component.set_material(0, material) + return actor + + def primitive(self, shape="Cube", **kwargs): + if shape not in {"Cube", "Sphere", "Cylinder", "Cone", "Plane"}: + raise ValueError("Ksztalt: Cube, Sphere, Cylinder, Cone lub Plane.") + return self.mesh(f"/Engine/BasicShapes/{shape}.{shape}", **kwargs) + + def character(self, material=None, head_scale=1.0): + root = self.spawn(unreal.StaticMeshActor, "Character") + root.static_mesh_component.set_mobility(unreal.ComponentMobility.MOVABLE) + for shape, name, position, scale in [ + ("Cube", "Body", (0, 0, 90), (0.55, 0.8, 0.9)), + ("Sphere", "Head", (0, 0, 175), (0.7 * head_scale,) * 3), + ("Cube", "LeftLeg", (0, -23, 25), (0.35, 0.3, 0.5)), + ("Cube", "RightLeg", (0, 23, 25), (0.35, 0.3, 0.5)), + ("Sphere", "Nose", (34, 0, 177), (0.24, 0.2, 0.2)), + ]: + actor = self.primitive(shape, name=name, location=position, scale=scale, material=material) + actor.attach_to_actor(root, "", unreal.AttachmentRule.KEEP_WORLD, + unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, False) + return root + + def save(self): + editor = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem) + if not editor.save_current_level(): + raise RuntimeError("Nie mozna zapisac mapy " + self.map_path) diff --git a/Content/Python/ue_workshop/sequence.py b/Content/Python/ue_workshop/sequence.py new file mode 100644 index 0000000..0e26ace --- /dev/null +++ b/Content/Python/ue_workshop/sequence.py @@ -0,0 +1,81 @@ +"""Frame-based Sequencer helpers. Ranges are [start, end), rotations roll/pitch/yaw.""" +import unreal + +from .paths import GENERATED, slug + + +class Sequence: + def __init__(self, scene, name="Shot", fps=24, frames=48): + if not isinstance(fps, int) or fps < 1 or not isinstance(frames, int) or frames < 1: + raise ValueError("FPS i liczba klatek musza byc dodatnimi liczbami calkowitymi.") + self.scene, self.fps, self.frames = scene, fps, frames + path = f"{GENERATED}/{scene.name}/LS_{slug(name)}" + self.asset = unreal.load_asset(path) + if self.asset and unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence() == self.asset: + unreal.LevelSequenceEditorBlueprintLibrary.close_level_sequence() + if self.asset is None: + self.asset = unreal.AssetToolsHelpers.get_asset_tools().create_asset( + "LS_" + name, f"{GENERATED}/{scene.name}", unreal.LevelSequence, + unreal.LevelSequenceFactoryNew()) + for binding in self.asset.get_bindings(): + binding.remove() + for track in self.asset.get_tracks(): + self.asset.remove_track(track) + self.asset.set_display_rate(unreal.FrameRate(fps, 1)) + self.asset.set_playback_start(0) + self.asset.set_playback_end(frames) + self._bindings = {} + self._transforms = {} + + def bind(self, actor): + key = actor.get_path_name() + if key not in self._bindings: + self._bindings[key] = self.asset.add_possessable(actor) + return self._bindings[key] + + def camera(self, actor): + binding = self.bind(actor) + tracks = self.asset.find_tracks_by_type(unreal.MovieSceneCameraCutTrack) + track = tracks[0] if tracks else self.asset.add_track(unreal.MovieSceneCameraCutTrack) + for old in track.get_sections(): + track.remove_section(old) + section = track.add_section() + section.set_range(0, self.frames) + binding_id = unreal.MovieSceneObjectBindingID() + binding_id.set_editor_property("guid", binding.get_id()) + section.set_camera_binding_id(binding_id) + + def transform_key(self, actor, frame, location=None, rotation=None, scale=None, interpolation="linear"): + if not isinstance(frame, int) or not 0 <= frame < self.frames: + raise ValueError(f"Klatka musi nalezec do 0..{self.frames - 1}.") + modes = {"linear": unreal.MovieSceneKeyInterpolation.LINEAR, + "auto": unreal.MovieSceneKeyInterpolation.AUTO, + "constant": unreal.MovieSceneKeyInterpolation.CONSTANT} + if interpolation not in modes: + raise ValueError("Interpolacja: linear, auto lub constant.") + key = actor.get_path_name() + if key not in self._transforms: + track = self.bind(actor).add_track(unreal.MovieScene3DTransformTrack) + section = track.add_section() + section.set_range(0, self.frames) + channels = section.get_all_channels() + initial = actor.get_actor_transform() + r = initial.rotation.rotator() + defaults = [*initial.translation.to_tuple(), r.roll, r.pitch, r.yaw, + *initial.scale3d.to_tuple()] + for channel, value in zip(channels, defaults): + channel.set_default(float(value)) + self._transforms[key] = channels + channels = self._transforms[key] + for offset, values in [(0, location), (3, rotation), (6, scale)]: + if values is not None: + if len(values) != 3: + raise ValueError("Transformacja wymaga trzech wartosci.") + for channel, value in zip(channels[offset:offset + 3], values): + channel.add_key(unreal.FrameNumber(frame), float(value), interpolation=modes[interpolation]) + + def save(self, open_editor=True): + self.scene.save() + unreal.EditorAssetLibrary.save_loaded_asset(self.asset) + if open_editor: + unreal.LevelSequenceEditorBlueprintLibrary.open_level_sequence(self.asset) diff --git a/Content/Python/ue_workshop/video.py b/Content/Python/ue_workshop/video.py new file mode 100644 index 0000000..dc888f9 --- /dev/null +++ b/Content/Python/ue_workshop/video.py @@ -0,0 +1,27 @@ +"""FFmpeg encoding; this module also works outside Unreal.""" +from pathlib import Path +import subprocess + +from .paths import executable + + +def encode(frames, fps, output=None, ffmpeg=None, expected_frames=None): + frames = Path(frames).resolve() + if fps <= 0: + raise ValueError("FPS musi byc dodatni.") + images = sorted(frames.glob("frame_*.png")) + count = len(images) + if not count or any(p.name != f"frame_{i:04d}.png" for i, p in enumerate(images)): + raise RuntimeError(f"Brak kompletnej numeracji frame_0000.png, frame_0001.png... w {frames}") + if expected_frames is not None and count != expected_frames: + raise RuntimeError(f"Render zapisal {count} klatek zamiast {expected_frames}.") + output = Path(output) if output else frames.parent / "film.mp4" + output.parent.mkdir(parents=True, exist_ok=True) + command = [executable("ffmpeg", ffmpeg), "-hide_banner", "-loglevel", "error", "-y", + "-framerate", str(fps), "-start_number", "0", "-i", str(frames / "frame_%04d.png"), + "-frames:v", str(count), "-c:v", "libx264", "-crf", "20", "-pix_fmt", "yuv420p", + "-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2", "-movflags", "+faststart", str(output)] + result = subprocess.run(command, capture_output=True, text=True) + if result.returncode: + raise RuntimeError("FFmpeg: " + result.stderr[-4000:]) + return output.resolve() diff --git a/Content/Sequences/LS_Cinematic.uasset b/Content/Sequences/LS_Cinematic.uasset deleted file mode 100644 index aa12e4a..0000000 Binary files a/Content/Sequences/LS_Cinematic.uasset and /dev/null differ diff --git a/Content/Sequences/LS_IntroScene.uasset b/Content/Sequences/LS_IntroScene.uasset deleted file mode 100644 index 23b5611..0000000 Binary files a/Content/Sequences/LS_IntroScene.uasset and /dev/null differ diff --git a/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice.uasset b/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice.uasset deleted file mode 100755 index bd8a95c..0000000 Binary files a/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice.uasset and /dev/null differ diff --git a/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice_RobotPolice_Anim.uasset b/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice_RobotPolice_Anim.uasset deleted file mode 100755 index 4abf196..0000000 Binary files a/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice_RobotPolice_Anim.uasset and /dev/null differ diff --git a/ExampleProject.uproject b/ExampleProject.uproject index cfcec1f..9ec4ade 100644 --- a/ExampleProject.uproject +++ b/ExampleProject.uproject @@ -1,98 +1,32 @@ { - "FileVersion": 3, - "EngineAssociation": "5.8", - "Category": "", - "Description": "", - "Modules": [ - { - "Name": "ExampleProject", - "Type": "Runtime", - "LoadingPhase": "Default" - } - ], - "Plugins": [ - { - "Name": "Bridge", - "Enabled": true - }, - { - "Name": "Dataflow", - "Enabled": true - }, - { - "Name": "Fab", - "Enabled": true - }, - { - "Name": "MeshPainting", - "Enabled": true - }, - { - "Name": "PCG", - "Enabled": true - }, - { - "Name": "PluginUtils", - "Enabled": true - }, - { - "Name": "PluginBrowser", - "Enabled": true - }, - { - "Name": "GLTFExporter", - "Enabled": true - }, - { - "Name": "InterchangeEditor", - "Enabled": true, - "SupportedTargetPlatforms": [ - "Win64", - "Linux", - "Mac" - ] - }, - { - "Name": "MetaHumanSDK", - "Enabled": false - }, - { - "Name": "Interchange", - "Enabled": true, - "SupportedTargetPlatforms": [ - "Win64", - "Linux", - "Mac" - ] - }, - { - "Name": "SkeletalMeshModelingTools", - "Enabled": true - }, - { - "Name": "ChaosEditor", - "Enabled": true - }, - { - "Name": "InterchangeTests", - "Enabled": true, - "SupportedTargetPlatforms": [ - "Win64", - "Linux", - "Mac" - ] - }, - { - "Name": "EditorScriptingUtilities", - "Enabled": true - }, - { - "Name": "MovieRenderPipeline", - "Enabled": true - }, - { - "Name": "SequencerScripting", - "Enabled": true - } - ] + "FileVersion": 3, + "EngineAssociation": "5.7", + "Category": "Education", + "Description": "Python rendering workshop", + "Plugins": [ + { + "Name": "PythonScriptPlugin", + "Enabled": true + }, + { + "Name": "EditorScriptingUtilities", + "Enabled": true + }, + { + "Name": "SequencerScripting", + "Enabled": true + }, + { + "Name": "MovieRenderPipeline", + "Enabled": true + }, + { + "Name": "Interchange", + "Enabled": true + }, + { + "Name": "InterchangeEditor", + "Enabled": true + } + ] } diff --git a/Plugins/DemoPlugin/DemoPlugin.uplugin b/Plugins/DemoPlugin/DemoPlugin.uplugin deleted file mode 100755 index 9684492..0000000 --- a/Plugins/DemoPlugin/DemoPlugin.uplugin +++ /dev/null @@ -1,16 +0,0 @@ -{ - "FileVersion": 3, - "Version": 1, - "VersionName": "1.0.0", - "FriendlyName": "Demo Render Bridge", - "Description": "Minimalny przyklad wywolania C++ z Unreal Python.", - "Category": "Examples", - "CanContainContent": false, - "Modules": [ - { - "Name": "DemoPlugin", - "Type": "Editor", - "LoadingPhase": "Default" - } - ] -} diff --git a/Plugins/DemoPlugin/README.md b/Plugins/DemoPlugin/README.md deleted file mode 100755 index 9495063..0000000 --- a/Plugins/DemoPlugin/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Demo Render Bridge: minimalny prawdziwy plugin UE C++ -> Python - -Plugin pokazuje rzeczywisty przeplyw: - -```text -TextureRenderTarget2D - -> Python wywoluje UFUNCTION - -> C++: FImageUtils::GetRenderTargetImage - -> C++: FImageUtils::CompressImage(".png") - -> TArray wraca do Pythona - -> Python zapisuje bytes na dysku -``` - -## Struktura - -```text -DemoPlugin.uplugin -Source/DemoPlugin/DemoPlugin.Build.cs -Source/DemoPlugin/Public/DemoRenderBridge.h -Source/DemoPlugin/Private/DemoRenderBridge.cpp -Source/DemoPlugin/Private/DemoPlugin.cpp -use_plugin.py -``` - -## Jak uruchomic - -1. Skopiuj caly katalog jako `/Plugins/DemoPlugin`. -2. Upewnij sie, ze projekt ma modul C++ i wlaczony `Python Editor Script Plugin`. -3. Otworz projekt i zaakceptuj kompilacje pluginu albo zbuduj projekt w IDE. -4. Utworz asset `TextureRenderTarget2D` pod `/Game/Demo/RT_Demo` lub zmien - sciezke `RENDER_TARGET_ASSET` w `use_plugin.py`. -5. W Unreal Editor wybierz `Tools -> Execute Python Script` i uruchom - `use_plugin.py`. -6. Wynik pojawi sie jako `Saved/demo_from_cpp.png`. - -## Co jest mostem do Pythona - -- `UCLASS()` rejestruje klase w refleksji. -- `UFUNCTION(BlueprintCallable)` eksportuje funkcje. -- `UDemoRenderBridge` staje sie `unreal.DemoRenderBridge`. -- `RenderTargetToPng` staje sie `render_target_to_png`. -- `UTextureRenderTarget2D*` przyjmuje obiekt zaladowany w Pythonie. -- `TArray` wraca jako kolekcja konwertowalna przez `bytes(...)`. - -To jest odchudzona wersja prawdziwego render bridge'a: pomija wybor formatu, -luminancje, specjalne kodowanie RGB, transport sieciowy i rozbudowana obsluge -bledow. Zachowuje jednak faktyczne wywolania API C++ Unreala i prawdziwy -kontrakt widoczny z Unreal Python. diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/DemoPlugin.Build.cs b/Plugins/DemoPlugin/Source/DemoPlugin/DemoPlugin.Build.cs deleted file mode 100755 index f5b5329..0000000 --- a/Plugins/DemoPlugin/Source/DemoPlugin/DemoPlugin.Build.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UnrealBuildTool; - -public class DemoPlugin : ModuleRules -{ - public DemoPlugin(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange(new[] - { - "Core", - "CoreUObject", - "Engine", - "ImageCore" - }); - } -} diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoPlugin.cpp b/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoPlugin.cpp deleted file mode 100755 index ecec6c1..0000000 --- a/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoPlugin.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "Modules/ModuleManager.h" - -IMPLEMENT_MODULE(FDefaultModuleImpl, DemoPlugin) - -//This registers that it is the implementation of DemoPlugin module -//that can be loaded by UE - -//Here could be startup scripts or sth diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoRenderBridge.cpp b/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoRenderBridge.cpp deleted file mode 100755 index a017a70..0000000 --- a/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoRenderBridge.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "DemoRenderBridge.h" - -#include "Engine/TextureRenderTarget2D.h" -#include "ImageCore.h" -#include "ImageUtils.h" - -bool UDemoRenderBridge::RenderTargetToPng( - UTextureRenderTarget2D* RenderTarget, - TArray& OutPngBytes, - FString& OutError) -{ - OutPngBytes.Reset(); - OutError.Reset(); - - if (!IsInGameThread()) - { - OutError = TEXT("Function must be ran on Game Thread"); - return false; - } - - if (!IsValid(RenderTarget)) - { - OutError = TEXT("Render Target reference is invalid"); - return false; - } - - // Faktyczne wywolanie API C++ Unreala: odczyt Render Targetu do FImage. - FImage Image; - if (!FImageUtils::GetRenderTargetImage(RenderTarget, Image)) - { - OutError = TEXT("Couldnt read Render Target"); - return false; - } - - // Faktyczne wywolanie API C++ Unreala: kompresja FImage do PNG w pamieci. - TArray64 Encoded; - if (!FImageUtils::CompressImage(Encoded, TEXT(".png"), Image, 0)) - { - OutError = TEXT("Couldnt encode png"); - return false; - } - - if (Encoded.Num() > MAX_int32) - { - OutError = TEXT("Result too large"); - return false; - } - - OutPngBytes.Append(Encoded.GetData(), static_cast(Encoded.Num())); - return true; -} diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/Public/DemoRenderBridge.h b/Plugins/DemoPlugin/Source/DemoPlugin/Public/DemoRenderBridge.h deleted file mode 100755 index 3de6c44..0000000 --- a/Plugins/DemoPlugin/Source/DemoPlugin/Public/DemoRenderBridge.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "Kismet/BlueprintFunctionLibrary.h" -#include "DemoRenderBridge.generated.h" - -class UTextureRenderTarget2D; - -/** Funkcje C++ dostepne w Blueprintach i Python API */ -UCLASS() -class DEMOPLUGIN_API UDemoRenderBridge : public UBlueprintFunctionLibrary -{ - GENERATED_BODY() - -public: - - /** Odczytuje Render Target z GPU i zwraca zakodowany PNG w pamieci. */ - UFUNCTION(BlueprintCallable, Category = "Demo|C++ Bridge") - static bool RenderTargetToPng( - UTextureRenderTarget2D* RenderTarget, - TArray& OutPngBytes, - FString& OutError); -}; diff --git a/Plugins/DemoPlugin/use_plugin.py b/Plugins/DemoPlugin/use_plugin.py deleted file mode 100755 index 63801ae..0000000 --- a/Plugins/DemoPlugin/use_plugin.py +++ /dev/null @@ -1,19 +0,0 @@ -from pathlib import Path - -import unreal - -RENDER_TARGET_ASSET = "/Game/NewTextureRenderTarget2D" -OUTPUT_FILE = Path(unreal.Paths.project_saved_dir()) / "demo_from_cpp.png" - -render_target = unreal.load_asset(RENDER_TARGET_ASSET) - -# Parametry Out funkcji UFUNCTION wracaja w Pythonie jako tuple. -png_bytes, error = unreal.DemoRenderBridge.render_target_to_png( - render_target -) - -if error: - raise RuntimeError(error) - -OUTPUT_FILE.write_bytes(bytes(png_bytes)) -unreal.log(f"preview saved as {OUTPUT_FILE}") diff --git a/README.md b/README.md index 3289dc4..405a5ec 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,193 @@ -# PythonInUnreal +# Python w Unrealu — warsztat na godzinę +UE **5.7**, Windows / Linux. Biblioteka `ue_workshop` automatyzuje scenę, +Sequencer, Movie Render Queue i składanie MP4. Nie wymaga kompilacji C++ ani pip. +Pierwsze ćwiczenie korzysta wyłącznie z brył silnika. Blender jest opcjonalny. + +Jeśli używasz wcześniejszej kopii projektu z C++ i edytor zgłasza +`Target Upgrade Required`, zamknij UE i usuń pozostałości starej kompilacji: +`Binaries/`, `Intermediate/Build/` oraz `Intermediate/TargetInfo.json`. +Nowy projekt nie ma własnego targetu C++ i nie wymaga aktualizacji `Target.cs`. + +## Przygotowanie przed spotkaniem + +1. Zainstaluj UE 5.7 i otwórz `ExampleProject.uproject`. Poczekaj na kompilację shaderów. +2. Zainstaluj FFmpeg z enkoderem `libx264` i dodaj do PATH, jeżeli chcesz MP4. + Bez FFmpeg można renderować PNG: `render.start(shot, mp4=False)`. +3. Zwykły Python 3.10+ jest potrzebny tylko do launchera z terminala. +4. Sprawdź `python tools/run_workshop.py --check` i wykonaj ćwiczenie próbnie. + +W projekcie są już włączone Python Editor Script Plugin, Editor Scripting Utilities, +Sequencer Scripting, Movie Render Queue oraz Interchange. Python jest narzędziem +automatyzacji edytora; nie służy tu do gameplayu. + +## Pierwszy skrypt w edytorze + +Zapisz aktualną mapę. Wybierz **File → Execute Python Script** i wskaż +`exercises/my_shot.py`. Możesz też wpisać w konsoli UE: + +```text +py "C:/sciezka/do/projektu/exercises/my_shot.py" +``` + +Powstanie prosta postać, podłoga, trzy światła, kamera i dwusekundowa animacja. +Obejrzyj ją w Sequencerze. Zmień kolor, `head_scale`, kamerę albo klucze i uruchom +plik ponownie. Skrypt jest odczytywany ponownie z dysku. + +Ustaw `RENDER = True`, aby wyrenderować film. Domyślny preset `preview` to +640×360, 24 FPS, 48 klatek. `final` to 1280×720 i więcej próbek. +Szybkie sprawdzenie obrazu: zamiast pełnego renderu użyj +`renderer.render(shot, single_frame=24)`. + +Własne ustawienia wpisujesz bezpośrednio w skrypcie uczestnika: + +```python +from ue_workshop import Renderer + +renderer = Renderer(width=1920, height=1080, samples=8, warmup=32, mp4=True) +renderer.render(shot) +# Albo zamiast filmu: renderer.render(shot, single_frame=24) +``` + +`width` i `height` to piksele, `samples` to liczba próbek przestrzennych na klatkę, +a `warmup` to liczba klatek rozgrzewki. Wymiary i próbki muszą być dodatnimi +liczbami całkowitymi, rozgrzewka może wynosić zero. FPS pochodzi z sekwencji. +Można też użyć `Renderer.from_preset("final", width=1920, height=1080)`: +argumenty nadpisują wybrane wartości presetu. Obiektu użyjesz ponownie do kolejnych +sekwencji po zakończeniu poprzedniego renderu. Ustawienia są zapisane w `result.json`. +Dotychczasowe `render.start(...)` nadal działa jako skrót do presetów. + +Wyniki: `renders/MyShot//frames/`, `film.mp4`, `result.json`. +Każdy render ma nowy katalog. Nieudane kodowanie zachowuje klatki. +MP4 powstaje dopiero po zakończeniu MRQ; nie zamykaj wtedy edytora. +Na czas przebudowy sceny i renderowania biblioteka zamyka kartę Sequencera. +Render startuje po powrocie ze skryptu. Sekwencję można ponownie otworzyć +z Content Browser; ponowne uruchomienie ćwiczenia też ją otwiera. + +## Z terminala, bez okna edytora + +Drugi plik obok ćwiczenia uruchamia `my_shot.py` bez okna UE, czeka na zakończenie +renderu oraz kodowania MP4 i automatycznie zamyka uruchomiony proces edytora: + +```text +python exercises/my_shot_headless.py +``` + +Uruchamiaj go **zwykłym Pythonem z terminala**, nie przez Execute Python Script +w Unreal. Korzysta z obiektu `renderer` i ustawień zapisanych w `my_shot.py`. +Wymusza render również przy `RENDER=False`. Wynik trafia do tego samego +`renders/MyShot//film.mp4`. Można dopisać `--editor`, `--timeout` +lub `--check`; ścieżka skryptu jest ustalana względem pliku launchera. + +Domyślnie konsola pokazuje etapy: uruchamianie UE, przygotowanie sceny, +renderowanie, składanie MP4 i zamykanie UE, a na końcu wynik i ścieżkę do logu. +Podczas renderowania podaje liczbę zapisanych PNG; przy dłuższym oczekiwaniu +co 10 sekund potwierdza, że proces nadal działa. To nie jest szacowany procent +całego zadania — rozgrzewka, zapis klatek i kodowanie mają różne czasy wykonania. +Pełne logi trafiają do `Saved/Workshop/.log`, także przy błędzie lub timeout. +Żeby widzieć logi UE na żywo w terminalu, dodaj: + +```text +python exercises/my_shot_headless.py --verbose +``` + +Ścieżki filmu i logu są klikalne w obsługujących to terminalach, m.in. +Windows Terminal i terminalu VS Code (zwykle Ctrl+klik). W pozostałych konsolach +oraz przy przekierowaniu wyjścia do pliku wypisywane są zwykłe ścieżki. +Można wymusić linki opcją `--links always` lub wyłączyć je przez `--links never`. + +Ogólny launcher do pozostałych skryptów: + +```text +python tools/run_workshop.py --check +python tools/run_workshop.py exercises/my_shot.py --render +python tools/run_workshop.py exercises/my_shot.py --single-frame 24 +``` + +Bez `--render` launcher tylko wykonuje skrypt i zapisuje scenę (chyba że sam skrypt +uruchamia render). Zwraca kod 0 przy powodzeniu i 1 przy błędzie. Domyślny limit +to 900 sekund; można zmienić `--timeout`. Nie używaj `-nullrhi` do renderowania. +Tryb bez okna nadal wymaga GPU, sterowników i na Linuksie działającego Vulkan. +Obsługa Linuksa jest przygotowana w kodzie; trzeba sprawdzić ją na docelowej maszynie. + +Ścieżki programów można ustawić zmiennymi środowiskowymi: + +```powershell +$env:WORKSHOP_UNREAL = 'C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe' +$env:WORKSHOP_BLENDER = 'C:\Program Files\Blender Foundation\Blender 5.1\blender.exe' +$env:WORKSHOP_FFMPEG = 'C:\tools\ffmpeg\bin\ffmpeg.exe' +``` + +```bash +export WORKSHOP_UNREAL=/opt/UnrealEngine/Engine/Binaries/Linux/UnrealEditor +export WORKSHOP_BLENDER=/usr/bin/blender +export WORKSHOP_FFMPEG=/usr/bin/ffmpeg +python3 tools/run_workshop.py --check --blender +``` + +Ścieżki nie są zapisane na sztywno w bibliotece. Launcher ma domyślną lokalizację +instalacji UE 5.7 na Windows; na Linuksie korzysta ze zmiennej lub PATH. + +## Blender → UE → film + +Przykładowy model znajduje się w `source_assets/character/character.blend`. +Można go też odtworzyć (generator nie nadpisuje istniejącego pliku): + +```text +blender --background --python-exit-code 1 --python tools/create_character.py +``` + +Otwórz model w Blenderze, powiększ obiekt `Head`, zmień kolor materiału i **zapisz**. +Uruchom `examples/blender_character.py` w UE albo: + +```text +python tools/run_workshop.py examples/blender_character.py +``` + +Skrypt uruchamia osobny proces Blendera, eksportuje kolekcję `Character` do GLB, +aktualizuje model i materiały w UE, buduje sekwencję i renderuje MP4. +Blender nie musi być otwarty. Eksport czyta ostatni zapis z dysku. + +Umowa dla modelu: statyczne siatki w kolekcji `Character`, metry w Blenderze, +stopy na Z=0, przód w +X. Modyfikatory są uwzględniane, obiekty łączone do jednej +siatki z zachowaniem slotów materiałów. Origin eksportu to zero świata. +Podstawowe PBR: Principled BSDF, kolory, roughness, metallic, obsługiwane tekstury. +Tekstury PNG/JPEG są wyciągane z GLB i aktualizowane w wydzielonym katalogu +`WorkshopTextures`. Parametry PBR synchronizujemy jawnie, ponieważ standardowy +reimport siatki w UE zachowuje wcześniejsze materiały. Zmiany w tych importowanych +materiałach wykonuj w Blenderze; skrypt nadpisuje wspierane parametry ich źródłem. +Dowolne grafy proceduralne, rig, animacje szkieletowe, włosy i shader skóry nie +wchodzą w zakres v1. Materiały proceduralne należy wypalić do tekstur. + +## Układ i zasady + +- `Content/Python/ue_workshop/` — biblioteka, automatycznie dostępna w Pythonie UE. +- `exercises/my_shot.py` — jedyny wymagany plik uczestnika; zrób kopię przed edycją. +- `examples/` — dodatkowa kamera i Blender; ćwiczenie startowe jest też rozwiązaniem bazowym. +- `tools/` — launcher, eksport/generator Blendera i testy integracyjne. +- `docs/` — ściąga API i scenariusz spotkania. +- `Content/Workshop/Generated/` — mapy, sekwencje, importy i materiały generowane w UE. +- `build/`, `renders/` — pliki pośrednie i wyniki, pomijane przez Git. +- `archive/legacy_python/` — stare skrypty; poza ścieżką importu UE. +- `DemoAssets/` — zachowane źródłowe modele z poprzedniego projektu, opcjonalne. + +`Scene("MyShot")` zarządza mapą `/Game/Workshop/Generated/MyShot/Map`. +Ponowne wykonanie usuwa w niej tylko aktorów z tagiem `Workshop`, a sekwencję +odbudowuje. Własną scenę nazwij inaczej, np. `Scene("AniaShot")`. +Generowana sekwencja jest w całości zastępowana: ręczne zmiany zachowaj w jej kopii. +Mapy spoza warsztatu nie są czyszczone; przy niezapisanej obcej mapie skrypt prosi o zapis. +Nie usuwaj całego folderu Generated podczas pracy — zawiera aktualnie używane assety. + +## Weryfikacja + +```text +python -m unittest discover -s tests -v +python tools/run_workshop.py tools/smoke_scene.py +python tools/run_workshop.py tools/smoke_blender.py +python tools/run_workshop.py tools/smoke_renderer.py +``` + +Pierwszy test integracyjny wykonuje ćwiczenie dwa razy, sprawdza duplikaty, +renderuje dwie klatki i koduje MP4. Drugi wymaga Blendera i sprawdza powtórny +import zmienionej geometrii oraz materiałów i render. +Opis sprawdzonego środowiska: `docs/validation.md`. diff --git a/Source/ExampleProject.Target.cs b/Source/ExampleProject.Target.cs deleted file mode 100755 index c18e0cd..0000000 --- a/Source/ExampleProject.Target.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class ExampleProjectTarget : TargetRules -{ - public ExampleProjectTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Game; - DefaultBuildSettings = BuildSettingsVersion.V7; - - ExtraModuleNames.AddRange( new string[] { "ExampleProject" } ); - } -} diff --git a/Source/ExampleProject/ExampleProject.Build.cs b/Source/ExampleProject/ExampleProject.Build.cs deleted file mode 100755 index c25b174..0000000 --- a/Source/ExampleProject/ExampleProject.Build.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -using UnrealBuildTool; - -public class ExampleProject : ModuleRules -{ - public ExampleProject(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); - - PrivateDependencyModuleNames.AddRange(new string[] { }); - - // Uncomment if you are using Slate UI - // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); - - // Uncomment if you are using online features - // PrivateDependencyModuleNames.Add("OnlineSubsystem"); - - // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true - } -} diff --git a/Source/ExampleProject/ExampleProject.cpp b/Source/ExampleProject/ExampleProject.cpp deleted file mode 100755 index 881ecf4..0000000 --- a/Source/ExampleProject/ExampleProject.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#include "ExampleProject.h" -#include "Modules/ModuleManager.h" - -IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ExampleProject, "ExampleProject" ); diff --git a/Source/ExampleProject/ExampleProject.h b/Source/ExampleProject/ExampleProject.h deleted file mode 100755 index c56eab6..0000000 --- a/Source/ExampleProject/ExampleProject.h +++ /dev/null @@ -1,6 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" - diff --git a/Source/ExampleProject/dummy.cpp b/Source/ExampleProject/dummy.cpp deleted file mode 100755 index 8496413..0000000 --- a/Source/ExampleProject/dummy.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "dummy.h" - -dummy::dummy() -{ -} - -dummy::~dummy() -{ -} diff --git a/Source/ExampleProject/dummy.h b/Source/ExampleProject/dummy.h deleted file mode 100755 index 33000dc..0000000 --- a/Source/ExampleProject/dummy.h +++ /dev/null @@ -1,15 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" - -/** - * - */ -class EXAMPLEPROJECT_API dummy -{ -public: - dummy(); - ~dummy(); -}; diff --git a/Source/ExampleProjectEditor.Target.cs b/Source/ExampleProjectEditor.Target.cs deleted file mode 100755 index ef54f9c..0000000 --- a/Source/ExampleProjectEditor.Target.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class ExampleProjectEditorTarget : TargetRules -{ - public ExampleProjectEditorTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Editor; - DefaultBuildSettings = BuildSettingsVersion.V7; - - ExtraModuleNames.AddRange( new string[] { "ExampleProject" } ); - } -} diff --git a/archive/README.md b/archive/README.md new file mode 100644 index 0000000..bff6777 --- /dev/null +++ b/archive/README.md @@ -0,0 +1,3 @@ +Stare skrypty zostaly zachowane jako material dodatkowy. Nie sa czescia +nowego warsztatu i moga wymagac assetow/uslug z poprzedniej wersji projektu. +Zachowano rowniez lokalna zmiane w legacy_python/basic_scripting_demo/3.py. diff --git a/Content/Python/auto_import_pipeline.py b/archive/legacy_python/auto_import_pipeline.py similarity index 100% rename from Content/Python/auto_import_pipeline.py rename to archive/legacy_python/auto_import_pipeline.py diff --git a/Content/Python/basic_scripting_demo/1.py b/archive/legacy_python/basic_scripting_demo/1.py old mode 100755 new mode 100644 similarity index 97% rename from Content/Python/basic_scripting_demo/1.py rename to archive/legacy_python/basic_scripting_demo/1.py index 413fbec..6ebbf7d --- a/Content/Python/basic_scripting_demo/1.py +++ b/archive/legacy_python/basic_scripting_demo/1.py @@ -1,5 +1,5 @@ -import unreal - -sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) -actors = sub.get_all_level_actors() +import unreal + +sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +actors = sub.get_all_level_actors() unreal.log(f"{len(actors)} actors in level") \ No newline at end of file diff --git a/Content/Python/basic_scripting_demo/2.py b/archive/legacy_python/basic_scripting_demo/2.py old mode 100755 new mode 100644 similarity index 97% rename from Content/Python/basic_scripting_demo/2.py rename to archive/legacy_python/basic_scripting_demo/2.py index 7db0462..ebfba01 --- a/Content/Python/basic_scripting_demo/2.py +++ b/archive/legacy_python/basic_scripting_demo/2.py @@ -1,16 +1,16 @@ -import unreal - -OLD = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree04.SM_tree04") -NEW = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree01.SM_tree01") - -sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) -actors = sub.get_all_level_actors() - -old_mesh_count = 0 - -for actor in actors: - comp = actor.get_component_by_class(unreal.StaticMeshComponent) - if comp and comp.static_mesh == OLD: - old_mesh_count += 1 - +import unreal + +OLD = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree04.SM_tree04") +NEW = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree01.SM_tree01") + +sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +actors = sub.get_all_level_actors() + +old_mesh_count = 0 + +for actor in actors: + comp = actor.get_component_by_class(unreal.StaticMeshComponent) + if comp and comp.static_mesh == OLD: + old_mesh_count += 1 + print(f"Found {old_mesh_count} instances of the old mesh") \ No newline at end of file diff --git a/Content/Python/basic_scripting_demo/3.py b/archive/legacy_python/basic_scripting_demo/3.py old mode 100755 new mode 100644 similarity index 87% rename from Content/Python/basic_scripting_demo/3.py rename to archive/legacy_python/basic_scripting_demo/3.py index 6c6bb18..8c33430 --- a/Content/Python/basic_scripting_demo/3.py +++ b/archive/legacy_python/basic_scripting_demo/3.py @@ -1,19 +1,20 @@ -import unreal - -OLD = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree04.SM_tree04") -NEW = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree01.SM_tree01") - -sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) -actors = sub.get_all_level_actors() - -old_mesh_count = 0 - -with unreal.ScopedEditorTransaction("Swap meshes"): - for actor in actors: - comp = actor.get_component_by_class(unreal.StaticMeshComponent) - if comp and comp.static_mesh == OLD: - comp.set_editor_property("static_mesh", NEW) - # comp.set_static_mesh(NEW) -- tak tez mozna ale undo nie zadziala bo zmiana nie jest robiona przez edytor wiec jej on nie rejestruje - old_mesh_count += 1 - +import unreal + +OLD = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree04.SM_tree04") +NEW = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree01.SM_tree01") + +sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +actors = sub.get_all_level_actors() + +old_mesh_count = 0 + +with unreal.ScopedEditorTransaction("Swap meshes"): + for actor in actors: + comp = actor.get_component_by_class(unreal.StaticMeshComponent) + if comp and comp.static_mesh == OLD: + comp.set_editor_property("static_mesh", NEW) + # comp.set_static_mesh(NEW) -- tak tez mozna ale undo nie zadziala + # bo zmiana nie jest robiona przez edytor wiec jej on nie rejestruje + old_mesh_count += 1 + unreal.log(f"Swapped {old_mesh_count} meshes") \ No newline at end of file diff --git a/Content/Python/generate_blueprint_turntable.py b/archive/legacy_python/generate_blueprint_turntable.py similarity index 100% rename from Content/Python/generate_blueprint_turntable.py rename to archive/legacy_python/generate_blueprint_turntable.py diff --git a/Content/Python/generate_procedural_terrain.py b/archive/legacy_python/generate_procedural_terrain.py similarity index 100% rename from Content/Python/generate_procedural_terrain.py rename to archive/legacy_python/generate_procedural_terrain.py diff --git a/Content/Python/generate_terrain_from_heightmap.py b/archive/legacy_python/generate_terrain_from_heightmap.py similarity index 100% rename from Content/Python/generate_terrain_from_heightmap.py rename to archive/legacy_python/generate_terrain_from_heightmap.py diff --git a/Content/Python/heightmap_config.json b/archive/legacy_python/heightmap_config.json similarity index 100% rename from Content/Python/heightmap_config.json rename to archive/legacy_python/heightmap_config.json diff --git a/Content/Python/helloworld.py b/archive/legacy_python/helloworld.py similarity index 100% rename from Content/Python/helloworld.py rename to archive/legacy_python/helloworld.py diff --git a/Content/Python/import_environment_assets.py b/archive/legacy_python/import_environment_assets.py similarity index 100% rename from Content/Python/import_environment_assets.py rename to archive/legacy_python/import_environment_assets.py diff --git a/Content/Python/organize_content.py b/archive/legacy_python/organize_content.py similarity index 100% rename from Content/Python/organize_content.py rename to archive/legacy_python/organize_content.py diff --git a/Content/Python/png_reader.py b/archive/legacy_python/png_reader.py similarity index 100% rename from Content/Python/png_reader.py rename to archive/legacy_python/png_reader.py diff --git a/Content/Python/terrain_common.py b/archive/legacy_python/terrain_common.py similarity index 100% rename from Content/Python/terrain_common.py rename to archive/legacy_python/terrain_common.py diff --git a/Content/Python/terrain_config.json b/archive/legacy_python/terrain_config.json similarity index 100% rename from Content/Python/terrain_config.json rename to archive/legacy_python/terrain_config.json diff --git a/Content/Python/terrain_config_variants_demo.json b/archive/legacy_python/terrain_config_variants_demo.json similarity index 100% rename from Content/Python/terrain_config_variants_demo.json rename to archive/legacy_python/terrain_config_variants_demo.json diff --git a/docs/cheatsheet.md b/docs/cheatsheet.md new file mode 100644 index 0000000..0599b3f --- /dev/null +++ b/docs/cheatsheet.md @@ -0,0 +1,39 @@ +# Ściąga — ue_workshop + +Import: `from ue_workshop import Scene, Sequence, Renderer, camera, lighting, materials, assets, blender, render` + +| Operacja | Przykład | +|---|---| +| Własna mapa | `scene = Scene("MyShot")` | +| Kolor PBR (wartości liniowe 0–1) | `mat = materials.color("Red", (0.8, 0.02, 0.01), roughness=0.4)` | +| Prosta postać | `actor = scene.character(material=mat, head_scale=1.3)` | +| Bryła | `scene.primitive("Sphere", name="Ball", location=(0, 100, 50), material=mat)` | +| Import statycznego modelu | `mesh = assets.import_model("C:/models/model.glb", name="MyModel")` | +| Eksport + import Blendera | `mesh = blender.import_character("C:/models/character.blend")` | +| Model na scenie | `actor = scene.mesh(mesh, name="Hero")` | +| Światła studyjne | `lights = lighting.studio(scene)` | +| Pojedyncze światło | `lighting.area(scene, location=(200, -200, 350), intensity=10000, color=(1, 0.5, 0.2))` | +| Kamera | `cam = camera.create(scene, location=(450, -550, 300), target=(0, 0, 100), focal_length=45)` | +| Zmiana kierunku | `camera.look_at(cam, (0, 0, 150))` | +| Sekwencja | `shot = Sequence(scene, fps=24, frames=48)` | +| Aktywna kamera | `shot.camera(cam)` | +| Klucz pozycji | `shot.transform_key(actor, 0, location=(0, 0, 0))` | +| Klucz rotacji | `shot.transform_key(actor, 47, rotation=(0, 0, 360))` | +| Klucz skali | `shot.transform_key(actor, 24, scale=(1.2, 1.2, 1.2), interpolation="auto")` | +| Zapis + otwarcie Sequencera | `shot.save()` | +| Własny renderer | `renderer = Renderer(width=1920, height=1080, samples=8, warmup=32, mp4=True)` | +| Preset z własnymi wartościami | `renderer = Renderer.from_preset("final", samples=8)` | +| Render z własnymi ustawieniami | `renderer.render(shot)` | +| Podgląd tym samym rendererem | `renderer.render(shot, single_frame=24)` | +| PNG + MP4 | `render.start(shot, preset="preview")` | +| Tylko PNG | `render.start(shot, mp4=False)` | +| Jedna klatka | `render.start(shot, single_frame=24)` | + +UE: centymetry, Z w górę. Rotacja w kluczach: **roll, pitch, yaw**, stopnie. +Klatki: **0..frames-1**, koniec zakresu wyłączny. Interpolacja: `linear`, `auto`, `constant`. +Cały obrót: klucze 0 i 360, nie 0 i 0. Kamera i modele to oryginalne obiekty UE: +możesz wywoływać ich metody, np. `actor.set_actor_label("MyHero")`. +`shot.asset` to oryginalny LevelSequence, `shot.bind(actor)` zwraca binding UE. + +Przy pojedynczym podglądzie nie uruchamiaj dodatkowo pełnego renderu: +jednocześnie może pracować tylko jeden renderer warsztatowy. diff --git a/docs/validation.md b/docs/validation.md new file mode 100644 index 0000000..540b4da --- /dev/null +++ b/docs/validation.md @@ -0,0 +1,33 @@ +# Weryfikacja v1 — 31 października 2026 + +Sprawdzone lokalnie: Windows, Unreal Engine 5.7, Blender 5.1.2 i FFmpeg. +Nie uruchamiano testów na Linuksie ani na Blenderze 4.x. + +- Testy poza UE: 7 zaliczonych, w tym rzeczywiste kodowanie dwóch PNG do MP4, + odczyt częściowo zapisanych komunikatów i raportowanie liczby klatek. +- Postęp headless: sprawdzone etapy od startu UE do jego zamknięcia, + licznik zapisanych PNG i cichy zapis pełnych logów do pliku. +- Obiekt `Renderer`: własne 320×180, dwie próbki, rozgrzewka 0; sprawdzone + właściwości MRQ i rzeczywiste wymiary PNG. Walidacja błędnych argumentów + i nadpisywanie presetu. Launcher używa obiektu ze skryptu: sprawdzone 400×224. +- Dwukrotne wykonanie ćwiczenia w UE: bez dodatkowych aktorów, bindingów i Camera Cuts. +- Render MRQ w procesie edytora uruchomionym bez okna: 48 PNG oraz MP4, 24 FPS. +- `python exercises/my_shot_headless.py`: pełne ćwiczenie 1920×1080, 4 próbki, + rozgrzewka 32, 48 PNG i MP4. UE zamknął się automatycznie; oba kody zakończenia 0. +- Podgląd pojedynczej klatki z terminala (`--single-frame 24`): zaliczony. +- Odroczone zamknięcie edytora po renderze: kod zakończenia UE i launchera 0. +- Zwykły `UnrealEditor.exe`: projekt otwiera się bez żądania aktualizacji targetu + po usunięciu pozostałych receiptów i cache kompilacji UE 5.8. +- Wizualnie sprawdzony kadr, ekspozycja i wygenerowana postać. +- Pełny edytor z działającym UI Sequencera: otwarcie sekwencji, ticki UI, + przebudowa sceny i dwa kolejne rendery po 48 klatek do MP4 w jednym procesie. + Renderer startuje przez MoviePipelineQueueSubsystem po powrocie ze skryptu; + Sequencer jest zamykany przed przebudową bindingów oraz rozpoczęciem PIE. +- Blender: eksport zapisanej kolekcji, trzy materiały, aktualizacja istniejącego + StaticMesh pod tą samą ścieżką po zmianie geometrii. +- Zmiana koloru materiału oraz dodanie osadzonej tekstury: sprawdzone wartości + parametrów UE, przypisanie tekstury i render do MP4. + +Uruchamianie sprawdzeń jest opisane w README. Wyniki każdego renderu są zapisane +w jego `result.json`; logi UE znajdują się w `Saved/Logs`. +Skrypty integracyjne generują własne mapy i assety w `Workshop/Generated`. diff --git a/docs/workshop.md b/docs/workshop.md new file mode 100644 index 0000000..063b7b0 --- /dev/null +++ b/docs/workshop.md @@ -0,0 +1,25 @@ +# Spotkanie — 60 minut + +Przed spotkaniem: UE 5.7, pobrany projekt, skompilowane shadery, sprawdzony render. +FFmpeg potrzebny do MP4, Blender opcjonalnie. Przygotuj gotowy film na wypadek +wolniejszego sprzętu. Nie poświęcaj części praktycznej na instalowanie narzędzi. + +| Minuty | Działanie | +|---|---| +| 0–15 | Python w edytorze, wykonanie pliku, aktor i asset, pokaz Blender → film. | +| 15–23 | `my_shot.py`: model, scena, klucze, Camera Cuts, render. Pokaż wynik w UI. | +| 23–53 | Własna kopia ćwiczenia, zmiany i krótki render. | +| 53–60 | Wyniki, pytania, pokaz launchera bez okna. | + +Propozycje zadań, bez obowiązku wykonania wszystkich: + +1. Zmień kolory postaci i oświetlenia, powiększ głowę. +2. Zmień pozycję i ogniskową kamery. Zrób pojedynczy podgląd. +3. Dodaj klucz w środku sekwencji: podskok albo zmianę skali. +4. Animuj kamerę — przykład w `examples/animated_camera.py`. +5. Opcjonalnie: zmień model w Blenderze i wykonaj `examples/blender_character.py`. + +Punkt ratunkowy: świeża kopia `exercises/my_shot.py` z Git. Do pierwszych prób +zostaw `RENDER=False` i oglądaj Sequencera. Do renderu włącz `RENDER=True`. +Przy powrocie do skryptu pamiętaj wyłączyć drugi render, jeżeli dodajesz wywołanie +`render.start` ręcznie. Pokaż gdzie są PNG oraz `result.json`. diff --git a/examples/animated_camera.py b/examples/animated_camera.py new file mode 100644 index 0000000..2b79ec5 --- /dev/null +++ b/examples/animated_camera.py @@ -0,0 +1,13 @@ +"""Wariant: nieruchoma postac i animowana kamera.""" +from ue_workshop import Scene, Sequence, camera, lighting, materials + +scene = Scene("CameraShot") +scene.character(material=materials.color("Orange", (0.8, 0.16, 0.03))) +scene.primitive("Cube", name="Floor", location=(0, 0, -10), scale=(20, 20, 0.2)) +lighting.studio(scene) +cam = camera.create(scene, location=(450, -550, 300)) +shot = Sequence(scene) +shot.camera(cam) +shot.transform_key(cam, 0, location=(450, -550, 300), interpolation="auto") +shot.transform_key(cam, 47, location=(280, -340, 230), interpolation="auto") +shot.save() diff --git a/examples/blender_character.py b/examples/blender_character.py new file mode 100644 index 0000000..307b6a1 --- /dev/null +++ b/examples/blender_character.py @@ -0,0 +1,18 @@ +"""Najpierw wygeneruj source_assets/character/character.blend; instrukcja w README.""" +from ue_workshop import Scene, Sequence, Renderer, blender, camera, lighting, materials +from ue_workshop.paths import PROJECT + +mesh = blender.import_character(PROJECT / "source_assets/character/character.blend") +scene = Scene("BlenderShot") +character = scene.mesh(mesh, name="Character") +floor = materials.color("Floor", rgb=(0.07, 0.08, 0.11)) +scene.primitive("Cube", name="Floor", location=(0, 0, -10), scale=(20, 20, 0.2), material=floor) +lighting.studio(scene) +cam = camera.create(scene) +shot = Sequence(scene, frames=48) +shot.camera(cam) +shot.transform_key(character, 0, rotation=(0, 0, 0)) +shot.transform_key(character, 47, rotation=(0, 0, 360)) +shot.save() +renderer = Renderer(width=640, height=360, samples=1, warmup=8) +renderer.render(shot) diff --git a/exercises/my_shot.py b/exercises/my_shot.py new file mode 100644 index 0000000..41e4c53 --- /dev/null +++ b/exercises/my_shot.py @@ -0,0 +1,45 @@ +"""Uruchom plik w UE: File > Execute Python Script. Zmieniaj i uruchamiaj ponownie.""" +import importlib +import sys + +# Ponowny import bibliotek (ue cachuje zaimportowaną biblioteke i jeśli nie bedzie +# jawnego reloadu/restartu edytora to będzie wykonywał stary kod) +if "ue_workshop" in sys.modules: + _render_module = sys.modules.get("ue_workshop.render") + if _render_module is not None and _render_module.is_rendering(): + raise RuntimeError("Poczekaj na zakonczenie renderu przed przeladowaniem biblioteki.") + # Najpierw zaleznosci, potem moduly, ktore z nich korzystaja. + for _name in ("paths", "progress", "video", "materials", "camera", "lighting", "assets", + "_gltf", "blender", "scene", "sequence", "render"): + _module = sys.modules.get("ue_workshop." + _name) + if _module is not None: + importlib.reload(_module) + importlib.reload(sys.modules["ue_workshop"]) + +from ue_workshop import Scene, Sequence, Renderer, camera, lighting, materials + +# 1. MODEL I SCENA. Jednostki: centymetry. Os Z wskazuje gore. +scene = Scene("MyShot") +blue = materials.color("Character", rgb=(0.04, 0.35, 0.8), metallic=0.2) +floor = materials.color("Floor", rgb=(0.07, 0.08, 0.11)) +character = scene.character(material=blue, head_scale=1.0) +scene.primitive("Cube", name="Floor", location=(0, 0, -10), scale=(20, 20, 0.2), material=floor) +lighting.studio(scene, key_color=(1.0, 0.75, 0.5), fill_color=(0.3, 0.6, 1.0)) +cam = camera.create(scene, location=(450, -550, 300), focal_length=45) + +# 2. ANIMACJA. 48 klatek przy 24 FPS = 2 sekundy. Ostatnia klatka ma numer 47. +shot = Sequence(scene, fps=24, frames=48) +shot.camera(cam) +# Rotacja: (roll, pitch, yaw), w stopniach. +shot.transform_key(character, 0, rotation=(0, 0, 0)) +shot.transform_key(character, 47, rotation=(0, 0, 360)) +shot.save() + +# 3. WYNIK. Najpierw obejrzyj animacje w Sequencerze, potem zmien RENDER na True. +RENDER = True +renderer = Renderer(width=1920, height=1080, samples=4, warmup=32, mp4=True) +# Mozesz tez zaczac od presetu i nadpisac wybrane wartosci: +# renderer = Renderer.from_preset("final", width=1920, height=1080) +if RENDER: + renderer.render(shot) + # Szybki podglad zamiast filmu: renderer.render(shot, single_frame=24) diff --git a/exercises/my_shot_headless.py b/exercises/my_shot_headless.py new file mode 100644 index 0000000..13dd8c2 --- /dev/null +++ b/exercises/my_shot_headless.py @@ -0,0 +1,25 @@ +"""Uruchom zwyklym Pythonem: python exercises/my_shot_headless.py""" +from pathlib import Path +import sys + +PROJECT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(PROJECT / "tools")) + +from run_workshop import main as run_workshop + + +def main(): + if "unreal" in sys.modules: + raise RuntimeError("Ten plik uruchom z terminala zwyklym Pythonem, poza edytorem UE.") + # --render zapewnia film takze wtedy, gdy w my_shot.py jest RENDER=False. + # Argumenty launchera, np. --editor i --timeout, mozna dopisac w terminalu. + script = Path(__file__).with_name("my_shot.py") + return run_workshop([str(script), "--render", *sys.argv[1:]]) + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, ValueError) as exc: + print(str(exc), file=sys.stderr) + raise SystemExit(1) diff --git a/open_in_unreal.sh b/open_in_unreal.sh index 7bd23a0..1837fb0 100755 --- a/open_in_unreal.sh +++ b/open_in_unreal.sh @@ -46,7 +46,6 @@ find_editor() { fi for candidate in \ - "$HOME/Unreal/UE_5.8/Engine/Binaries/Linux/UnrealEditor" \ "$HOME/Unreal/UE_5.7/Engine/Binaries/Linux/UnrealEditor" \ "$HOME/UnrealEngine/Engine/Binaries/Linux/UnrealEditor" \ /opt/UnrealEngine/Engine/Binaries/Linux/UnrealEditor \ diff --git a/source_assets/character/character.blend b/source_assets/character/character.blend new file mode 100644 index 0000000..6c60c6c Binary files /dev/null and b/source_assets/character/character.blend differ diff --git a/tests/test_offline.py b/tests/test_offline.py new file mode 100644 index 0000000..96db1d1 --- /dev/null +++ b/tests/test_offline.py @@ -0,0 +1,59 @@ +"""Plain Python checks, including real FFmpeg encoding when installed.""" +import binascii +import os +from pathlib import Path +import shutil +import struct +import sys +import tempfile +import unittest +import zlib + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "Content/Python")) +from ue_workshop.paths import slug +from ue_workshop.video import encode + + +def png(path, rgb): + def chunk(kind, data): + return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", binascii.crc32(kind + data) & 0xffffffff) + path.write_bytes(b"\x89PNG\r\n\x1a\n" + + chunk(b"IHDR", struct.pack(">IIBBBBB", 4, 4, 8, 2, 0, 0, 0)) + + chunk(b"IDAT", zlib.compress((b"\x00" + bytes(rgb) * 4) * 4)) + chunk(b"IEND", b"")) + + +class OfflineTests(unittest.TestCase): + def test_generated_asset_name_cannot_escape_folder(self): + for value in ["../Outside", "A/B", "", "A.B", "0Shot", "A B"]: + with self.subTest(value=value), self.assertRaises(ValueError): + slug(value) + self.assertEqual(slug("MyShot_2"), "MyShot_2") + + def test_missing_frames_are_rejected_before_encoding(self): + with tempfile.TemporaryDirectory() as directory: + folder = Path(directory) + png(folder / "frame_0001.png", (255, 0, 0)) + with self.assertRaisesRegex(RuntimeError, "numeracji"): + encode(folder, 24) + + def test_incomplete_render_is_not_encoded(self): + with tempfile.TemporaryDirectory() as directory: + folder = Path(directory) + png(folder / "frame_0000.png", (255, 0, 0)) + with self.assertRaisesRegex(RuntimeError, "zamiast 2"): + encode(folder, 24, expected_frames=2) + + @unittest.skipUnless(shutil.which("ffmpeg") or os.environ.get("WORKSHOP_FFMPEG"), "FFmpeg missing") + def test_real_mp4_encoding(self): + with tempfile.TemporaryDirectory() as directory: + folder = Path(directory) / "frames" + folder.mkdir() + png(folder / "frame_0000.png", (255, 0, 0)) + png(folder / "frame_0001.png", (0, 0, 255)) + movie = encode(folder, 24, expected_frames=2) + self.assertGreater(movie.stat().st_size, 100) + self.assertIn(b"ftyp", movie.read_bytes()[:32]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_progress.py b/tests/test_progress.py new file mode 100644 index 0000000..fd313b8 --- /dev/null +++ b/tests/test_progress.py @@ -0,0 +1,60 @@ +import json +from pathlib import Path +import sys +import tempfile +import unittest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools")) +from progress_display import ProgressDisplay + + +class ProgressTests(unittest.TestCase): + def display(self, path): + self.now = 0 + self.messages = [] + return ProgressDisplay(path, clock=lambda: self.now, + emit=lambda text, **kwargs: self.messages.append(text)) + + def test_partial_events_are_read_once_after_they_finish(self): + with tempfile.TemporaryDirectory() as folder: + path = Path(folder) / "progress.jsonl" + display = self.display(path) + path.write_bytes(b'{"state":"prepar') + display.update() + self.assertEqual(len(self.messages), 1) + with path.open("ab") as stream: + stream.write(b'ing"}\n') + display.update() + display.update() + self.assertEqual(len(self.messages), 2) + self.assertIn("Przygotowywanie sceny", self.messages[-1]) + + def test_waiting_heartbeat_is_throttled(self): + with tempfile.TemporaryDirectory() as folder: + display = self.display(Path(folder) / "progress.jsonl") + self.now = 9 + display.update() + self.assertEqual(len(self.messages), 1) + self.now = 10 + display.update() + display.update() + self.assertEqual(len(self.messages), 2) + self.assertIn("Nadal pracuje", self.messages[-1]) + + def test_frame_progress_counts_actual_output_files(self): + with tempfile.TemporaryDirectory() as folder: + root = Path(folder) + (root / "frames").mkdir() + (root / "frames/frame_0000.png").touch() + (root / "frames/frame_0001.png").touch() + path = root / "progress.jsonl" + path.write_text(json.dumps(dict(state="rendering", output=folder, frames=4)) + "\n") + display = self.display(path) + display.update() + self.now = 2 + display.update() + self.assertIn("2/4 klatek", self.messages[-1]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/blender_export.py b/tools/blender_export.py new file mode 100644 index 0000000..5aa8383 --- /dev/null +++ b/tools/blender_export.py @@ -0,0 +1,46 @@ +"""Executed by Blender, never by Unreal. Export an evaluated, joined static mesh.""" +import argparse +from pathlib import Path +import sys + +import bpy + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--output", required=True) + parser.add_argument("--collection", default="Character") + args = parser.parse_args(sys.argv[sys.argv.index("--") + 1:]) + collection = bpy.data.collections.get(args.collection) + if collection is None: + raise RuntimeError(f"Brak kolekcji {args.collection!r} w zapisanym pliku blend.") + meshes = [obj for obj in collection.all_objects if obj.type == "MESH"] + if not meshes: + raise RuntimeError("Kolekcja Character nie zawiera siatek.") + bpy.ops.object.select_all(action="DESELECT") + depsgraph = bpy.context.evaluated_depsgraph_get() + copies = [] + for obj in meshes: + evaluated = obj.evaluated_get(depsgraph) + data = bpy.data.meshes.new_from_object(evaluated, depsgraph=depsgraph) + duplicate = bpy.data.objects.new(obj.name + "_export", data) + bpy.context.scene.collection.objects.link(duplicate) + duplicate.matrix_world = obj.matrix_world.copy() + duplicate.select_set(True) + copies.append(duplicate) + bpy.context.view_layer.objects.active = copies[0] + bpy.ops.object.join() + joined = bpy.context.object + joined.name = "Character" + # Origin at world zero: stable pivot, independent of which body part is first. + bpy.context.scene.cursor.location = (0, 0, 0) + bpy.ops.object.origin_set(type="ORIGIN_CURSOR") + output = Path(args.output).resolve() + output.parent.mkdir(parents=True, exist_ok=True) + bpy.ops.export_scene.gltf(filepath=str(output), export_format="GLB", use_selection=True, + export_animations=False, export_materials="EXPORT", export_yup=True) + print("WORKSHOP_EXPORTED=" + str(output)) + + +if __name__ == "__main__": + main() diff --git a/tools/console_links.py b/tools/console_links.py new file mode 100644 index 0000000..c366c13 --- /dev/null +++ b/tools/console_links.py @@ -0,0 +1,22 @@ +"""OSC 8 file links, with plain paths for unsupported or redirected consoles.""" +import os +from pathlib import Path +import sys + + +def file_link(path, mode="auto", stream=None): + target = Path(path).resolve() + stream = sys.stdout if stream is None else stream + supported = ( + stream.isatty() + and os.environ.get("TERM") != "dumb" + and ( + os.environ.get("WT_SESSION") + or os.environ.get("TERM_PROGRAM") in {"vscode", "iTerm.app", "WezTerm", "ghostty"} + or os.environ.get("VTE_VERSION") + or os.environ.get("KONSOLE_VERSION") + ) + ) + if mode == "always" or (mode == "auto" and supported): + return f"\033]8;;{target.as_uri()}\033\\{target}\033]8;;\033\\" + return str(target) diff --git a/tools/create_character.py b/tools/create_character.py new file mode 100644 index 0000000..ef06991 --- /dev/null +++ b/tools/create_character.py @@ -0,0 +1,58 @@ +"""Blender --background --python tools/create_character.py (does not need UE).""" +from pathlib import Path + +import bpy + +root = Path(__file__).resolve().parents[1] +target = root / "source_assets/character/character.blend" +if target.exists(): + raise RuntimeError(f"Plik juz istnieje: {target}. Generator nie nadpisuje Twojego modelu.") +bpy.ops.object.select_all(action="SELECT") +bpy.ops.object.delete(use_global=False) +collection = bpy.data.collections.new("Character") +bpy.context.scene.collection.children.link(collection) + + +def material(name, color): + mat = bpy.data.materials.new(name) + mat.use_nodes = True + bsdf = mat.node_tree.nodes.get("Principled BSDF") + bsdf.inputs["Base Color"].default_value = (*color, 1) + bsdf.inputs["Roughness"].default_value = 0.5 + mat.diffuse_color = (*color, 1) + return mat + + +blue = material("Body_Blue", (0.03, 0.3, 0.8)) +orange = material("Head_Orange", (0.9, 0.22, 0.04)) +dark = material("Eyes", (0.01, 0.01, 0.015)) + + +def part(name, position, scale, mat, sphere=False): + if sphere: + bpy.ops.mesh.primitive_uv_sphere_add(segments=24, ring_count=16, radius=0.5, location=position) + else: + bpy.ops.mesh.primitive_cube_add(size=1, location=position) + obj = bpy.context.object + obj.name = name + obj.scale = scale + for owner in list(obj.users_collection): + owner.objects.unlink(obj) + collection.objects.link(obj) + obj.data.materials.append(mat) + if sphere: + for polygon in obj.data.polygons: + polygon.use_smooth = True + return obj + + +part("Body", (0, 0, 0.9), (0.55, 0.8, 0.9), blue) +part("Head", (0, 0, 1.75), (0.7, 0.7, 0.7), orange, True) +part("LeftLeg", (0, -0.23, 0.25), (0.35, 0.3, 0.5), blue) +part("RightLeg", (0, 0.23, 0.25), (0.35, 0.3, 0.5), blue) +part("Eye_L", (0.31, -0.14, 1.82), (0.12, 0.12, 0.12), dark, True) +part("Eye_R", (0.31, 0.14, 1.82), (0.12, 0.12, 0.12), dark, True) +bpy.context.scene.unit_settings.system = "METRIC" +target.parent.mkdir(parents=True, exist_ok=True) +bpy.ops.wm.save_as_mainfile(filepath=str(target)) +print("Gotowy model: " + str(target)) diff --git a/tools/progress_display.py b/tools/progress_display.py new file mode 100644 index 0000000..1955abb --- /dev/null +++ b/tools/progress_display.py @@ -0,0 +1,63 @@ +"""Concise console progress; does not depend on Unreal or its log format.""" +import json +from pathlib import Path +import time + + +class ProgressDisplay: + LABELS = { + "starting": "Uruchamianie UE...", + "preparing": "Przygotowywanie sceny i sekwencji...", + "preparing_render": "Przygotowywanie renderu...", + "rendering": "Renderowanie...", + "encoding": "Render zakonczony. Skladanie MP4...", + "closing": "Zamykanie UE...", + } + + def __init__(self, events, clock=time.monotonic, emit=print): + self.events = Path(events) + self.clock, self.emit = clock, emit + self.started = self.last_print = clock() + self.position = 0 + self.pending = b"" + self.state = "starting" + self.details = {} + self.last_count = -1 + self.show(self.LABELS[self.state]) + + def show(self, message): + seconds = int(self.clock() - self.started) + self.emit(f"[{seconds // 60:02d}:{seconds % 60:02d}] {message}", flush=True) + self.last_print = self.clock() + + def update(self): + try: + with self.events.open("rb") as stream: + stream.seek(self.position) + self.pending += stream.read() + self.position = stream.tell() + except FileNotFoundError: + pass + while b"\n" in self.pending: + line, self.pending = self.pending.split(b"\n", 1) + try: + event = json.loads(line) + except (ValueError, UnicodeError): + continue + state = event.get("state") + if state in self.LABELS: + self.state, self.details = state, event + self.last_count = -1 + self.show(self.LABELS[state]) + elapsed = self.clock() - self.last_print + if self.state == "rendering" and elapsed >= 2: + output = self.details.get("output") + total = self.details.get("frames") + if output and total: + count = len(list((Path(output) / "frames").glob("frame_*.png"))) + if count != self.last_count and count > 0: + self.last_count = count + self.show(f"Renderowanie: {count}/{total} klatek zapisanych.") + return + if elapsed >= 10: + self.show(self.LABELS[self.state] + " Nadal pracuje.") diff --git a/tools/run_workshop.py b/tools/run_workshop.py new file mode 100644 index 0000000..35b5b0f --- /dev/null +++ b/tools/run_workshop.py @@ -0,0 +1,127 @@ +"""Plain Python launcher. Does not import unreal; works on Windows and Linux.""" +import argparse +from contextlib import nullcontext +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import time +import uuid + +from progress_display import ProgressDisplay +from console_links import file_link + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "Content/Python")) +from ue_workshop.paths import executable + + +def editor_path(explicit=None): + value = explicit or os.environ.get("WORKSHOP_UNREAL") or shutil.which("UnrealEditor") + if not value and os.name == "nt": + value = r"C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" + if value and Path(value).is_file(): + return str(Path(value).resolve()) + raise FileNotFoundError("Ustaw WORKSHOP_UNREAL na plik UnrealEditor-Cmd.exe / UnrealEditor (UE 5.7).") + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("script", nargs="?", default="exercises/my_shot.py") + parser.add_argument("--editor", help="Pelna sciezka do UE 5.7") + parser.add_argument("--check", action="store_true", help="Sprawdz srodowisko bez otwierania UE") + parser.add_argument("--blender", action="store_true", help="Przy --check wymagaj tez Blendera") + parser.add_argument("--timeout", type=int, default=900) + parser.add_argument("--render", action="store_true", help="Renderuj zmienna shot ze skryptu, nawet gdy RENDER=False") + parser.add_argument("--single-frame", type=int, help="Renderuj tylko wskazana klatke (bez MP4)") + parser.add_argument("--verbose", action="store_true", help="Pokazuj pelne logi UE w konsoli") + parser.add_argument("--links", choices=("auto", "always", "never"), default="auto", + help="Klikalne sciezki w terminalu (domyslnie: auto)") + args = parser.parse_args(argv) + + def link(path, stream=None): + return file_link(path, args.links, stream) + + editor = editor_path(args.editor) + if args.check: + print("UE:", editor) + print("FFmpeg:", executable("ffmpeg")) + if args.blender: + print("Blender:", executable("blender")) + print("Projekt:", ROOT / "ExampleProject.uproject") + return 0 + script = (ROOT / args.script).resolve() + if not script.is_file(): + raise FileNotFoundError(script) + status = ROOT / "Saved/Workshop" / (uuid.uuid4().hex + ".json") + log_path = status.with_suffix(".log") + progress_path = status.with_suffix(".progress.jsonl") + status.parent.mkdir(parents=True, exist_ok=True) + env = dict(os.environ, WORKSHOP_SCRIPT=str(script), WORKSHOP_STATUS=str(status), WORKSHOP_BATCH="1") + env["WORKSHOP_PROGRESS"] = str(progress_path) + env["WORKSHOP_RENDER"] = "1" if args.render else "0" + env["WORKSHOP_SINGLE_FRAME"] = "" if args.single_frame is None else str(args.single_frame) + command = [editor, str(ROOT / "ExampleProject.uproject"), + "-ExecutePythonScript=" + (ROOT / "tools/ue_bootstrap.py").as_posix(), + "-RenderOffscreen", "-unattended", "-nosplash", "-nosound", "-stdout", + "-FullStdOutLogOutput", "-NoLoadingScreen"] + if args.verbose: + # UE writes its own log when console output is streamed directly. + command.append("-abslog=" + log_path.as_posix()) + print("Uruchamiam:", script, flush=True) + progress = ProgressDisplay(progress_path) + try: + # Keep native stderr (including third-party plugins) out of the terminal too. + with nullcontext(None) if args.verbose else log_path.open("wb") as output: + with subprocess.Popen(command, env=env, cwd=ROOT, stdout=output, + stderr=subprocess.STDOUT) as process: + deadline = time.monotonic() + args.timeout + try: + while True: + progress.update() + remaining = deadline - time.monotonic() + if remaining <= 0: + raise subprocess.TimeoutExpired(command, args.timeout) + try: + exit_code = process.wait(timeout=min(0.5, remaining)) + except subprocess.TimeoutExpired: + continue + progress.update() + break + finally: + if process.poll() is None: + process.kill() + process.wait() + except subprocess.TimeoutExpired: + print(f"Przekroczono limit czasu UE. Log: {link(log_path, sys.stderr)}", file=sys.stderr) + return 1 + if not status.is_file(): + print(f"UE nie zapisalo wyniku (kod {exit_code}). Log: {link(log_path, sys.stderr)}", file=sys.stderr) + return 1 + try: + result = json.loads(status.read_text(encoding="utf-8")) + except (OSError, ValueError) as exc: + print(f"Nie mozna odczytac wyniku: {exc}. Log: {link(log_path, sys.stderr)}", file=sys.stderr) + return 1 + result["editor_exit_code"] = exit_code + result["log"] = str(log_path) + status.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") + success = exit_code == 0 and result.get("success") + if success: + print("Gotowe:", link(result.get("video") or result.get("output") or script)) + else: + print("Blad:", result.get("error") or f"UE zakonczyl prace z kodem {exit_code}.", file=sys.stderr) + print("Log:", link(log_path)) + if args.verbose: + print(json.dumps(result, ensure_ascii=False, indent=2)) + return 0 if success else 1 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, ValueError) as exc: + print(str(exc), file=sys.stderr) + raise SystemExit(1) diff --git a/tools/smoke_blender.py b/tools/smoke_blender.py new file mode 100644 index 0000000..0c3e0c4 --- /dev/null +++ b/tools/smoke_blender.py @@ -0,0 +1,69 @@ +"""Integration check: export/import, changed geometry/material, stable asset paths, MRQ.""" +import os +from pathlib import Path +import subprocess + +import unreal + +from ue_workshop import Scene, Sequence, blender, camera, lighting, render +from ue_workshop.paths import PROJECT, executable + +source = PROJECT / "source_assets/character/character.blend" +if not source.is_file(): + raise RuntimeError("Wygeneruj przykladowa postac: blender --background --python tools/create_character.py") +mesh = blender.import_character(source, name="SmokeCharacterV1") +asset_path = mesh.get_path_name() +old_bounds = mesh.get_bounds().box_extent.z +old_materials = {str(item.material_slot_name): item.material_interface.get_path_name() + for item in mesh.static_materials} +assert len(old_materials) == 3, old_materials +assert all(old_materials.values()) + +# Change a COPY of the source, never the student's .blend. +variant = PROJECT / "build/smoke/character_variant.blend" +variant.parent.mkdir(parents=True, exist_ok=True) +code = ("import bpy; bpy.data.objects['Head'].scale *= 2; " + "bpy.data.materials['Head_Orange'].node_tree.nodes.get('Principled BSDF')." + "inputs['Base Color'].default_value = (0.02, 0.8, 0.05, 1); " + "image = bpy.data.images.new('BodyPattern', width=2, height=2); " + "image.pixels = [0.1, 0.3, 0.8, 1] * 4; image.pack(); " + "tree = bpy.data.materials['Body_Blue'].node_tree; " + "node = tree.nodes.new('ShaderNodeTexImage'); node.image = image; " + "tree.links.new(node.outputs['Color'], tree.nodes.get('Principled BSDF').inputs['Base Color']); " + f"bpy.ops.wm.save_as_mainfile(filepath={str(variant)!r})") +completed = subprocess.run([executable("blender"), "--background", str(source), + "--python-exit-code", "1", "--python-expr", code], + capture_output=True, text=True, timeout=120) +if completed.returncode: + raise RuntimeError((completed.stdout + completed.stderr)[-4000:]) +updated = blender.import_character(variant, name="SmokeCharacterV1") +assert updated.get_path_name() == asset_path, "Import created a different mesh asset" +assert updated.get_bounds().box_extent.z > old_bounds + 5, "Geometry was not updated" +new_materials = {str(item.material_slot_name): item.material_interface.get_path_name() + for item in updated.static_materials} +assert new_materials == old_materials, "Material identities changed" +assert all(unreal.load_asset(path) for path in new_materials.values()) +green_found = False +texture_found = False +for path in new_materials.values(): + material = unreal.load_asset(path) + for parameter in unreal.MaterialEditingLibrary.get_vector_parameter_names(material): + color = unreal.MaterialEditingLibrary.get_material_instance_vector_parameter_value(material, parameter) + if color.g > 0.7 and color.r < 0.1 and color.b < 0.1: + green_found = True + for parameter in unreal.MaterialEditingLibrary.get_texture_parameter_names(material): + texture = unreal.MaterialEditingLibrary.get_material_instance_texture_parameter_value(material, parameter) + if texture and texture.get_path_name().startswith("/Game/Workshop/Generated/Imports/SmokeCharacterV1/"): + texture_found = True +assert green_found, "Updated green material was not imported" +assert texture_found, "Embedded Blender texture was not imported and assigned" + +scene = Scene("BlenderSmoke") +actor = scene.mesh(updated, name="UpdatedCharacter") +lighting.studio(scene) +cam = camera.create(scene) +shot = Sequence(scene, frames=2) +shot.camera(cam) +shot.transform_key(actor, 0, rotation=(0, 0, 0)) +shot.transform_key(actor, 1, rotation=(0, 0, 30)) +render.start(shot) diff --git a/tools/smoke_editor_render.py b/tools/smoke_editor_render.py new file mode 100644 index 0000000..ad3373e --- /dev/null +++ b/tools/smoke_editor_render.py @@ -0,0 +1,62 @@ +"""Exercise real Sequencer UI ticks, rebuild, render, then render again in one editor.""" +import json +import os +import traceback + +import unreal + +from ue_workshop import render +from ue_workshop.paths import PROJECT + +unreal.EditorPythonScripting.set_keep_python_script_alive(True) +# The test owns shutdown; allow multiple renders in the same editor process. +os.environ["WORKSHOP_BATCH"] = "0" +code = (PROJECT / "exercises/my_shot.py").read_text(encoding="utf-8") +code = code.replace("RENDER = True", "RENDER = False") +state = {"stage": 0, "ticks": 0, "outputs": []} + + +def build(): + namespace = {"__name__": "__main__"} + exec(compile(code, "my_shot.py", "exec"), namespace) + return namespace + + +namespace = build() + + +def tick(delta): + global namespace + state["ticks"] += 1 + try: + if state["stage"] in {0, 2} and state["ticks"] >= 30: + # The previous sequence editor has ticked before its actors and + # bindings are replaced, matching repeated Execute Python Script. + next_stage = state["stage"] + 1 + # Asset saves can pump Slate recursively. Do not re-enter the test. + state["stage"] = -1 + namespace = build() + output = render.start(namespace["shot"], mp4=True) + state["outputs"].append(output) + state["stage"] = next_stage + elif state["stage"] in {1, 3} and not render.is_rendering(): + result = json.loads((state["outputs"][-1] / "result.json").read_text()) + assert result["success"], result + if state["stage"] == 3: + finish({"success": True, "renders": [str(path) for path in state["outputs"]]}) + else: + unreal.LevelSequenceEditorBlueprintLibrary.open_level_sequence(namespace["shot"].asset) + state["stage"] = 2 + state["ticks"] = 0 + except Exception: + finish({"success": False, "error": traceback.format_exc()}) + + +def finish(result): + unreal.unregister_slate_post_tick_callback(handle) + render._write_status(result) + unreal.log("[Workshop UI test] " + str(result)) + unreal.EditorPythonScripting.set_keep_python_script_alive(False) + + +handle = unreal.register_slate_post_tick_callback(tick) diff --git a/tools/smoke_renderer.py b/tools/smoke_renderer.py new file mode 100644 index 0000000..6495810 --- /dev/null +++ b/tools/smoke_renderer.py @@ -0,0 +1,41 @@ +"""Verify custom settings, preset overrides and PNG dimensions inside UE.""" +import struct + +import unreal + +from ue_workshop import Renderer, render +from ue_workshop.paths import PROJECT + +for kwargs in ({"width": 0}, {"height": -1}, {"samples": 0}, {"warmup": -1}, {"width": 1.5}, {"samples": True}): + try: + Renderer(**kwargs) + except ValueError: + pass + else: + raise AssertionError(f"Accepted invalid settings: {kwargs}") +preset = Renderer.from_preset("final", width=800) +assert (preset.width, preset.height, preset.samples, preset.warmup) == (800, 720, 4, 32) +code = (PROJECT / "exercises/my_shot.py").read_text(encoding="utf-8") +code = code.replace('Scene("MyShot")', 'Scene("RendererSmoke")') +code = code.replace('materials.color("Character"', 'materials.color("RendererSmokeCharacter"') +code = code.replace('materials.color("Floor"', 'materials.color("RendererSmokeFloor"') +namespace = {"__name__": "__main__"} +exec(compile(code.replace("RENDER = True", "RENDER = False"), "my_shot.py", "exec"), namespace) +renderer = Renderer(width=320, height=180, samples=2, warmup=0) +output = renderer.render(namespace["shot"], single_frame=24) +config = render._active["queue"].get_jobs()[0].get_configuration() +aa = config.find_setting_by_class(unreal.MoviePipelineAntiAliasingSetting) +assert aa.spatial_sample_count == 2 +assert aa.engine_warm_up_count == 0 + + +def verify(executor, success): + if not success: + return + frames = list((output / "frames").glob("*.png")) + assert len(frames) == 1 + assert struct.unpack(">II", frames[0].read_bytes()[16:24]) == (320, 180) + unreal.log("[Workshop] Custom renderer: PNG dimensions 320x180 verified.") + + +render._active["executor"].on_executor_finished_delegate.add_callable_unique(verify) diff --git a/tools/smoke_scene.py b/tools/smoke_scene.py new file mode 100644 index 0000000..516ed40 --- /dev/null +++ b/tools/smoke_scene.py @@ -0,0 +1,27 @@ +"""Integration check run inside a full UE editor by run_workshop.py.""" +import unreal + +from ue_workshop import Sequence, render +from ue_workshop.paths import PROJECT + +def build(): + # Respect student edits on disk, but let this test own the render. + code = (PROJECT / "exercises/my_shot.py").read_text(encoding="utf-8") + namespace = {"__name__": "__main__"} + exec(compile(code.replace("RENDER = True", "RENDER = False"), "my_shot.py", "exec"), namespace) + return namespace + + +# Execute twice: no duplicate actors, bindings or transform tracks. +first = build() +actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +count = len(actor_subsystem.get_all_level_actors()) +second = build() +assert count == len(actor_subsystem.get_all_level_actors()), "Duplicate actors after rerun" +assert len(second["shot"].asset.get_bindings()) == 2 +assert len(second["shot"].asset.find_tracks_by_type(unreal.MovieSceneCameraCutTrack)) == 1 +short = Sequence(second["scene"], "Smoke", fps=24, frames=2) +short.camera(second["cam"]) +short.transform_key(second["character"], 0, rotation=(0, 0, 0)) +short.transform_key(second["character"], 1, rotation=(0, 0, 30)) +render.start(short, mp4=True) diff --git a/tools/ue_bootstrap.py b/tools/ue_bootstrap.py new file mode 100644 index 0000000..d95b82b --- /dev/null +++ b/tools/ue_bootstrap.py @@ -0,0 +1,38 @@ +"""Entry point for -ExecutePythonScript; preserve the editor until MRQ finishes.""" +import json +import os +from pathlib import Path +import runpy +import sys +import traceback + +import unreal + +root = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(root / "Content/Python")) +from ue_workshop.progress import report + +try: + unreal.EditorPythonScripting.set_keep_python_script_alive(True) + report("preparing") + namespace = runpy.run_path(os.environ["WORKSHOP_SCRIPT"], run_name="__main__") + from ue_workshop import render + if not render.is_rendering() and (os.environ.get("WORKSHOP_RENDER") == "1" or + os.environ.get("WORKSHOP_SINGLE_FRAME")): + if "shot" not in namespace: + raise RuntimeError("Opcja --render wymaga zmiennej shot w skrypcie.") + frame = os.environ.get("WORKSHOP_SINGLE_FRAME") + renderer = namespace.get("renderer") or render.Renderer() + renderer.render(namespace["shot"], single_frame=int(frame) if frame else None) + if not render.is_rendering(): + render._write_status({"success": True, "state": "script_finished"}) + report("closing") + unreal.EditorPythonScripting.set_keep_python_script_alive(False) +except Exception: + error = traceback.format_exc() + unreal.log_error(error) + status = os.environ.get("WORKSHOP_STATUS") + if status: + Path(status).write_text(json.dumps({"success": False, "error": error}), encoding="utf-8") + report("closing") + unreal.EditorPythonScripting.set_keep_python_script_alive(False)