├── .gitignore ├── Content ├── Blueprints │ └── BP_ImposterManagerActor.uasset ├── MPC │ └── MPC_Capture.uasset ├── Material │ ├── Capture │ │ ├── M_Blit_DFAlpha.uasset │ │ ├── M_Blit_DilationRGB+DFAlpha.uasset │ │ ├── M_Blit_DilationRGBA.uasset │ │ ├── M_Capture_BaseColorAlpha.uasset │ │ ├── M_Capture_MetallicRoughnessAO.uasset │ │ ├── M_Capture_MetallicRoughnessSpecularAO.uasset │ │ ├── M_Capture_NormalDepth.uasset │ │ └── MaterialFunctions │ │ │ ├── MF_DistanceField.uasset │ │ │ └── MF_UVDilation.uasset │ └── Imposter │ │ ├── M_BillboardXYZ.uasset │ │ ├── M_Imposter_Placeholder.uasset │ │ ├── M_Impostor_SimpleOffset.uasset │ │ ├── MaterialFunctions │ │ ├── FrameBlendWeights.uasset │ │ ├── FrameBlendWeights_TextureObject.uasset │ │ ├── HemiOctahedronToUnitVector.uasset │ │ ├── ImposterFrameTransform.uasset │ │ ├── ImposterFrameTransform_Setup.uasset │ │ ├── ImposterSpriteBillboard.uasset │ │ ├── ImpostorGridToVector.uasset │ │ ├── MF_BIllboardXYZ.uasset │ │ ├── TriangleInterpolator.uasset │ │ ├── UnitVectorToHemiOctahedra.uasset │ │ └── VirtualPlaneCoordinates_Imposter.uasset │ │ └── MaterialInstances │ │ ├── M_BillboardXYZ_Inst.uasset │ │ ├── M_Impostor_SimpleOffset_Full_Inst.uasset │ │ ├── M_Impostor_SimpleOffset_Hemi_Inst.uasset │ │ ├── M_Impostor_SimpleOffset_Hemi_MRA_Inst.uasset │ │ ├── M_Impostor_SimpleOffset_Hemi_MRSA_Inst.uasset │ │ ├── M_Impostor_SimpleOffset_Hemi_MRSA_NoPDO_Inst.uasset │ │ ├── M_Impostor_SimpleOffset_Hemi_NoPDO_Inst.uasset │ │ ├── M_Impostor_SimpleOffset_Hemi_NoPDO_NoParallax_Inst.uasset │ │ └── M_Impostor_SimpleOffset_Inst.uasset └── Mesh │ └── SubdividedPlane.uasset ├── ImposterGenerator.uplugin ├── README.assets ├── image-20211105165558852.png ├── image-20211108163609671.png ├── image-20211108170514549.png ├── image-20211108180710560.png ├── image-20211108180744755.png ├── image-20211108181820233.png └── image-20211108183130883.png ├── README.md ├── Resources ├── ButtonIcon_40x.png └── Icon128.png └── Source └── ImposterGenerator ├── ImposterGenerator.Build.cs ├── Private ├── ImposterActor.cpp ├── ImposterCaptureActor.cpp ├── ImposterCore.cpp ├── ImposterGenerator.cpp ├── ImposterGeneratorCommands.cpp ├── ImposterGeneratorStyle.cpp └── ImposterManagerActor.cpp └── Public ├── ImposterActor.h ├── ImposterCaptureActor.h ├── ImposterCore.h ├── ImposterGenerator.h ├── ImposterGeneratorCommands.h ├── ImposterGeneratorStyle.h └── ImposterManagerActor.h /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* -------------------------------------------------------------------------------- /Content/Blueprints/BP_ImposterManagerActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Blueprints/BP_ImposterManagerActor.uasset -------------------------------------------------------------------------------- /Content/MPC/MPC_Capture.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/MPC/MPC_Capture.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/M_Blit_DFAlpha.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/M_Blit_DFAlpha.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/M_Blit_DilationRGB+DFAlpha.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/M_Blit_DilationRGB+DFAlpha.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/M_Blit_DilationRGBA.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/M_Blit_DilationRGBA.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/M_Capture_BaseColorAlpha.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/M_Capture_BaseColorAlpha.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/M_Capture_MetallicRoughnessAO.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/M_Capture_MetallicRoughnessAO.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/M_Capture_MetallicRoughnessSpecularAO.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/M_Capture_MetallicRoughnessSpecularAO.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/M_Capture_NormalDepth.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/M_Capture_NormalDepth.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/MaterialFunctions/MF_DistanceField.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/MaterialFunctions/MF_DistanceField.uasset -------------------------------------------------------------------------------- /Content/Material/Capture/MaterialFunctions/MF_UVDilation.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Capture/MaterialFunctions/MF_UVDilation.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/M_BillboardXYZ.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/M_BillboardXYZ.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/M_Imposter_Placeholder.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/M_Imposter_Placeholder.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/M_Impostor_SimpleOffset.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/M_Impostor_SimpleOffset.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/FrameBlendWeights.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/FrameBlendWeights.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/FrameBlendWeights_TextureObject.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/FrameBlendWeights_TextureObject.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/HemiOctahedronToUnitVector.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/HemiOctahedronToUnitVector.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/ImposterFrameTransform.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/ImposterFrameTransform.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/ImposterFrameTransform_Setup.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/ImposterFrameTransform_Setup.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/ImposterSpriteBillboard.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/ImposterSpriteBillboard.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/ImpostorGridToVector.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/ImpostorGridToVector.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/MF_BIllboardXYZ.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/MF_BIllboardXYZ.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/TriangleInterpolator.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/TriangleInterpolator.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/UnitVectorToHemiOctahedra.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/UnitVectorToHemiOctahedra.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialFunctions/VirtualPlaneCoordinates_Imposter.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialFunctions/VirtualPlaneCoordinates_Imposter.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_BillboardXYZ_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_BillboardXYZ_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Full_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Full_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_MRA_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_MRA_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_MRSA_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_MRSA_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_MRSA_NoPDO_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_MRSA_NoPDO_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_NoPDO_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_NoPDO_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_NoPDO_NoParallax_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Hemi_NoPDO_NoParallax_Inst.uasset -------------------------------------------------------------------------------- /Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Material/Imposter/MaterialInstances/M_Impostor_SimpleOffset_Inst.uasset -------------------------------------------------------------------------------- /Content/Mesh/SubdividedPlane.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Content/Mesh/SubdividedPlane.uasset -------------------------------------------------------------------------------- /ImposterGenerator.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Imposter Generator", 6 | "Description": "An imposter generator.", 7 | "Category": "Other", 8 | "CreatedBy": "malosgao", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": true, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "ImposterGenerator", 20 | "Type": "Editor", 21 | "LoadingPhase": "Default" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /README.assets/image-20211105165558852.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/README.assets/image-20211105165558852.png -------------------------------------------------------------------------------- /README.assets/image-20211108163609671.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/README.assets/image-20211108163609671.png -------------------------------------------------------------------------------- /README.assets/image-20211108170514549.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/README.assets/image-20211108170514549.png -------------------------------------------------------------------------------- /README.assets/image-20211108180710560.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/README.assets/image-20211108180710560.png -------------------------------------------------------------------------------- /README.assets/image-20211108180744755.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/README.assets/image-20211108180744755.png -------------------------------------------------------------------------------- /README.assets/image-20211108181820233.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/README.assets/image-20211108181820233.png -------------------------------------------------------------------------------- /README.assets/image-20211108183130883.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/README.assets/image-20211108183130883.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Imposter 插件 说明文档 (CN) 2 | 3 | v1.0 | for Unreal Engine 4+ 4 | 5 | 2021/11/8 6 | 7 | --- 8 | 9 | ## 插件简介 10 | 11 | ![image-20211105165558852](README.assets/image-20211105165558852.png) 12 | 13 | “Imposter” 可以理解为场景中一个原始模型的“替身”。不同于传统的 LOD 和基于合并多边形的 HLOD,该插件所使用的 Imposter 基于光场渲染,在运行时只通过一个朝向摄像机的平面(Billboard),可以显示出与原始模型别无二致的替身物体,并且效果优于低面数的 LOD 模型。 14 | 15 | Imposter 技术可以用来生成一个原始模型的 LOD 模型,也可以生成代表多个原始物体的 HLOD 模型。插件提供一套工作流,美术可以指定场景中的某些或全部物体,插件既可以一键生成出选中物体的 Imposter,其大小和位置与原始物体完全相同。Imposter 对象将会被保存到硬盘上(蓝图、纹理、材质实例、关卡),插件同时会创建 LOD 子关卡,供 World Composition 等支持 LOD 关卡的开放世界功能模块所使用。 16 | 17 | 18 | 19 | ## 插件特性 20 | 21 | * 插件由 https://github.com/ictusbrucks/ImpostorBaker 改良而来。渲染算法遵循原始插件,使用 C++ 重构原始插件,重写材质,并对渲染质量、性能和工作流加以优化。 22 | * 相比基于 Mesh 减面的 LOD(用于HLOD),Imposter 能提供更好的效果和性能。同时由于纹理可以流式加载而Mesh不能,Imposter 比传统方法更省内存,适合用在移动平台和开放世界类型游戏中。 23 | * 插件能够在不侵入场景的情况下,捕获并生成 Imposter 物体到新的子关卡中,位置和大小均与原物体一致,并将资产文件存入硬盘。该子关卡能够直接被用作 LOD 子关卡,供 World Composition 系统使用。 24 | * 插件提供完善的工作流支持,可以一键识别并生成整个景观关卡的 Imposter 物体并保存,简化美术操作繁琐程度。 25 | * 插件能够捕获并渲染常用的材质属性,包括 BaseColor, Depth, Normal, Opacity, Roughness, Metallic, Specular, AO,并且可以被 Imposter 还原。 26 | * Imposter 同时能正常渲染深度,能够和 Mesh 或 Imposter 产生正常的遮挡关系。 27 | * 支持延迟渲染和前向渲染,兼容移动平台渲染器(ES3)。 28 | * **完善的代码注释(英文)。** 29 | 30 | 31 | 32 | ## 使用流程 33 | 34 | 2. 导入插件。 35 | 3. 在插件目录的 `Blueprints` 下,找到 `BP_ImposterManagerActor` 并拖入到待生成的场景中。 36 | 4. 参照“插件参数”一章配置好参数。刚刚拖入场景中的蓝图已经包含一套默认的捕获参数,可以直接拿来使用。 37 | 4. 确保当前虚幻引擎工作在延迟渲染的渲染路径中(仅需要在捕获时使用延迟渲染)。 38 | 5. 点击 *A. Add Scene Actors* 按钮,将当前场景中满足条件的 Static Mesh Actor 添加到列表中,或找到 `Captured Actors ` 参数手动指定需要捕获的 Actors。 39 | 6. 点击 *B Init Core*,随后点击 *C Capture* 并等待(很短的)一段时间,此时插件已经将数据捕获到内存中。 40 | 7. 点击 *D Generate* ,此时插件将把 Imposter 蓝图类、相关纹理、材质蓝图和子关卡写入到硬盘中,并且将 Imposter Actor 以及其包含的子关卡添加进场景结构中。 41 | 8. 同时,也可以通过点击 *E Preview* 创建一份预览的 Imposter Actor 到场景中,供临时查看效果所使用。此时插件不会向硬盘写入任何信息,预览使用的 Imposter Actor 也不会被保存到地图中。 42 | 43 | 44 | 45 | ## 插件参数 46 | 47 | ### 捕获部分 48 | 49 | 为了保证低耦合性,因此捕获部分将不会硬编码任何和渲染和捕获材质有关的参数。当参数完全配置完毕后,该蓝图类实例即和材质产生耦合,可以被一起使用。捕获部分所对应的参数均来自于 `BP_ImposterManagerActor` 蓝图类中。 50 | 51 | #### 通用配置 52 | 53 | 下列参数均被包含在结构体 `FImposterGeneratorSettings` 中。后期版本将会允许序列化该类型到硬盘中,作为固定的配置信息资产,并可直接设置在插件面板中。 54 | 55 | ![image-20211108163609671](README.assets/image-20211108163609671.png) 56 | 57 | * **Imposter Type**:Imposter 的种类,其中包含: 58 | 59 | * Upper Hemi:上半球 Imposter。由于多数建筑、树木等物件将会被摆放在地上,因此为了节省纹理,地下的半球可以不捕获; 60 | * Full:整个球面捕获的 Imposter。 61 | 62 | * **Capture Material Map**:欲生成的纹理类型,为一个 Map 数据结构。字段可以选择为: 63 | 64 | ```c++ 65 | // These types can be captured by setting capture component's capture source, and it might be faster since GBuffer will not be composed. 66 | // post-processing material will not be used (ignored if assigned) here. 67 | SceneColor, 68 | // SCS_SceneColorHDR 69 | BaseColor, 70 | // SCS_BaseColor 71 | WorldNormal, 72 | // SCS_WorldNormal 73 | SceneDepth, 74 | // SCS_SceneDepth 75 | 76 | // These types should be used with a post-processing material to extract a particular RT in GBuffer. SCS_FinalColorLDR will be used to enable custom post-processing material. 77 | // There is no way to reuse GBuffer without tinkering engine code, so for each type, whole scene will be rendered entirely. Use aggregate capture type if you can. 78 | Depth01, 79 | Specular, 80 | Metallic, 81 | Opacity, 82 | Roughness, 83 | AO, 84 | Emissive, 85 | 86 | // Useful aggregate types below. Use these to avoid custom channel packing and speed will be faster. These type will always use a custom post-processing material. 87 | BaseColorAlpha, 88 | // RGB: Base Color, A: Opacity 89 | NormalDepth, 90 | // RGB: Normal, A: Linear Depth 91 | MetallicRoughnessSpecularAO, 92 | // R: Metallic, G: Roughness, B: Specular, A: AO 93 | 94 | // Custom type. 95 | // Actually, all types above can be considered as a named custom type since logics are the same. 96 | Custom1, 97 | Custom2, 98 | Custom3, 99 | Custom4, 100 | Custom5, 101 | Custom6, 102 | ``` 103 | 104 | 对于字段为 `SceneColor`,`BaseColor`,`WorldNormal`,`SceneDepth` 参数来说,其可以直接使用 SceneCaptureComponent 得到的结果,因此不需要指定这些字段所对应的值,留空即可;对于剩下的字段来说,需要指定一个材质,否则捕获将会被跳过。 105 | 106 | 当前插件预制了对于常用的 `BaseColorAlpha`,`NormalDepth`,`MetallicRoughnessSpecularAO` 的捕获材质。其为一个后处理材质,将待捕获的通道从 GBuffer 中取出并输出到对应分量中。用户也可以手动按照上述思路制作其余字段的捕获材质来拓展插件功能。 107 | 108 | ![image-20211108170514549](README.assets/image-20211108170514549.png) 109 | 110 | * **Additional Capture Blit**:(可选)捕获的后处理。一些捕获后的纹理可能需要附加的后处理 Pass 才能呈现最好的效果。即捕获流程为 `Capture Pass -> Additional Blit Pass #1 -> Additional Blit Pass #2...` 111 | 112 | * Capture Type 对应着上面所列举出来的捕获类型。这些类型也应该同时存在于上述 Capture Material Map 中来构成完整的捕获流程,不存在的类型将会被忽略(当然也可以选择都放在这里,用来支持所有 Capture Material Map 可能出现的捕获类型); 113 | 114 | * Blit Material:后处理材质。同样,此处内置了两个常用的后处理材质: 115 | 116 | * `DilationRGB+DFAlpha`:对 RGB 通道做 UV 膨胀,对 Alpha 通道生成 Distance Field。 117 | * `DilationRGBA`:对 RGBA 通道均做 UV 膨胀。 118 | 119 | UV 膨胀可以用于优化 Mipmap 的生成,对 Opacity Mask 生成 Distance Field 有助于让不同角度更好地进行混合。 120 | 121 | * **Imposter Material**:真正用来渲染 Imposter 的材质。由于捕获部分与材质解耦,因此此处选用的渲染材质类型必须和其余参数搭配使用。 122 | 123 | 内置的材质类型均为 `M_Impostor_SimpleOffset` 的材质实例,可以通过调整开关参数实现: 124 | 125 | * `SimpleOffset_Full`:整个球面的 Imposter。 126 | * `SimpleOffset_Hemi`:半球 Imposter。 127 | * `SimpleOffset_Hemi_MRSA`:半球 Imposter。支持输出 PBR 属性(Metallic, Roughness, Specular, AO)。 128 | * `SimpleOffset_Hemi_MRSA_NoPDO`:半球 Imposter。支持输出 PBR 属性。不输出 PDO。 129 | * `SimpleOffset_Hemi_NoPDO`:半球 Imposter。不输出 PDO。 130 | * `SimpleOffset_Hemi_NoPDO_NoParallax`:半球 Imposter。不输出 PDO。不进行二次深度矫正。 131 | 132 | 具体开关参数含义可以参考下面“内置 Imposter 渲染材质”一章。 133 | 134 | * **Imposter Material Grid Size Param**:渲染 Imposter 的材质中,决定 Octahedron 栅格大小的参数名称。 135 | 136 | 对于上述内置材质 `M_Impostor_SimpleOffset` 来说,该名称为 `Num Frames`。 137 | 138 | * **Imposter Material Size Param**:渲染 Imposter 的材质中,决定物体大小,即物体球体包围盒半径(`CaptureBounds.SphereRadius`)的参数名称。 139 | 140 | 对于上述内置材质 `M_Impostor_SimpleOffset` 来说,该名称为 `Default Mesh Size`。 141 | 142 | ![image-20211108180710560](README.assets/image-20211108180710560.png) 143 | 144 | * **Imposter Material Texture Binding Map**:渲染 Imposter 的材质中纹理参数所对应的名称。 145 | * **Unit Quad**:Imposter 所使用的 Billboard 几何体(Static Mesh)。可以参考内置 Mesh `SubdividedPlane` 。 146 | * **Capture MPC**:捕获材质所使用的 Material Property Collection(MPC)引用。插件通过该 MPC 将捕获参数传递给捕获材质。对于内置的捕获材质,内置的 `MPC_Capture` 已经够用。 147 | * **Working Path**:插件工作目录。所有 Imposter 相关内容均会被生成于工作目录之内,便于管理。 148 | 149 | #### 需要在每次生成时酌情调整的配置 150 | 151 | 下列参数在每次生成时可能均会更改,因此不纳入配置资产文件: 152 | 153 | ![image-20211108180744755](README.assets/image-20211108180744755.png) 154 | 155 | * **Imposter Name**:此次生成的 Imposter 名称。需要不和之前的命名重复。 156 | 157 | 每个 Imposter 在保存资产时均会保存在工作目录内,以该名称命名的文件夹下。 158 | 159 | * **Captured Actors**:此次需要捕获的 Actor 列表。该列表可以手动指定,也可以通过 *A Add Scene Actors* 按钮自动添加。 160 | 161 | #### 功能按钮 162 | 163 | ![image-20211108181820233](README.assets/image-20211108181820233.png) 164 | 165 | 其中 Actions 分类中的五个按钮在使用流程一章均有提及,此处不再赘述。 166 | 167 | * ***Debug Capture Bounds***:在编辑器视口中绘制捕获物体对应的 AABB 和球包围盒。需要在执行到 *B Init Core* 之后使用。 168 | * ***Debug Capture Directions***:在编辑器视口中绘制全部捕获方向的 Debug 信息。需要在执行到 *B Init Core* 之后使用。 169 | 170 | ### 内置 Imposter 渲染材质 171 | 172 | 材质位于插件目录下 `Material/Imposter/M_Impostor_SimpleOffset.uasset` 173 | 174 | 对于其材质实例来说,有下列可调节参数(以 `M_Impostor_SimpleOffset_Hemi_MRSA_Inst` 为例): 175 | 176 | ![image-20211108183130883](README.assets/image-20211108183130883.png) 177 | 178 | * **Full Sphere**:是否渲染为整个球面的 Imposter。该选项需要和上述捕获部分的配置保持一致。 179 | * **Use MSRA**:是否采样并输出 PBR 属性(Metallic, Roughness, Specular, AO)。关闭可以减少一张纹理使用,但效果会变差。 180 | * **Use Parallax**:是否开启额外的深度矫正。关闭深度矫正用来获得更高的性能,但效果会变差。 181 | * **Use PDO**:是否输出 Pixel Depth Offset,用来写入正确的物体深度以正确渲染遮挡关系。部分机型可能不兼容 PDO。关闭 PDO 用来获得更高的性能。 182 | * **Atlas Mip Bias**:采样纹理图集时的 Mip 偏移(作用于 `TexCoord0`)。调高此值来使用更高 Mipmap 渲染,可能会节省纹理内存。 183 | * **Default Mesh Size**:物体大小,即物体球体包围盒半径(`CaptureBounds.SphereRadius`)。该值由插件自动设置。 184 | * **Mip Level For Parallax**:深度矫正时使用的 Mipmaps 层级。调高层级可能会获得更高的性能。 185 | * **Num Frames**:Octahedron 栅格大小。该值由插件自动设置。 186 | * **Opacity Mask Offset**:输出 Opacity Mask 时的偏移值。材质使用 0.5 来截断 Opacity Mask。使用负值可以缩小物体轮廓,可能能够避免一些特定的渲染异常现象。 187 | * **BaseColorAlpha,MetallicRoughnessSpecularAO,NormalDepth**:和捕获部分所对应,为材质所需要的三张纹理。如果 Use MSRA 未开启,MetallicRoughnessSpecularAO 将不可用,即所需纹理降至两张。 188 | 189 | 190 | 191 | ## 目录结构 192 | 193 | ### ImposterGenerator Content 194 | 195 | * Blueprints 196 | * *BP_ImposterManagerActor*:当前的插件交互面板。拖入场景中,Details 面板即为用户的交互面板。 197 | * Material 198 | * Capture 199 | * Material Functions:目前均为 Additional Capture Blit 所使用的材质方法。 200 | * *M_Blit* 系列:上述 Additional Capture Blit 所使用的材质。 201 | * *M_Capture* 系列:上述 Capture Material Map 所使用的材质。 202 | * Imposter 203 | * *M_Impostor_SimpleOffset*:预设的 Imposter 渲染材质。 204 | * MaterialFunctions:预设的 Imposter 渲染材质所使用到的材质方法。 205 | * MaterialInstances:几种常用的 Imposter 渲染材质实例。 206 | * Mesh 207 | * *SubdividedPlane*:当前所使用的 Billboard 面片。细分一次用来保证计算得到的顶点信息插值正确。 208 | * MPC 209 | * *MPC_Capture*:当前插件默认的,用来给捕获材质传递信息所使用的 Material Property Collection 资源。 210 | 211 | ### ImposterGenerator C++ Classes 212 | 213 | * `ImposterActor`:承载 Imposter 的 Actor,带有一个 `StaticMeshComponent`,将会在游戏中使用。其包含少量代码用来设置其自身属性,例如是否投射阴影(默认为否,因为 Imposter 当前不支持阴影)。 214 | * `ImposterCaptureActor`:用来在场景中捕获物体的 Actor,带有一个 `SceneCaptureComponent2D` 用来捕获场景到 Render Target。被 `ImposterCore` 管理,承载 `ImposterCore` 与场景交互的逻辑,会在多次捕获之间复用。不会被保存到地图中。 215 | * `ImposterCore`:Imposter 核心模块,为一个 `UObject`。 216 | * `ImposterManagerActor`:用户和插件的交互 Actor,通过操作该 Actor 属性栏的参数和按钮来完成全部工作。该 Actor 会在工作时初始化并使用 `ImposterCore` 实例。后续有可能会使用 Slate 制作插件的交互面板,此时该 Actor 可能会被废弃。 217 | 218 | -------------------------------------------------------------------------------- /Resources/ButtonIcon_40x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Resources/ButtonIcon_40x.png -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GavinKG/ImposterGenerator/379d93d1a0372e5641e6d9f61135eb47bb220c07/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/ImposterGenerator/ImposterGenerator.Build.cs: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class ImposterGenerator : ModuleRules 6 | { 7 | public ImposterGenerator(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | "Projects", 38 | "InputCore", 39 | "UnrealEd", 40 | "ToolMenus", 41 | "CoreUObject", 42 | "Engine", 43 | "Slate", 44 | "SlateCore", 45 | // ... add private dependencies that you statically link with here ... 46 | } 47 | ); 48 | 49 | 50 | DynamicallyLoadedModuleNames.AddRange( 51 | new string[] 52 | { 53 | // ... add any modules that your module loads dynamically here ... 54 | } 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Private/ImposterActor.cpp: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | 4 | #include "ImposterActor.h" 5 | 6 | 7 | // Sets default values 8 | AImposterActor::AImposterActor() 9 | { 10 | PrimaryActorTick.bCanEverTick = false; 11 | 12 | RootComponent = CreateDefaultSubobject(TEXT("Root")); 13 | StaticMeshComponent = CreateDefaultSubobject(TEXT("StaticMesh")); 14 | StaticMeshComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); 15 | 16 | StaticMeshComponent->SetCastShadow(bCastShadows); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Private/ImposterCaptureActor.cpp: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | 4 | #include "ImposterCaptureActor.h" 5 | 6 | #include "Components/SceneCaptureComponent2D.h" 7 | #include "Components/StaticMeshComponent.h" 8 | #include "Materials/MaterialInstance.h" 9 | 10 | 11 | // Sets default values 12 | AImposterCaptureActor::AImposterCaptureActor() 13 | { 14 | RootComponent = CreateDefaultSubobject(TEXT("Root")); 15 | 16 | SceneCaptureComponent2D = CreateDefaultSubobject(TEXT("Capture")); 17 | SceneCaptureComponent2D->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); 18 | 19 | SceneCaptureComponent2D->ProjectionType = ECameraProjectionMode::Orthographic; 20 | 21 | TArray ShowFlagSettings; 22 | FEngineShowFlagsSetting AOShowFlags; 23 | AOShowFlags.ShowFlagName = TEXT("AmbientOcclusion"); 24 | ShowFlagSettings.Add(AOShowFlags); 25 | FEngineShowFlagsSetting FogShowFlags; 26 | FogShowFlags.ShowFlagName = TEXT("Fog"); 27 | ShowFlagSettings.Add(FogShowFlags); 28 | FEngineShowFlagsSetting AtmosphericFogShowFlags; 29 | AtmosphericFogShowFlags.ShowFlagName = TEXT("AtmosphericFog"); 30 | ShowFlagSettings.Add(AtmosphericFogShowFlags); 31 | SceneCaptureComponent2D->ShowFlagSettings = ShowFlagSettings; 32 | SceneCaptureComponent2D->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList; 33 | SceneCaptureComponent2D->bCaptureEveryFrame = 0; // we will trigger the capture manually. 34 | SceneCaptureComponent2D->bCaptureOnMovement = 0; 35 | SceneCaptureComponent2D->bAlwaysPersistRenderingState = true; // Set it to true to preserve GBuffer for post-processing material. 36 | 37 | // SceneCaptureComponent2D are not configured properly right now. Further init will be done inside RegisterActorInfo 38 | } 39 | 40 | void AImposterCaptureActor::SetCaptureTransform(const FVector& Location, const FRotator& Rotation) 41 | { 42 | SceneCaptureComponent2D->SetWorldLocationAndRotation(Location, Rotation); 43 | } 44 | 45 | void AImposterCaptureActor::RegisterActorInfo(const TArray& Actors, const FBoxSphereBounds& Bounds) 46 | { 47 | SceneCaptureComponent2D->ShowOnlyActors = Actors; 48 | SceneCaptureComponent2D->OrthoWidth = Bounds.SphereRadius * 2.0f; 49 | } 50 | 51 | void AImposterCaptureActor::SetPostProcessingMaterial(UMaterialInterface* Material) 52 | { 53 | check(SceneCaptureComponent2D); 54 | SceneCaptureComponent2D->PostProcessSettings.WeightedBlendables.Array.Reset(); 55 | if (!Material) 56 | { 57 | return; 58 | } 59 | FWeightedBlendable Blendable; 60 | Blendable.Object = Material; // UMaterialInstance implemented IBlendableInterface 61 | Blendable.Weight = 1; 62 | SceneCaptureComponent2D->PostProcessSettings.WeightedBlendables.Array.Add(Blendable); 63 | } 64 | 65 | void AImposterCaptureActor::Capture(ESceneCaptureSource CaptureSource, UTextureRenderTarget2D* RT) 66 | { 67 | SceneCaptureComponent2D->CaptureSource = CaptureSource; 68 | SceneCaptureComponent2D->TextureTarget = RT; 69 | SceneCaptureComponent2D->CaptureScene(); 70 | } 71 | 72 | // Called when the game starts or when spawned 73 | void AImposterCaptureActor::BeginPlay() 74 | { 75 | Super::BeginPlay(); 76 | } 77 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Private/ImposterCore.cpp: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | 4 | #include "ImposterCore.h" 5 | 6 | #include "AssetToolsModule.h" 7 | #include "DrawDebugHelpers.h" 8 | #include "EditorLevelUtils.h" 9 | #include "FileHelpers.h" 10 | #include "IAssetTools.h" 11 | #include "ImposterActor.h" 12 | #include "ImposterCaptureActor.h" 13 | #include "PackageTools.h" 14 | #include "Components/StaticMeshComponent.h" 15 | #include "Engine/Canvas.h" 16 | #include "Engine/LevelStreamingDynamic.h" 17 | #include "Engine/TextureRenderTarget2D.h" 18 | #include "Factories/MaterialInstanceConstantFactoryNew.h" 19 | #include "GameFramework/Actor.h" 20 | #include "Kismet/GameplayStatics.h" 21 | #include "Kismet/KismetMaterialLibrary.h" 22 | #include "Kismet/KismetRenderingLibrary.h" 23 | #include "Materials/MaterialInstanceConstant.h" 24 | #include "Materials/MaterialInstanceDynamic.h" 25 | #include "UObject/ConstructorHelpers.h" 26 | 27 | DEFINE_LOG_CATEGORY(LogImposterGenerator) 28 | 29 | FImposterGeneratorSettings::FImposterGeneratorSettings() 30 | { 31 | // find default value 32 | 33 | if (!UnitQuad) 34 | { 35 | static ConstructorHelpers::FObjectFinder UnitQuadObj(TEXT("/Engine/BasicShapes/Plane")); 36 | if (UnitQuadObj.Succeeded()) 37 | { 38 | UnitQuad = UnitQuadObj.Object; 39 | } 40 | } 41 | } 42 | 43 | UImposterCore::UImposterCore() 44 | { 45 | UE_LOG(LogImposterGenerator, Display, TEXT("ImposterCore created.")); 46 | } 47 | 48 | bool UImposterCore::CanAddActorToCapture(const AActor* Actor) 49 | { 50 | // null check 51 | if (!Actor) 52 | { 53 | return false; 54 | } 55 | 56 | // if actor is an imposter actor, ignore. 57 | if (Actor->IsA(AImposterActor::StaticClass())) 58 | { 59 | return false; 60 | } 61 | 62 | // if actor is hidden, ignore. 63 | if (Actor->IsHidden() || Actor->IsHiddenEd()) 64 | { 65 | return false; 66 | } 67 | 68 | // should have at least one static mesh component. 69 | TArray StaticMeshComponents; 70 | Actor->GetComponents(StaticMeshComponents, false); 71 | 72 | bool bFoundValidComponent = false; 73 | for (const UStaticMeshComponent* StaticMeshComponent : StaticMeshComponents) 74 | { 75 | if (!StaticMeshComponent->bHiddenInGame && StaticMeshComponent->IsVisible()) 76 | { 77 | bFoundValidComponent = true; 78 | break; 79 | } 80 | } 81 | 82 | return bFoundValidComponent; 83 | } 84 | 85 | bool UImposterCore::TryAddCapturedActor(AActor* Actor) 86 | { 87 | if (CanAddActorToCapture(Actor)) 88 | { 89 | CapturedActors.Add(Actor); 90 | return true; 91 | } 92 | return false; 93 | } 94 | 95 | void UImposterCore::RemoveAllCapturedActors() 96 | { 97 | CapturedActors.Reset(); 98 | } 99 | 100 | bool UImposterCore::Init(const FImposterGeneratorSettings& ImposterGeneratorSettings, 101 | const TArray& CapturedActorArray, 102 | const FString& ImposterNameString) 103 | { 104 | // COPY all parameters to core. 105 | this->Settings = ImposterGeneratorSettings; 106 | this->CapturedActors = CapturedActorArray; 107 | this->ImposterName = ImposterNameString; 108 | 109 | if (!ShouldInit()) 110 | { 111 | UE_LOG(LogImposterGenerator, Error, TEXT("Initialization failed. See errors above.")); 112 | return false; 113 | } 114 | 115 | InitTex(); 116 | InitCaptureBounds(); 117 | InitCaptureDirections(); 118 | InitMpc(); 119 | 120 | ImposterCoreState = EImposterCoreState::Initialized; 121 | UE_LOG(LogImposterGenerator, Display, TEXT("Initialization finished.")); 122 | return true; 123 | } 124 | 125 | // Returned direction vector is normalized. 126 | FVector UImposterCore::ImposterGridToDirVector(FVector2D Grid) const 127 | { 128 | Grid = Grid * 2 - 1; 129 | switch (Settings.ImposterType) 130 | { 131 | case EImposterSphereType::UpperHemi: 132 | return HemiOctahedronToDirVector(Grid); 133 | case EImposterSphereType::Full: 134 | return OctahedronToDirVector(Grid); 135 | } 136 | check(0); 137 | return FVector::ZeroVector; 138 | } 139 | 140 | FVector UImposterCore::OctahedronToDirVector(const FVector2D& Grid) 141 | { 142 | // Same as Blueprint "Octahedron to Vector" 143 | const FVector2D GridAbs(FMath::Abs(Grid.X), FMath::Abs(Grid.Y)); 144 | const float T1 = 1 - FVector2D::DotProduct(GridAbs, FVector2D::UnitVector); 145 | if (T1 >= 0) 146 | { 147 | FVector Ret(Grid.X, Grid.Y, T1); 148 | Ret.Normalize(0.0001f); 149 | return Ret; 150 | } 151 | else 152 | { 153 | FVector Ret((Grid.X >= 0 ? 1 : -1) * (1 - GridAbs.Y), (Grid.Y >= 0 ? 1 : -1) * (1 - GridAbs.X), T1); 154 | Ret.Normalize(0.0001f); 155 | return Ret; 156 | } 157 | } 158 | 159 | FVector UImposterCore::HemiOctahedronToDirVector(const FVector2D& Grid) 160 | { 161 | // Same as Blueprint "Hemi Octahedron to Unit Vector" 162 | FVector2D Grid2(Grid.X + Grid.Y, Grid.X - Grid.Y); 163 | Grid2 *= 0.5f; 164 | const float Z = 1 - FVector2D::DotProduct(FVector2D(FMath::Abs(Grid2.X), FMath::Abs(Grid2.Y)), 165 | FVector2D::UnitVector); 166 | FVector Ret(Grid2.X, Grid2.Y, Z); 167 | Ret.Normalize(0.0001f); 168 | return Ret; 169 | } 170 | 171 | void UImposterCore::InitTex() 172 | { 173 | CapturedTexMap.Reset(); 174 | for (EImposterCaptureType CaptureType : Settings.CaptureTextureTypes) 175 | { 176 | UTextureRenderTarget2D* RT = NewObject(this); 177 | check(RT); 178 | RT->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8; 179 | RT->ClearColor = FLinearColor::Transparent; 180 | RT->bAutoGenerateMips = false; 181 | RT->InitAutoFormat((Settings.ImposterTextureRes), 182 | (Settings.ImposterTextureRes)); 183 | RT->UpdateResourceImmediate(true); 184 | CapturedTexMap.Add(CaptureType, RT); 185 | } 186 | 187 | MiddleRT = NewObject(this); 188 | check(MiddleRT); 189 | MiddleRT->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8; 190 | MiddleRT->bAutoGenerateMips = false; 191 | MiddleRT->InitAutoFormat(Settings.ImposterTextureRes / Settings.GridSize, 192 | Settings.ImposterTextureRes / Settings.GridSize); 193 | // todo: supersampling? 194 | MiddleRT->ClearColor = FLinearColor::Transparent; 195 | MiddleRT->UpdateResourceImmediate(true); 196 | } 197 | 198 | void UImposterCore::InitCaptureBounds() 199 | { 200 | UGameplayStatics::GetActorArrayBounds(CapturedActors, false, CaptureBounds.Origin, 201 | CaptureBounds.BoxExtent); 202 | CaptureBounds.SphereRadius = CaptureBounds.BoxExtent.Size(); 203 | UE_LOG(LogImposterGenerator, Display, TEXT("Capture Bounds generated with origin %s, extent %s, radius %f"), 204 | *CaptureBounds.Origin.ToString(), *CaptureBounds.BoxExtent.ToString(), CaptureBounds.SphereRadius); 205 | } 206 | 207 | void UImposterCore::InitCaptureDirections() 208 | { 209 | CaptureDirections.Reset(); 210 | for (uint32 Y = 0; Y < Settings.GridSize; ++Y) 211 | { 212 | for (uint32 X = 0; X < Settings.GridSize; ++X) 213 | { 214 | FVector2D Loc(static_cast(X) / (Settings.GridSize - 1), 215 | static_cast(Y) / (Settings.GridSize - 1)); 216 | CaptureDirections.Add(ImposterGridToDirVector(Loc)); 217 | } 218 | } 219 | } 220 | 221 | void UImposterCore::InitMpc() 222 | { 223 | UKismetMaterialLibrary::SetScalarParameterValue(GetWorld(), Settings.CaptureMpc, FName(TEXT("CaptureRadius")), 224 | CaptureBounds.SphereRadius); 225 | UKismetMaterialLibrary::SetVectorParameterValue(GetWorld(), Settings.CaptureMpc, FName(TEXT("CaptureOrigin")), 226 | CaptureBounds.Origin); 227 | UKismetMaterialLibrary::SetVectorParameterValue(GetWorld(), Settings.CaptureMpc, FName(TEXT("CaptureExtent")), 228 | CaptureBounds.BoxExtent); 229 | UKismetMaterialLibrary::SetScalarParameterValue(GetWorld(), Settings.CaptureMpc, FName(TEXT("TextureSize")), 230 | static_cast(Settings.ImposterTextureRes)); 231 | } 232 | 233 | bool UImposterCore::ShouldInit(bool bShouldLog) const 234 | { 235 | bool bResult = true; 236 | 237 | if (CapturedActors.Num() == 0) 238 | { 239 | if (bShouldLog) 240 | { 241 | UE_LOG(LogImposterGenerator, Error, TEXT("No actors to capture.")); 242 | } 243 | bResult = false; 244 | } 245 | 246 | if (Settings.CaptureTextureTypes.Num() == 0) 247 | { 248 | if (bShouldLog) 249 | { 250 | UE_LOG(LogImposterGenerator, Error, TEXT("Capture texture type not specified.")); 251 | } 252 | bResult = false; 253 | } 254 | 255 | if (Settings.ImposterTextureRes % 2u != 0) 256 | { 257 | if (bShouldLog) 258 | { 259 | UE_LOG(LogImposterGenerator, Error, TEXT("ImposterTextureRes should be power of 2.")); 260 | } 261 | bResult = false; 262 | } 263 | 264 | if (bShouldLog && !Settings.ImposterMaterial) 265 | { 266 | UE_LOG(LogImposterGenerator, Warning, 267 | TEXT("Imposter material not assigned properly. Imposter MIC might not be generated.")); 268 | } 269 | 270 | if (bShouldLog && Settings.ImposterMaterialTextureBindingMap.Num() == 0) 271 | { 272 | UE_LOG(LogImposterGenerator, Warning, 273 | TEXT( 274 | "Imposter material texture bindings not set properly. Imposter will have no texture to use when exporting and you have to manually assign it." 275 | )); 276 | } 277 | 278 | if (bShouldLog && !Settings.CaptureMpc) 279 | { 280 | UE_LOG(LogImposterGenerator, Warning, 281 | TEXT( 282 | "Capture Material Property Collection not assigned. Materials that uses capture properties may not work proerly." 283 | )) 284 | } 285 | 286 | 287 | return bResult; 288 | } 289 | 290 | bool UImposterCore::ShouldCapture(bool bShouldLog) const 291 | { 292 | bool bResult = true; 293 | 294 | if (ImposterCoreState < EImposterCoreState::Initialized) 295 | { 296 | if (bShouldLog) 297 | { 298 | UE_LOG(LogImposterGenerator, Error, TEXT("Imposter core not initialized.")); 299 | bResult = false; 300 | } 301 | } 302 | 303 | return bResult; 304 | } 305 | 306 | bool UImposterCore::ShouldGenerate(bool bShouldLog) const 307 | { 308 | bool bResult = true; 309 | 310 | if (ImposterCoreState < EImposterCoreState::Captured) 311 | { 312 | if (bShouldLog) 313 | { 314 | UE_LOG(LogImposterGenerator, Error, TEXT("Not captured.")); 315 | } 316 | bResult = false; 317 | } 318 | 319 | if (Settings.WorkingPath.IsEmpty()) 320 | { 321 | if (bShouldLog) 322 | { 323 | UE_LOG(LogImposterGenerator, Error, 324 | TEXT( 325 | "Working path not specified. Working path is the only path required and you did not specify it. Great job dude!" 326 | )); 327 | } 328 | bResult = false; 329 | } 330 | 331 | if (CapturedTexMap.Num() == 0) 332 | { 333 | if (bShouldLog) 334 | { 335 | UE_LOG(LogImposterGenerator, Error, TEXT("Nothing to export.")) 336 | } 337 | bResult = false; 338 | } 339 | 340 | return bResult; 341 | } 342 | 343 | void UImposterCore::Capture() 344 | { 345 | if (!ShouldCapture(true)) 346 | { 347 | UE_LOG(LogImposterGenerator, Error, TEXT("Initialization failed. See above for details.")); 348 | return; 349 | } 350 | 351 | // Check whether this object is spawned by a world-related object. 352 | check(GetWorld()); 353 | 354 | // Put capture actor to current active level if not spawned. 355 | if (!ImposterCaptureActor) 356 | { 357 | FActorSpawnParameters Params; 358 | Params.ObjectFlags |= RF_Transient; // should not save with map. 359 | ImposterCaptureActor = GetWorld()->SpawnActor(Params); 360 | check(ImposterCaptureActor); 361 | } 362 | 363 | ImposterCaptureActor->RegisterActorInfo(CapturedActors, CaptureBounds); 364 | 365 | // For each capture type 366 | for (const EImposterCaptureType CaptureType : Settings.CaptureTextureTypes) 367 | { 368 | const ESceneCaptureSource SceneCaptureSource = GetSceneCaptureSourceFrom(CaptureType); 369 | 370 | if (SceneCaptureSource == SCS_FinalColorLDR) 371 | { 372 | // Should use a custom pp material. 373 | UMaterialInterface** PtrToCaptureMaterial = Settings.CaptureMaterialMap.Find(CaptureType); 374 | if (PtrToCaptureMaterial && *PtrToCaptureMaterial) 375 | { 376 | ImposterCaptureActor->SetPostProcessingMaterial(*PtrToCaptureMaterial); 377 | UE_LOG(LogImposterGenerator, Display, TEXT("Capture %s with post-processing material %s..."), 378 | *CaptureTypeEnumToString(CaptureType), *(*PtrToCaptureMaterial)->GetName()); 379 | } 380 | else 381 | { 382 | UE_LOG(LogImposterGenerator, Warning, 383 | TEXT("Material for capture type %s could not be found. Capture will be skipped for this type."), 384 | *CaptureTypeEnumToString(CaptureType)); 385 | continue; 386 | } 387 | } 388 | else 389 | { 390 | ImposterCaptureActor->SetPostProcessingMaterial(nullptr); 391 | UE_LOG(LogImposterGenerator, Display, TEXT("Capture %s with custom scene capture source..."), 392 | *CaptureTypeEnumToString(CaptureType)); 393 | } 394 | 395 | // for each capture direction 396 | for (int32 i = 0; i < CaptureDirections.Num(); ++i) 397 | { 398 | const FVector& Dir = CaptureDirections[i]; 399 | 400 | // Transform Scene Capture Component 401 | FVector Loc = CaptureBounds.Origin + Dir * CaptureBounds.SphereRadius; 402 | FRotator Rot = FRotationMatrix::MakeFromX(-Dir).Rotator(); // UKismetMathLibrary::MakeRotFromX() 403 | ImposterCaptureActor->SetCaptureTransform(Loc, Rot); 404 | 405 | // Capture to MiddleRT 406 | ImposterCaptureActor->Capture(SceneCaptureSource, MiddleRT); 407 | 408 | // Compose final tex 409 | UTextureRenderTarget2D* CompTex = CapturedTexMap[CaptureType]; 410 | check(CompTex); 411 | UCanvas* Canvas; 412 | FVector2D CanvasSize; // whole canvas size, same as ImposterTextureRes 413 | FDrawToRenderTargetContext CompContext; 414 | 415 | UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(GetWorld(), CompTex, 416 | Canvas, CanvasSize, CompContext); 417 | 418 | // draw canvas 419 | const FVector2D SliceSize = CanvasSize / Settings.GridSize; 420 | FVector2D SliceOffset(i % Settings.GridSize, i / Settings.GridSize); // in grid coord, [0, GridSize) 421 | SliceOffset = SliceOffset / Settings.GridSize * CanvasSize; 422 | Canvas->K2_DrawTexture(MiddleRT, SliceOffset, SliceSize, FVector2D::ZeroVector, FVector2D::UnitVector, 423 | FLinearColor::White, BLEND_Opaque); 424 | 425 | UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(GetWorld(), CompContext); 426 | } // end capture type 427 | } // end capture direction 428 | 429 | // Apply additional blit 430 | for (const FImposterBlitSettings& BlitSettings : Settings.AdditionalCaptureBlit) 431 | { 432 | // Get Source RT 433 | UTextureRenderTarget2D** pRT = CapturedTexMap.Find(BlitSettings.CaptureType); 434 | if (!pRT) 435 | { 436 | continue; 437 | } 438 | UTextureRenderTarget2D* SrcRT = *pRT; 439 | check(SrcRT); // CapturedTexMap should not contain null RT. 440 | 441 | // Create Destination RT (ignore performance) 442 | UTextureRenderTarget2D* DstRT = NewObject(this); 443 | check(DstRT); 444 | DstRT->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8; 445 | DstRT->bAutoGenerateMips = false; 446 | DstRT->InitAutoFormat(static_cast(SrcRT->SizeX), 447 | static_cast(SrcRT->SizeY)); 448 | DstRT->UpdateResourceImmediate(false); 449 | 450 | // Draw material. 451 | UMaterialInstanceDynamic* Mid = UMaterialInstanceDynamic::Create(BlitSettings.BlitMaterial, this); 452 | Mid->SetTextureParameterValue(TEXT("Texture"), SrcRT); 453 | UKismetRenderingLibrary::DrawMaterialToRenderTarget(GetWorld(), DstRT, Mid); // TRY 454 | 455 | // Replace. 456 | CapturedTexMap[BlitSettings.CaptureType] = DstRT; 457 | } 458 | 459 | 460 | ImposterCoreState = EImposterCoreState::Captured; 461 | UE_LOG(LogImposterGenerator, Display, TEXT("Capture complete.")); 462 | } 463 | 464 | void UImposterCore::GenerateImposter(bool bCreateSublevel, const FVector& Offset) 465 | { 466 | if (!ShouldGenerate()) 467 | { 468 | UE_LOG(LogImposterGenerator, Error, TEXT("Generate failed. See above for more information.")) 469 | return; 470 | } 471 | 472 | // Step.1: Export textures (using UKismetRenderingLibrary). 473 | TMap ExportedTexMap; 474 | const FString TextureRootPath = FPaths::Combine(Settings.WorkingPath, ImposterName); 475 | for (const auto Elem : CapturedTexMap) 476 | { 477 | const FString TexturePath = FPaths::Combine(TextureRootPath, FString::Printf( 478 | TEXT("T_%s_%s"), *ImposterName, 479 | *CaptureTypeEnumToString(Elem.Key))); 480 | UTexture2D* ExportedTex = UKismetRenderingLibrary::RenderTargetCreateStaticTexture2DEditorOnly( 481 | Elem.Value, TexturePath, TC_Default, TMGS_FromTextureGroup); 482 | ExportedTexMap.Add(Elem.Key, ExportedTex); 483 | } 484 | 485 | // Step.2: Export Material Instance 486 | UMaterialInstanceConstant* Mic = CreateMIC_EditorOnly(Settings.ImposterMaterial, GetMaterialPackageName()); 487 | for (const auto& Elem : ExportedTexMap) 488 | { 489 | FString* TexParam = Settings.ImposterMaterialTextureBindingMap.Find(Elem.Key); 490 | if (!TexParam) 491 | { 492 | UE_LOG(LogImposterGenerator, Warning, TEXT("%s not found in texture binding map."), 493 | *CaptureTypeEnumToString(Elem.Key)); 494 | continue; 495 | } 496 | Mic->SetTextureParameterValueEditorOnly(FName(**TexParam), Elem.Value); 497 | } 498 | Mic->SetScalarParameterValueEditorOnly(Settings.ImposterMaterialGridSizeParam, Settings.GridSize); 499 | Mic->SetScalarParameterValueEditorOnly(Settings.ImposterMaterialSizeParam, CaptureBounds.SphereRadius * 2); 500 | 501 | // Step.3: Create Sublevel. 502 | ULevel* SubLevel = nullptr; 503 | if (bCreateSublevel) 504 | { 505 | ULevelStreaming* LevelStreaming = Cast( 506 | EditorLevelUtils::CreateNewStreamingLevelForWorld(*GetWorld(), ULevelStreamingDynamic::StaticClass(), 507 | GetSubLevelPackageName())); 508 | SubLevel = LevelStreaming->GetLoadedLevel(); 509 | check(SubLevel); 510 | EditorLevelUtils::MakeLevelCurrent(LevelStreaming); // for spawn actor 511 | } 512 | 513 | // Step.4: Generate Imposter to scene. 514 | FActorSpawnParameters Params; 515 | Params.Name = FName(GetActorAssetName()); 516 | AImposterActor* ImposterActor = GetWorld()->SpawnActor( 517 | CaptureBounds.Origin + Offset, FRotator::ZeroRotator, Params); 518 | UStaticMeshComponent* StaticMeshComponent = Cast( 519 | ImposterActor->GetComponentByClass(UStaticMeshComponent::StaticClass())); 520 | check(StaticMeshComponent); 521 | StaticMeshComponent->SetStaticMesh(Settings.UnitQuad); 522 | StaticMeshComponent->SetMaterial(0, Mic); 523 | StaticMeshComponent->SetBoundsScale(CaptureBounds.SphereRadius / 100.0f); // prevent OC flickering, 100: considered as 1uu, UnitQuad=100uu 524 | 525 | FEditorFileUtils::SaveLevel(SubLevel, GetSubLevelPackageName()); 526 | } 527 | 528 | void UImposterCore::PreviewImposter() 529 | { 530 | // MID 531 | UMaterialInstanceDynamic* Mid = UMaterialInstanceDynamic::Create(Settings.ImposterMaterial, this); 532 | for (const auto& Elem : CapturedTexMap) 533 | { 534 | FString* TexParam = Settings.ImposterMaterialTextureBindingMap.Find(Elem.Key); 535 | if (!TexParam) 536 | { 537 | UE_LOG(LogImposterGenerator, Warning, TEXT("(PreviewImposter) %s not found in texture binding map."), 538 | *CaptureTypeEnumToString(Elem.Key)); 539 | continue; 540 | } 541 | Mid->SetTextureParameterValue(**TexParam, Elem.Value); 542 | Mid->SetScalarParameterValue(Settings.ImposterMaterialGridSizeParam, Settings.GridSize); 543 | Mid->SetScalarParameterValue(Settings.ImposterMaterialSizeParam, CaptureBounds.SphereRadius * 2); 544 | } 545 | 546 | // Transient Actor 547 | FActorSpawnParameters Params; 548 | // Params.Name = MakeUniqueObjectName(this, AImposterActor::StaticClass(), FName(GetActorAssetName())); 549 | Params.ObjectFlags |= RF_Transient; 550 | AImposterActor* ImposterActor = GetWorld()->SpawnActor( 551 | CaptureBounds.Origin, FRotator::ZeroRotator, Params); 552 | UStaticMeshComponent* StaticMeshComponent = Cast( 553 | ImposterActor->GetComponentByClass(UStaticMeshComponent::StaticClass())); 554 | check(StaticMeshComponent); 555 | StaticMeshComponent->SetStaticMesh(Settings.UnitQuad); 556 | StaticMeshComponent->SetMaterial(0, Mid); 557 | StaticMeshComponent->SetBoundsScale(CaptureBounds.SphereRadius / 100.0f); 558 | } 559 | 560 | void UImposterCore::DebugCaptureDirections() const 561 | { 562 | FlushPersistentDebugLines(GetWorld()); 563 | for (const FVector& Dir : CaptureDirections) 564 | { 565 | DrawDebugDirectionalArrow(GetWorld(), CaptureBounds.Origin + Dir * CaptureBounds.SphereRadius * 1.5, 566 | CaptureBounds.Origin + Dir * CaptureBounds.SphereRadius, 10.0f, FColor::Red, 567 | false, 5.0f, 0, 1); 568 | } 569 | } 570 | 571 | void UImposterCore::DebugCaptureBounds() const 572 | { 573 | FlushPersistentDebugLines(GetWorld()); 574 | DrawDebugSphere(GetWorld(), CaptureBounds.Origin, CaptureBounds.SphereRadius, 32, FColor::Red, false, 5.f, 0, 0); 575 | DrawDebugBox(GetWorld(), CaptureBounds.Origin, CaptureBounds.BoxExtent, FColor::Yellow, false, 6.f, 0, 1); 576 | } 577 | 578 | ESceneCaptureSource UImposterCore::GetSceneCaptureSourceFrom(EImposterCaptureType ImposterCaptureType) 579 | { 580 | switch (ImposterCaptureType) 581 | { 582 | case EImposterCaptureType::SceneColor: 583 | return SCS_SceneColorHDR; 584 | case EImposterCaptureType::SceneDepth: 585 | return SCS_SceneDepth; 586 | case EImposterCaptureType::WorldNormal: 587 | return SCS_Normal; 588 | case EImposterCaptureType::BaseColor: 589 | return SCS_BaseColor; 590 | default: 591 | return SCS_FinalColorLDR; 592 | } 593 | } 594 | 595 | FString UImposterCore::CaptureTypeEnumToString(EImposterCaptureType Type) 596 | { 597 | switch (Type) 598 | { 599 | case EImposterCaptureType::SceneColor: return TEXT("SceneColor"); 600 | case EImposterCaptureType::Depth01: return TEXT("SceneDepth"); 601 | case EImposterCaptureType::WorldNormal: return TEXT("WorldNormal"); 602 | case EImposterCaptureType::Metallic: return TEXT("Metallic"); 603 | case EImposterCaptureType::Opacity: return TEXT("Opacity"); 604 | case EImposterCaptureType::Roughness: return TEXT("Roughness"); 605 | case EImposterCaptureType::Specular: return TEXT("Specular"); 606 | case EImposterCaptureType::AO: return TEXT("AO"); 607 | case EImposterCaptureType::BaseColor: return TEXT("BaseColor"); 608 | case EImposterCaptureType::SceneDepth: return TEXT("SceneDepth"); 609 | case EImposterCaptureType::Emissive: return TEXT("Emissive"); 610 | case EImposterCaptureType::BaseColorAlpha: return TEXT("BaseColorAlpha"); 611 | case EImposterCaptureType::NormalDepth: return TEXT("NormalDepth"); 612 | case EImposterCaptureType::MetallicRoughnessSpecularAO: return TEXT("MetallicRoughnessSpecularAO"); 613 | case EImposterCaptureType::Custom1: return TEXT("Custom1"); 614 | case EImposterCaptureType::Custom2: return TEXT("Custom2"); 615 | case EImposterCaptureType::Custom3: return TEXT("Custom3"); 616 | case EImposterCaptureType::Custom4: return TEXT("Custom4"); 617 | case EImposterCaptureType::Custom5: return TEXT("Custom5"); 618 | case EImposterCaptureType::Custom6: return TEXT("Custom6"); 619 | default: check(0); 620 | } 621 | return TEXT(""); 622 | } 623 | 624 | void UImposterCore::PrefixReplaceContentToGame(FString& Path) 625 | { 626 | Path.RemoveFromStart(TEXT("/")); 627 | Path.RemoveFromStart(TEXT("Content/")); 628 | Path.StartsWith(TEXT("Game/")) == true ? Path.InsertAt(0, TEXT("/")) : Path.InsertAt(0, TEXT("/Game/")); 629 | } 630 | 631 | 632 | void UImposterCore::Reset() 633 | { 634 | CaptureDirections.Reset(); 635 | CapturedTexMap.Reset(); 636 | } 637 | 638 | // copied from BlueprintMaterialTextureNodesBPLibrary. 639 | UMaterialInstanceConstant* UImposterCore::CreateMIC_EditorOnly(UMaterialInterface* Material, FString InName) 640 | { 641 | check(Material); 642 | 643 | TArray ObjectsToSync; 644 | 645 | // Create an appropriate and unique name 646 | FString Name; 647 | FString PackageName; 648 | IAssetTools& AssetTools = FModuleManager::Get().LoadModuleChecked("AssetTools").Get(); 649 | 650 | //Use asset name only if directories are specified, otherwise full path 651 | if (!InName.Contains(TEXT("/"))) 652 | { 653 | FString AssetName = Material->GetOutermost()->GetName(); 654 | const FString SanitizedBasePackageName = UPackageTools::SanitizePackageName(AssetName); 655 | const FString PackagePath = FPackageName::GetLongPackagePath(SanitizedBasePackageName) + TEXT("/"); 656 | AssetTools.CreateUniqueAssetName(PackagePath, InName, PackageName, Name); 657 | } 658 | else 659 | { 660 | InName.RemoveFromStart(TEXT("/")); 661 | InName.RemoveFromStart(TEXT("Content/")); 662 | InName.StartsWith(TEXT("Game/")) == true 663 | ? InName.InsertAt(0, TEXT("/")) 664 | : InName.InsertAt(0, TEXT("/Game/")); 665 | AssetTools.CreateUniqueAssetName(InName, TEXT(""), PackageName, Name); 666 | } 667 | 668 | UMaterialInstanceConstantFactoryNew* Factory = NewObject(); 669 | Factory->InitialParent = Material; 670 | 671 | UObject* NewAsset = AssetTools.CreateAsset(Name, FPackageName::GetLongPackagePath(PackageName), 672 | UMaterialInstanceConstant::StaticClass(), Factory); 673 | 674 | ObjectsToSync.Add(NewAsset); 675 | GEditor->SyncBrowserToObjects(ObjectsToSync); 676 | 677 | UMaterialInstanceConstant* MIC = Cast(NewAsset); 678 | 679 | return MIC; 680 | } 681 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Private/ImposterGenerator.cpp: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #include "ImposterGenerator.h" 4 | #include "ImposterGeneratorStyle.h" 5 | #include "ImposterGeneratorCommands.h" 6 | #include "LevelEditor.h" 7 | #include "Widgets/Docking/SDockTab.h" 8 | #include "Widgets/Layout/SBox.h" 9 | #include "Widgets/Text/STextBlock.h" 10 | #include "ToolMenus.h" 11 | 12 | static const FName ImposterGeneratorTabName("ImposterGenerator"); 13 | 14 | #define LOCTEXT_NAMESPACE "FImposterGeneratorModule" 15 | 16 | void FImposterGeneratorModule::StartupModule() 17 | { 18 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 19 | 20 | FImposterGeneratorStyle::Initialize(); 21 | FImposterGeneratorStyle::ReloadTextures(); 22 | 23 | FImposterGeneratorCommands::Register(); 24 | 25 | PluginCommands = MakeShareable(new FUICommandList); 26 | 27 | PluginCommands->MapAction( 28 | FImposterGeneratorCommands::Get().OpenPluginWindow, 29 | FExecuteAction::CreateRaw(this, &FImposterGeneratorModule::PluginButtonClicked), 30 | FCanExecuteAction()); 31 | 32 | UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FImposterGeneratorModule::RegisterMenus)); 33 | 34 | FGlobalTabmanager::Get()->RegisterNomadTabSpawner(ImposterGeneratorTabName, FOnSpawnTab::CreateRaw(this, &FImposterGeneratorModule::OnSpawnPluginTab)) 35 | .SetDisplayName(LOCTEXT("FImposterGeneratorTabTitle", "ImposterGenerator")) 36 | .SetMenuType(ETabSpawnerMenuType::Hidden); 37 | } 38 | 39 | void FImposterGeneratorModule::ShutdownModule() 40 | { 41 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 42 | // we call this function before unloading the module. 43 | 44 | UToolMenus::UnRegisterStartupCallback(this); 45 | 46 | UToolMenus::UnregisterOwner(this); 47 | 48 | FImposterGeneratorStyle::Shutdown(); 49 | 50 | FImposterGeneratorCommands::Unregister(); 51 | 52 | FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(ImposterGeneratorTabName); 53 | } 54 | 55 | TSharedRef FImposterGeneratorModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) 56 | { 57 | FText WidgetText = FText::Format( 58 | LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"), 59 | FText::FromString(TEXT("FImposterGeneratorModule::OnSpawnPluginTab")), 60 | FText::FromString(TEXT("ImposterGenerator.cpp")) 61 | ); 62 | 63 | return SNew(SDockTab) 64 | .TabRole(ETabRole::NomadTab) 65 | [ 66 | // Put your tab content here! 67 | SNew(SBox) 68 | .HAlign(HAlign_Center) 69 | .VAlign(VAlign_Center) 70 | [ 71 | SNew(STextBlock) 72 | .Text(WidgetText) 73 | ] 74 | ]; 75 | } 76 | 77 | void FImposterGeneratorModule::PluginButtonClicked() 78 | { 79 | FGlobalTabmanager::Get()->TryInvokeTab(ImposterGeneratorTabName); 80 | } 81 | 82 | void FImposterGeneratorModule::RegisterMenus() 83 | { 84 | // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner 85 | FToolMenuOwnerScoped OwnerScoped(this); 86 | 87 | { 88 | UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window"); 89 | { 90 | FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout"); 91 | Section.AddMenuEntryWithCommandList(FImposterGeneratorCommands::Get().OpenPluginWindow, PluginCommands); 92 | } 93 | } 94 | 95 | { 96 | UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); 97 | { 98 | FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings"); 99 | { 100 | FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FImposterGeneratorCommands::Get().OpenPluginWindow)); 101 | Entry.SetCommandList(PluginCommands); 102 | } 103 | } 104 | } 105 | } 106 | 107 | #undef LOCTEXT_NAMESPACE 108 | 109 | IMPLEMENT_MODULE(FImposterGeneratorModule, ImposterGenerator) -------------------------------------------------------------------------------- /Source/ImposterGenerator/Private/ImposterGeneratorCommands.cpp: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #include "ImposterGeneratorCommands.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FImposterGeneratorModule" 6 | 7 | void FImposterGeneratorCommands::RegisterCommands() 8 | { 9 | UI_COMMAND(OpenPluginWindow, "ImposterGenerator", "Bring up ImposterGenerator window", EUserInterfaceActionType::Button, FInputGesture()); 10 | } 11 | 12 | #undef LOCTEXT_NAMESPACE 13 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Private/ImposterGeneratorStyle.cpp: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #include "ImposterGeneratorStyle.h" 4 | #include "Styling/SlateStyleRegistry.h" 5 | #include "Framework/Application/SlateApplication.h" 6 | #include "Slate/SlateGameResources.h" 7 | #include "Interfaces/IPluginManager.h" 8 | 9 | TSharedPtr< FSlateStyleSet > FImposterGeneratorStyle::StyleInstance = NULL; 10 | 11 | void FImposterGeneratorStyle::Initialize() 12 | { 13 | if (!StyleInstance.IsValid()) 14 | { 15 | StyleInstance = Create(); 16 | FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); 17 | } 18 | } 19 | 20 | void FImposterGeneratorStyle::Shutdown() 21 | { 22 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); 23 | ensure(StyleInstance.IsUnique()); 24 | StyleInstance.Reset(); 25 | } 26 | 27 | FName FImposterGeneratorStyle::GetStyleSetName() 28 | { 29 | static FName StyleSetName(TEXT("ImposterGeneratorStyle")); 30 | return StyleSetName; 31 | } 32 | 33 | #define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 34 | #define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 35 | #define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 36 | #define TTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ ) 37 | #define OTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ ) 38 | 39 | const FVector2D Icon16x16(16.0f, 16.0f); 40 | const FVector2D Icon20x20(20.0f, 20.0f); 41 | const FVector2D Icon40x40(40.0f, 40.0f); 42 | 43 | TSharedRef< FSlateStyleSet > FImposterGeneratorStyle::Create() 44 | { 45 | TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("ImposterGeneratorStyle")); 46 | Style->SetContentRoot(IPluginManager::Get().FindPlugin("ImposterGenerator")->GetBaseDir() / TEXT("Resources")); 47 | 48 | Style->Set("ImposterGenerator.OpenPluginWindow", new IMAGE_BRUSH(TEXT("ButtonIcon_40x"), Icon40x40)); 49 | 50 | return Style; 51 | } 52 | 53 | #undef IMAGE_BRUSH 54 | #undef BOX_BRUSH 55 | #undef BORDER_BRUSH 56 | #undef TTF_FONT 57 | #undef OTF_FONT 58 | 59 | void FImposterGeneratorStyle::ReloadTextures() 60 | { 61 | if (FSlateApplication::IsInitialized()) 62 | { 63 | FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); 64 | } 65 | } 66 | 67 | const ISlateStyle& FImposterGeneratorStyle::Get() 68 | { 69 | return *StyleInstance; 70 | } 71 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Private/ImposterManagerActor.cpp: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | 4 | #include "ImposterManagerActor.h" 5 | 6 | #include "EngineUtils.h" 7 | 8 | AImposterManagerActor::AImposterManagerActor() 9 | { 10 | PrimaryActorTick.bCanEverTick = false; 11 | } 12 | 13 | void AImposterManagerActor::DebugCaptureDirections() 14 | { 15 | check(Core); 16 | Core->DebugCaptureDirections(); 17 | } 18 | 19 | void AImposterManagerActor::DebugCaptureBounds() 20 | { 21 | check(Core); 22 | Core->DebugCaptureBounds(); 23 | } 24 | 25 | void AImposterManagerActor::E_Preview() 26 | { 27 | check(Core); 28 | Core->PreviewImposter(); 29 | } 30 | 31 | void AImposterManagerActor::CopyDebugFromCore() 32 | { 33 | CapturedTexMap = Core->CapturedTexMap; 34 | MiddleRT = Core->MiddleRT; 35 | } 36 | 37 | void AImposterManagerActor::C_Capture() 38 | { 39 | check(Core); 40 | Core->Capture(); 41 | CopyDebugFromCore(); 42 | } 43 | 44 | void AImposterManagerActor::D_Generate() 45 | { 46 | check(Core); 47 | Core->GenerateImposter(true); 48 | } 49 | 50 | void AImposterManagerActor::A_AddSceneActors() 51 | { 52 | CapturedActors.Reset(); 53 | for (TActorIterator ActorItr(GetWorld()); ActorItr; ++ActorItr) 54 | { 55 | AActor* Actor = *ActorItr; 56 | if (Core->CanAddActorToCapture(Actor)) 57 | { 58 | CapturedActors.Add(Actor); // add to local array, not core. 59 | } 60 | } 61 | UE_LOG(LogImposterGenerator, Display, TEXT("%d actors added."), CapturedActors.Num()); 62 | } 63 | 64 | void AImposterManagerActor::B_InitCore() 65 | { 66 | if (!Core) 67 | { 68 | Core = NewObject(this, TEXT("Imposter Core")); 69 | } 70 | 71 | TArray CapturedActorsRawPtr; 72 | for (auto &Ptr : CapturedActors) 73 | { 74 | AActor* Actor = Ptr.Get(); 75 | if (Actor) 76 | { 77 | CapturedActorsRawPtr.Add(Actor); 78 | } 79 | } 80 | 81 | Core->Init(Settings, CapturedActorsRawPtr, ImposterName); 82 | CopyDebugFromCore(); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Public/ImposterActor.h: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "ImposterActor.generated.h" 8 | 9 | UCLASS() 10 | class IMPOSTERGENERATOR_API AImposterActor : public AActor 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | AImposterActor(); 16 | 17 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly) 18 | UStaticMeshComponent* StaticMeshComponent; 19 | 20 | // Imposter should not cast shadows since it uses "Camera Position" instead of light position to do geometry calculation, and we cannot get light direction easily. 21 | // May hardcoded a light direction in the future so I leave it with a parameter. 22 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 23 | bool bCastShadows = false; 24 | 25 | protected: 26 | }; 27 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Public/ImposterCaptureActor.h: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "ImposterCaptureActor.generated.h" 8 | 9 | 10 | // should be stateless to ImposterCore. Act as a bridge between core and level. 11 | UCLASS() 12 | class IMPOSTERGENERATOR_API AImposterCaptureActor : public AActor 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | UPROPERTY(EditAnywhere, BlueprintReadOnly) 18 | class USceneCaptureComponent2D* SceneCaptureComponent2D; 19 | 20 | public: 21 | AImposterCaptureActor(); 22 | 23 | void SetCaptureTransform(const FVector& Location, const FRotator& Rotation); 24 | 25 | void RegisterActorInfo(const TArray& Actors, const FBoxSphereBounds& Bounds); 26 | 27 | void SetPostProcessingMaterial(UMaterialInterface* Material); 28 | 29 | void Capture(ESceneCaptureSource CaptureSource, UTextureRenderTarget2D* RT); 30 | 31 | protected: 32 | virtual void BeginPlay() override; 33 | }; 34 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Public/ImposterCore.h: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | #include "ImposterCore.generated.h" 8 | 9 | DECLARE_LOG_CATEGORY_EXTERN(LogImposterGenerator, Log, All); 10 | 11 | 12 | UENUM() 13 | enum class EImposterSphereType : uint8 14 | { 15 | Full, 16 | UpperHemi 17 | // Billboards, we do not support billboard right now... 18 | }; 19 | 20 | UENUM() 21 | enum class EImposterCaptureType : uint8 22 | { 23 | // These types can be captured by setting capture component's capture source, and it might be faster since GBuffer will not be composed. 24 | // post-processing material will not be used (ignored if assigned) here. 25 | SceneColor, 26 | // SCS_SceneColorHDR 27 | BaseColor, 28 | // SCS_BaseColor 29 | WorldNormal, 30 | // SCS_WorldNormal 31 | SceneDepth, 32 | // SCS_SceneDepth 33 | 34 | // These types should be used with a post-processing material to extract a particular RT in GBuffer. SCS_FinalColorLDR will be used to enable custom post-processing material. 35 | // There is no way to reuse GBuffer without tinkering engine code, so for each type, whole scene will be rendered entirely. Use aggregate capture type if you can. 36 | Depth01, 37 | Specular, 38 | Metallic, 39 | Opacity, 40 | Roughness, 41 | AO, 42 | Emissive, 43 | 44 | // Useful aggregate types below. Use these to avoid custom channel packing and speed will be faster. These type will always use a custom post-processing material. 45 | BaseColorAlpha, 46 | // RGB: Base Color, A: Opacity 47 | NormalDepth, 48 | // RGB: Normal, A: Linear Depth 49 | MetallicRoughnessSpecularAO, 50 | // R: Metallic, G: Roughness, B: Specular, A: AO 51 | 52 | // Custom type. 53 | // Actually, all types above can be considered as a named custom type since logics are the same. 54 | Custom1, 55 | Custom2, 56 | Custom3, 57 | Custom4, 58 | Custom5, 59 | Custom6, 60 | }; 61 | 62 | USTRUCT(BlueprintType) 63 | struct FImposterBlitSettings 64 | { 65 | GENERATED_BODY() 66 | 67 | UPROPERTY(EditAnywhere) 68 | EImposterCaptureType CaptureType; 69 | 70 | UPROPERTY(EditAnywhere) 71 | class UMaterialInterface* BlitMaterial; 72 | }; 73 | 74 | // Imposter Generate overall settings, or profiles. 75 | USTRUCT(BlueprintType) 76 | struct FImposterGeneratorSettings 77 | { 78 | GENERATED_BODY() 79 | 80 | FImposterGeneratorSettings(); 81 | 82 | UPROPERTY(EditAnywhere) 83 | EImposterSphereType ImposterType; 84 | 85 | UPROPERTY(EditAnywhere) 86 | TArray CaptureTextureTypes; 87 | 88 | UPROPERTY(EditAnywhere) 89 | uint32 GridSize = 8; 90 | 91 | UPROPERTY(EditAnywhere) 92 | uint32 ImposterTextureRes = 4096; 93 | 94 | UPROPERTY(EditAnywhere) 95 | TMap CaptureMaterialMap; 96 | 97 | // Used for custom multi-pass post-processing (aka. blit) on captured material map, like UV dilation and distance field generation. 98 | // Blit will be executed in order. 99 | // Same capture type can be used to achieve multi-pass blit. 100 | // Source texture parameter should be named "Texture" 101 | UPROPERTY(EditAnywhere) 102 | TArray AdditionalCaptureBlit; 103 | 104 | UPROPERTY(EditAnywhere) 105 | class UMaterialInterface* ImposterMaterial; 106 | 107 | UPROPERTY(EditAnywhere) 108 | FName ImposterMaterialGridSizeParam = TEXT("FramesXY"); 109 | 110 | UPROPERTY(EditAnywhere) 111 | FName ImposterMaterialSizeParam = TEXT("Default Mesh Size"); 112 | 113 | UPROPERTY(EditAnywhere) 114 | TMap ImposterMaterialTextureBindingMap; 115 | 116 | UPROPERTY(EditAnywhere) 117 | UStaticMesh* UnitQuad; 118 | 119 | // This MPC is used by Imposter and SceneDepth remapping material. 120 | UPROPERTY(EditAnywhere) 121 | UMaterialParameterCollection* CaptureMpc; 122 | 123 | UPROPERTY(EditAnywhere, Category=Export) 124 | FString WorkingPath = TEXT("/Game"); 125 | }; 126 | 127 | /** 128 | * 129 | */ 130 | UCLASS(BlueprintType) 131 | class IMPOSTERGENERATOR_API UImposterCore final : public UObject 132 | { 133 | GENERATED_BODY() 134 | 135 | friend class AImposterManagerActor; 136 | 137 | UImposterCore(); 138 | 139 | // Operation guard. 140 | enum class EImposterCoreState : uint8 141 | { 142 | NotInitialized, 143 | Initialized, 144 | Captured, 145 | Exported, 146 | }; 147 | 148 | public: 149 | bool TryAddCapturedActor(AActor* Actor); 150 | void RemoveAllCapturedActors(); 151 | 152 | // 1 - Init 153 | bool Init(const FImposterGeneratorSettings& ImposterGeneratorSettings, 154 | const TArray& CapturedActorArray, 155 | const FString& ImposterNameString); 156 | 157 | // 2 - Capture 158 | void Capture(); 159 | 160 | // 3 - Generate Imposter Object in place 161 | void GenerateImposter(bool bCreateSublevel, const FVector& Offset = FVector::ZeroVector); 162 | 163 | void PreviewImposter(); 164 | 165 | // Debugging functions 166 | void DebugCaptureDirections() const; 167 | void DebugCaptureBounds() const; 168 | 169 | public: // members 170 | 171 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 172 | FImposterGeneratorSettings Settings; 173 | 174 | private: 175 | 176 | // Set by Init() 177 | UPROPERTY() 178 | FString ImposterName = TEXT("Imposter"); 179 | 180 | UPROPERTY() 181 | TArray CapturedActors; 182 | 183 | UPROPERTY() 184 | TArray CaptureDirections; 185 | 186 | UPROPERTY() 187 | FBoxSphereBounds CaptureBounds; 188 | 189 | UPROPERTY() 190 | TMap CapturedTexMap; 191 | 192 | UPROPERTY() 193 | class AImposterCaptureActor* ImposterCaptureActor; 194 | 195 | UPROPERTY() 196 | class UTextureRenderTarget2D* MiddleRT; 197 | 198 | UPROPERTY() 199 | UMaterialParameterCollectionInstance* CaptureMpcInst; 200 | 201 | 202 | public: // static 203 | 204 | static bool CanAddActorToCapture(const AActor* Actor); 205 | 206 | // Just like normal compression & decompression using octahedron 207 | // https://jcgt.org/published/0003/02/01/ 208 | // Engine\Shaders\Private\DeferredShadingCommon.ush 209 | static FVector OctahedronToDirVector(const FVector2D& Grid); 210 | static FVector HemiOctahedronToDirVector(const FVector2D& Grid); 211 | 212 | // See EImposterCaptureType for details. 213 | static ESceneCaptureSource GetSceneCaptureSourceFrom(EImposterCaptureType ImposterCaptureType); 214 | 215 | static FString CaptureTypeEnumToString(EImposterCaptureType Type); 216 | 217 | static void PrefixReplaceContentToGame(FString& Path); 218 | 219 | static class UMaterialInstanceConstant* CreateMIC_EditorOnly(UMaterialInterface* Material, FString InName); 220 | 221 | 222 | private: 223 | // Can be called repeatedly. 224 | void InitTex(); 225 | void InitCaptureBounds(); 226 | void InitCaptureDirections(); 227 | void InitMpc(); 228 | 229 | 230 | // Imposter Root Dir: WorkingPath/ImposterName 231 | // Imposter Actor: BP_ImposterName 232 | // Imposter Package: WorkingPath/ImposterName/BP_ImposterName 233 | // Textures: T_ImposterName_CaptureType 234 | // Texture Package: WorkingPath/ImposterName/T_ImposterName_CaptureType 235 | // Material Instance: MI_ImposterName 236 | // Material Package: WorkingPath/ImposterName/MI_ImposterName 237 | // SubLevel: ImposterName_SubLevel 238 | // SubLevel Package: WorkingPath/ImposterName/ImposterName_SubLevel 239 | 240 | FORCEINLINE FString GetImposterRootDir() const 241 | { 242 | return FPaths::Combine(Settings.WorkingPath, ImposterName); 243 | } 244 | 245 | FORCEINLINE FString GetActorAssetName() const 246 | { 247 | return FString::Printf(TEXT("BP_%s"), *ImposterName); 248 | } 249 | 250 | FORCEINLINE FString GetActorPackageName() const 251 | { 252 | return FPaths::Combine(GetImposterRootDir(), GetActorAssetName()); 253 | } 254 | 255 | FORCEINLINE FString GetTextureAssetName(EImposterCaptureType CaptureType) const 256 | { 257 | return FString::Printf(TEXT("T_%s_%s"), *ImposterName, *CaptureTypeEnumToString(CaptureType)); 258 | } 259 | 260 | FORCEINLINE FString GetTexturePackageName(EImposterCaptureType CaptureType) const 261 | { 262 | return FPaths::Combine(GetImposterRootDir(), GetTextureAssetName(CaptureType)); 263 | } 264 | 265 | FORCEINLINE FString GetMaterialAssetName() const 266 | { 267 | return FString::Printf(TEXT("MI_%s"), *ImposterName); 268 | } 269 | 270 | FORCEINLINE FString GetMaterialPackageName() const 271 | { 272 | return FPaths::Combine(GetImposterRootDir(), GetMaterialAssetName()); 273 | } 274 | 275 | FORCEINLINE FString GetSubLevelAssetName() const 276 | { 277 | return FString::Printf(TEXT("%s_SubLevel"), *ImposterName); 278 | } 279 | 280 | FORCEINLINE FString GetSubLevelPackageName() const 281 | { 282 | return FPaths::Combine(GetImposterRootDir(), GetSubLevelAssetName()); 283 | } 284 | 285 | // Should 286 | bool ShouldInit(bool bShouldLog = true) const; 287 | bool ShouldCapture(bool bShouldLog = true) const; 288 | bool ShouldGenerate(bool bShouldLog = true) const; 289 | 290 | FVector ImposterGridToDirVector(FVector2D Grid) const; 291 | 292 | void Reset(); 293 | 294 | 295 | private: // members 296 | EImposterCoreState ImposterCoreState = EImposterCoreState::NotInitialized; 297 | }; 298 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Public/ImposterGenerator.h: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FToolBarBuilder; 9 | class FMenuBuilder; 10 | 11 | class FImposterGeneratorModule : public IModuleInterface 12 | { 13 | public: 14 | 15 | /** IModuleInterface implementation */ 16 | virtual void StartupModule() override; 17 | virtual void ShutdownModule() override; 18 | 19 | /** This function will be bound to Command (by default it will bring up plugin window) */ 20 | void PluginButtonClicked(); 21 | 22 | private: 23 | 24 | void RegisterMenus(); 25 | 26 | TSharedRef OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs); 27 | 28 | private: 29 | TSharedPtr PluginCommands; 30 | }; 31 | -------------------------------------------------------------------------------- /Source/ImposterGenerator/Public/ImposterGeneratorCommands.h: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Framework/Commands/Commands.h" 7 | #include "ImposterGeneratorStyle.h" 8 | 9 | class FImposterGeneratorCommands : public TCommands 10 | { 11 | public: 12 | 13 | FImposterGeneratorCommands() 14 | : TCommands(TEXT("ImposterGenerator"), NSLOCTEXT("Contexts", "ImposterGenerator", "ImposterGenerator Plugin"), NAME_None, FImposterGeneratorStyle::GetStyleSetName()) 15 | { 16 | } 17 | 18 | // TCommands<> interface 19 | virtual void RegisterCommands() override; 20 | 21 | public: 22 | TSharedPtr< FUICommandInfo > OpenPluginWindow; 23 | }; -------------------------------------------------------------------------------- /Source/ImposterGenerator/Public/ImposterGeneratorStyle.h: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Styling/SlateStyle.h" 7 | 8 | /** */ 9 | class FImposterGeneratorStyle 10 | { 11 | public: 12 | 13 | static void Initialize(); 14 | 15 | static void Shutdown(); 16 | 17 | /** reloads textures used by slate renderer */ 18 | static void ReloadTextures(); 19 | 20 | /** @return The Slate style set for the Shooter game */ 21 | static const ISlateStyle& Get(); 22 | 23 | static FName GetStyleSetName(); 24 | 25 | private: 26 | 27 | static TSharedRef< class FSlateStyleSet > Create(); 28 | 29 | private: 30 | 31 | static TSharedPtr< class FSlateStyleSet > StyleInstance; 32 | }; -------------------------------------------------------------------------------- /Source/ImposterGenerator/Public/ImposterManagerActor.h: -------------------------------------------------------------------------------- 1 | // malosgao presents. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "ImposterCore.h" 7 | #include "GameFramework/Actor.h" 8 | #include "ImposterManagerActor.generated.h" 9 | 10 | UCLASS(BlueprintType, Blueprintable, AutoCollapseCategories=(Debug)) 11 | class IMPOSTERGENERATOR_API AImposterManagerActor final : public AActor 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | AImposterManagerActor(); 17 | 18 | // virtual void OnConstruction(const FTransform& Transform) override; 19 | 20 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Imposter) 21 | FImposterGeneratorSettings Settings; 22 | 23 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Imposter) 24 | FString ImposterName = TEXT("Imposter"); 25 | 26 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Imposter) 27 | TArray> CapturedActors; 28 | 29 | UFUNCTION(BlueprintCallable, CallInEditor, Category=Actions) 30 | void A_AddSceneActors(); 31 | 32 | UFUNCTION(BlueprintCallable, CallInEditor, Category=Actions) 33 | void B_InitCore(); 34 | 35 | UFUNCTION(BlueprintCallable, CallInEditor, Category=Actions) 36 | void C_Capture(); 37 | 38 | UFUNCTION(BlueprintCallable, CallInEditor, Category=Actions) 39 | void D_Generate(); 40 | 41 | UFUNCTION(BlueprintCallable, CallInEditor, Category=Actions) 42 | void E_Preview(); 43 | 44 | UFUNCTION(BlueprintCallable, CallInEditor, Category=Debug) 45 | void DebugCaptureDirections(); 46 | 47 | UFUNCTION(BlueprintCallable, CallInEditor, Category=Debug) 48 | void DebugCaptureBounds(); 49 | 50 | 51 | UPROPERTY(VisibleAnywhere, Transient, Category="Debug|Capture") 52 | TMap CapturedTexMap; 53 | 54 | UPROPERTY(VisibleAnywhere, Transient, Category="Debug|Capture") 55 | UTextureRenderTarget2D* MiddleRT; 56 | 57 | private: 58 | UPROPERTY(Transient) 59 | UImposterCore* Core; 60 | 61 | void CopyDebugFromCore(); 62 | }; 63 | --------------------------------------------------------------------------------