Skip to content

Commit f8c5995

Browse files
committed
fix: crash bugs, add UE automation tests, update docs and metadata
- Fix 3 crash bugs in AssetConstructor.cpp: remove check() assertions on failure path and add early return nullptr after setting Failure result in ConstructProceduralMesh/StaticMesh/DynamicMeshComponentFromAssetFile - Fix potential crash in AssetLoader.cpp: add IsEmpty() guard before accessing &AssetData[0] in LoadAiScene to prevent undefined behavior - Add RuntimeAssetImportTest module with UE Automation Tests covering: LoadMeshFromAssetFile with non-existent path, empty path, and LoadMeshFromAssetData with empty array (all should return Failure not crash) - Pin assimp submodule to latest stable release tag - Update RuntimeAssetImport.uplugin: improve description, set Category to Mesh, add RuntimeAssetImportTest module entry - Update README.md: fix trailing backtick typo in git clone command, add known bugs section (multiplayer ProceduralMesh, packaged StaticMesh), add assimp first-build time note, clarify macOS arm64-only support, add Content folder asset descriptions, add Running Tests section
1 parent f5f81a4 commit f8c5995

8 files changed

Lines changed: 110 additions & 8 deletions

File tree

README.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
This is a plugin for the Unreal Engine to load 3D assets (e.g. FBX) at runtime. Made with UE5.4.2.
44

55
## Prerequisites
6-
- Windows is the primary supported platform. macOS and Linux support is experimental (build scripts are present but not officially tested).
6+
- Windows is the primary supported platform. macOS (arm64 only; x86_64 not supported) and Linux support is experimental.
77
- Download and install [CMake](https://cmake.org/)
88
The check box "Add CMake to the PATH environment variable" that appears during installation must be checked.
99

10+
> **Note:** The first build of the assimp library (a C++ dependency) takes approximately 5 or more minutes. Subsequent builds are fast. This is expected behavior.
11+
1012
## How to install
1113
Clone this repository with its submodules into the Plugins folder (or create your own if you don't have one) in the folder of the Unreal Engine project where you want to install this plugin by doing one of the following:
1214
<details>
@@ -25,7 +27,7 @@ Clone this repository with its submodules into the Plugins folder (or create you
2527
2. In the Plugins folder of the project where you want to install this plug-in, start a command prompt.
2628
3. Execute
2729
```
28-
git clone --recursive URL`
30+
git clone --recursive URL
2931
```
3032
Put the URL of this repository in the URL field.
3133
</details>
@@ -43,6 +45,30 @@ The following file formats are supported via assimp:
4345
- DAE (Collada)
4446
- Other formats supported by assimp
4547

48+
## Content Folder Assets
49+
50+
The plugin's `Content` folder includes the following assets:
51+
52+
- **`AssetImporterMeshMaterial.uasset`** — Parent material used by the plugin. Exposes the following parameters:
53+
- `TextureBlendIntensityForBaseColor` — blend intensity between texture and base color
54+
- `BaseColor4` — base color as a 4-component vector
55+
- `BaseColorTexture` — texture input for base color
56+
- **`purple.uasset`** — Sample purple texture for testing and demonstration purposes.
57+
58+
## Known Bugs
59+
60+
- **Multiplayer (ProceduralMesh):** Clients may experience abnormal movement accompanied by a `LogNetPackageMap` warning. Use `DynamicMeshComponent` instead as a workaround.
61+
- **Packaged game materials (StaticMesh):** Materials display as a checkerboard pattern in packaged builds. Use `DynamicMeshComponent` instead as a workaround.
62+
63+
## Running Tests
64+
65+
To run from the command line:
66+
```
67+
UnrealEditor.exe <YourProject>.uproject -ExecCmds="Automation RunTests RuntimeAssetImport" -unattended -nopause -log -testexit="Automation Test Queue Empty"
68+
```
69+
70+
Or from the UE Editor UI: **Window > Test Automation**, search `RuntimeAssetImport`, click **Start Tests**.
71+
4672
## Known Limitations
4773
- Only embedded textures are loaded (external texture file references are not yet supported)
4874
- Only 1 texture per material is supported

RuntimeAssetImport.uplugin

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"Version": 1,
44
"VersionName": "1.0",
55
"FriendlyName": "RuntimeAssetImport",
6-
"Description": "Plugin to load mesh at runtime.",
7-
"Category": "Other",
6+
"Description": "Runtime mesh loading plugin for Unreal Engine 5. Supports FBX, glTF/GLB, OBJ, DAE and other formats via the Assimp library.",
7+
"Category": "Mesh",
88
"EngineVersion": "5.4.0",
99
"CreatedBy": "Udon-Tobira",
1010
"CreatedByURL": "https://github.com/metyatech",
@@ -29,6 +29,11 @@
2929
"Name": "RuntimeAssetImport",
3030
"Type": "Runtime",
3131
"LoadingPhase": "PreDefault"
32+
},
33+
{
34+
"Name": "RuntimeAssetImportTest",
35+
"Type": "Developer",
36+
"LoadingPhase": "Default"
3237
}
3338
],
3439
"Plugins": [

Source/RuntimeAssetImport/Private/AssetConstructor.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ UProceduralMeshComponent*
100100
if (ELoadMeshFromAssetFileResult::Failure == LoadMeshFromAssetFileResult) {
101101
ConstructProceduralMeshComponentFromAssetFileResult =
102102
EConstructProceduralMeshComponentFromAssetFileResult::Failure;
103+
return nullptr;
103104
}
104-
check(ELoadMeshFromAssetFileResult::Success == LoadMeshFromAssetFileResult);
105105

106106
// assume the result is success
107107
ConstructProceduralMeshComponentFromAssetFileResult =
@@ -135,8 +135,8 @@ UStaticMeshComponent*
135135
if (ELoadMeshFromAssetFileResult::Failure == LoadMeshFromAssetFileResult) {
136136
ConstructStaticMeshComponentFromAssetFileResult =
137137
EConstructStaticMeshComponentFromAssetFileResult::Failure;
138+
return nullptr;
138139
}
139-
check(ELoadMeshFromAssetFileResult::Success == LoadMeshFromAssetFileResult);
140140

141141
// assume the result is success
142142
ConstructStaticMeshComponentFromAssetFileResult =
@@ -170,8 +170,8 @@ UDynamicMeshComponent*
170170
if (ELoadMeshFromAssetFileResult::Failure == LoadMeshFromAssetFileResult) {
171171
ConstructDynamicMeshComponentFromAssetFileResult =
172172
EConstructDynamicMeshComponentFromAssetFileResult::Failure;
173+
return nullptr;
173174
}
174-
check(ELoadMeshFromAssetFileResult::Success == LoadMeshFromAssetFileResult);
175175

176176
// assume the result is success
177177
ConstructDynamicMeshComponentFromAssetFileResult =

Source/RuntimeAssetImport/Private/AssetLoader.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ static const aiScene* LoadAiScene(Assimp::Importer& AiImporter,
156156

157157
static const aiScene* LoadAiScene(Assimp::Importer& AiImporter,
158158
const TArray<uint8>& AssetData) {
159+
if (AssetData.IsEmpty()) {
160+
return nullptr;
161+
}
159162
// import
160163
return AiImporter.ReadFileFromMemory(&AssetData[0], AssetData.Num(),
161164
AiImportFlags);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include "AssetLoader.h"
2+
#include "CoreMinimal.h"
3+
#include "Misc/AutomationTest.h"
4+
5+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
6+
FLoadMeshFromAssetFile_NonExistentPath_ReturnsFailure,
7+
"RuntimeAssetImport.AssetLoader.LoadMeshFromAssetFile.NonExistentPath",
8+
EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter)
9+
10+
bool FLoadMeshFromAssetFile_NonExistentPath_ReturnsFailure::RunTest(const FString& Parameters)
11+
{
12+
ELoadMeshFromAssetFileResult Result;
13+
FLoadedMeshData MeshData = UAssetLoader::LoadMeshFromAssetFile(
14+
TEXT("C:/nonexistent/path/does_not_exist.fbx"), Result);
15+
16+
TestEqual(
17+
TEXT("Loading a non-existent file should return Failure without crashing"),
18+
Result, ELoadMeshFromAssetFileResult::Failure);
19+
return true;
20+
}
21+
22+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
23+
FLoadMeshFromAssetFile_EmptyPath_ReturnsFailure,
24+
"RuntimeAssetImport.AssetLoader.LoadMeshFromAssetFile.EmptyPath",
25+
EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter)
26+
27+
bool FLoadMeshFromAssetFile_EmptyPath_ReturnsFailure::RunTest(const FString& Parameters)
28+
{
29+
ELoadMeshFromAssetFileResult Result;
30+
FLoadedMeshData MeshData = UAssetLoader::LoadMeshFromAssetFile(TEXT(""), Result);
31+
32+
TestEqual(
33+
TEXT("Loading with an empty path should return Failure without crashing"),
34+
Result, ELoadMeshFromAssetFileResult::Failure);
35+
return true;
36+
}
37+
38+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
39+
FLoadMeshFromAssetData_EmptyArray_ReturnsFailure,
40+
"RuntimeAssetImport.AssetLoader.LoadMeshFromAssetData.EmptyArray",
41+
EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter)
42+
43+
bool FLoadMeshFromAssetData_EmptyArray_ReturnsFailure::RunTest(const FString& Parameters)
44+
{
45+
ELoadMeshFromAssetDataResult Result;
46+
TArray<uint8> EmptyData;
47+
FLoadedMeshData MeshData = UAssetLoader::LoadMeshFromAssetData(EmptyData, Result);
48+
49+
TestEqual(
50+
TEXT("Loading from an empty byte array should return Failure without crashing"),
51+
Result, ELoadMeshFromAssetDataResult::Failure);
52+
return true;
53+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "Modules/ModuleManager.h"
2+
3+
IMPLEMENT_MODULE(FDefaultModuleImpl, RuntimeAssetImportTest)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using UnrealBuildTool;
2+
3+
public class RuntimeAssetImportTest : ModuleRules
4+
{
5+
public RuntimeAssetImportTest(ReadOnlyTargetRules Target) : base(Target)
6+
{
7+
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
8+
9+
PublicDependencyModuleNames.AddRange(new string[] { "Core", "RuntimeAssetImport" });
10+
PrivateDependencyModuleNames.AddRange(new string[] { "CoreUObject", "Engine" });
11+
}
12+
}

Source/ThirdParty/assimp/assimp

Submodule assimp updated 1036 files

0 commit comments

Comments
 (0)