Download

What happened to USkeletalMesh in UE 4.19?

Haven’t you set a breakpoint right on PostLoad() and followed the execution step by step with the debugger? I haven’t read all your code and I don’t know precisely you are trying to achieve there, but if you want to update the render data you could try

SkeletalMesh->ReleaseResources();
SkeletalMesh->InitResources();

But I think it’s best to call ReleaseResources() before making any changes to the data and to stop the render thread first with FSuspendRenderingThread() or some other method. At least that was the best approach I could find in earlier versions. I used to have crashes (although rarely) until I started doing it this way.

Anyway, Proteus is reporting the same linker error, so I would have liked to know what includes you are using in that .cpp file and what modules you have listed in your Build.cs. I’m gonna stop my porting efforts now. Maybe Epic will fix this half-***** new RenderData system and provide some user-friendly access to the vertex buffers. Until then they can keep 4.19. I hate this “If it’s not Blueprint, you can break it without explanation” philosophy.

I have the linker error on [FONT=courier new]AllocateData ONLY if I try to use the [FONT=courier new]operator= in editor; with the check [FONT=courier new]#if WITH_EDITOR/#else/#endif I don’t have that error, because the [FONT=courier new]operator= override is used only in the non-editor build. When compiling for non-editor I don’t have the linker error but another runtime error.

I DID IT!
I share with you what I did, in addition to what I said before.

1 - Call [FONT=courier new]ReleaseResources() and [FONT=courier new]InitResources() (thanks @MaxPower42 ) in the same point of [FONT=courier new]PostEditChange for in-editor build (actually they may be called for both builds, instead of [FONT=courier new]PostEditChange)
2 - An error occurs in SkeletalMeshLODRenderData.cpp, at line 161


check(RenderSection.DuplicatedVerticesBuffer.DupVertData.Num());

so I modified the LodRenderData vertices job this way (see “— HERE —” blocks):


    LodRenderData->StaticVertexBuffers.PositionVertexBuffer.Init(data->vertexCount);
    LodRenderData->StaticVertexBuffers.ColorVertexBuffer.Init(data->vertexCount);
    LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.Init(data->vertexCount, 1);

    TArray<TSkinWeightInfo<true>> InWeights;
    InWeights.AddUninitialized(data->vertexCount);
    // --- HERE ---
    TMap<int32, TArray<int32>> OverlappingVertices;
    // ------
    for (uint32_t VertIndex = 0; VertIndex < data->vertexCount; VertIndex++, SourceVertex++)
    {
        FModelVertex ModelVertex;
        ModelVertex.Position = 100.0f * FVector(-SourceVertex->z, SourceVertex->x, SourceVertex->y);
        BoundBox += ModelVertex.Position;

        FVector n = FVector(-SourceVertex->nz, SourceVertex->nx, SourceVertex->ny);
        FVector t = FVector(-SourceVertex->tz, SourceVertex->tx, SourceVertex->ty);
        FVector bt = FVector::CrossProduct(t, n) * FMath::Sign(SourceVertex->tw);
        ModelVertex.TangentX = FPackedNormal(t);
        ModelVertex.TangentZ = FPackedNormal(n);
        ModelVertex.TexCoord = FVector2D(SourceVertex->u, SourceVertex->v);

        LodRenderData->StaticVertexBuffers.PositionVertexBuffer.VertexPosition(VertIndex) = ModelVertex.Position;
        LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(VertIndex, ModelVertex.TangentX, ModelVertex.GetTangentY(), ModelVertex.TangentZ);
        LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexUV(VertIndex, 0, ModelVertex.TexCoord);

        // --- HERE ---
        TArray<int32> Vertices;
        // ------
        for (uint32_t BlendIndex = 0; BlendIndex < MAX_TOTAL_INFLUENCES; BlendIndex++)
        {
            InWeights[VertIndex].InfluenceWeights[BlendIndex] = BlendIndex < NumBlendWeights ? (uint8_t)(255.0f*SourceVertex->blendWeights[BlendIndex]) : 0;
            InWeights[VertIndex].InfluenceBones[BlendIndex] = BlendIndex < NumBlendWeights ? SourceVertex->blendIndices[BlendIndex] : 0;
            // --- HERE ---
            Vertices.Add(BlendIndex < NumBlendWeights ? SourceVertex->blendIndices[BlendIndex] : 0);
            // ------
        }
        OverlappingVertices.Add(VertIndex, Vertices);
    }
    LodRenderData->SkinWeightVertexBuffer.SetHasExtraBoneInfluences(true);
    LodRenderData->SkinWeightVertexBuffer = InWeights;
    // --- HERE ---
    RenderMeshSection.DuplicatedVerticesBuffer.Init(data->vertexCount, OverlappingVertices);
    // ------

3 - Another error occurs in SkeletalMesh.cpp at line 679:


ensure(Materials[MaterialIndex].UVChannelData.bInitialized);

so, I had to force that boolean to be true (I will investigate on how to make it true without forcing it)


SkeletalMesh->Materials[0].UVChannelData.bInitialized = true;

Now it works also for non-editor build!

Anyway, I hope UE team will do some work around this problem.

Thanks for sharing, although I still believe you can just use release/initResources instead of invalidate, which doesn’t require modifying the engine. Don’t know if you have tried that.

Good find regarding the linker error in editor/non-editor builds. This isn’t really acceptable though. How am I supposed to test and develop my project, if I can’t run my game code in the editor environment??

Can you Epic ppl please fix this sometime soon? Can you at least confirm that it has come to your attention and needs fixing? And can you please make the render data and their vertex buffers more user-friendly in general? Some ppl actually still code…

You are right, it also works by calling [FONT=courier new]ReleaseResources and [FONT=courier new]InitResources. I had tried it, but without the engine’s source code I did not see the other errors, but only a crash on my own code, and so I thought the problem still was the missing call to [FONT=courier new]InvalidateRenderData.
That is great :slight_smile: Many thanks!

I modified my latest post accordingly.

No problem, just remember that you may need to stop the render thread when releasing/initialising resources. Like I said, I used to have rare crashes until I started using FSuspendRenderingThread() whenever I was about to change render data. I guess it’s going well 95% of the times until one time the render thread is just trying to access the data when you are releasing it, causing a crash.

@MaxPower42 @masmil1988 Have an idea why the crash at runtime?

1)Build succeed;
2)Works at runtime (packaged build);
3)In editor, crash as usual:
Fatal error: [File:D:\Build++UE4+Release-4.19+Compile\Sync\Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp] [Line: 819]
Rendering thread exception:
Assertion failed: (Index >= 0) & (Index < ArrayNum) [File:D:\Build++UE4+Release 4.19+Compile\Sync\Engine\Source\Runtime\Core\Public\Containers/Array.h] [Line: 610]
Error: Array index out of bounds: 1 from an array of size 1

header file #include:
#include “GameFramework/Pawn.h”
#include “OVR_Avatar.h”
#include “Set.h”
#include “OVR_Plugin.h”
#include “OVR_Microphone.h”
#include “Engine/SkeletalMesh.h”
#include “Components/MeshComponent.h”
#include “Components/ActorComponent.h”
#include “OvrAvatar.generated.h”

.cpp file #include:
#include “OvrAvatar.h”
#include “OvrAvatarPCH.h”
#include “OvrAvatarManager.h”
#include “Private/OvrAvatarHelpers.h”
#include “Components/PoseableMeshComponent.h”
#include “IOculusHMDModule.h”
#include “IOculusInputModule.h”
#include “OVR_Voip_LowLevel.h”
#include “Rendering/SkeletalMeshLODModel.h”
#include “Engine/SkeletalMesh.h”
#include “SkeletalMeshModel.h”
#include “SkeletalMeshRenderData.h”
#include “SkeletalMeshLODRenderData.h”

The function of interest:

void UOvrAvatar::LoadMesh(USkeletalMesh* SkeletalMesh, const ovrAvatarMeshAssetData* data)
{
UE_LOG(LogAvatars, Warning, TEXT("[Avatars] Loaded Mesh."));

#if WITH_EDITOR
FSkeletalMeshLODModel* LodModel = new FSkeletalMeshLODModel();
SkeletalMesh->GetImportedModel()->LODModels.Add(LodModel);
#else
FSkeletalMeshLODRenderData* LodRenderData = new FSkeletalMeshLODRenderData();
SkeletalMesh->AllocateResourceForRendering();
SkeletalMesh->GetResourceForRendering()->LODRenderData.Add(LodRenderData);
#endif

#if WITH_EDITOR
new(LodModel->Sections) FSkelMeshSection();
LodModel->Sections[0].MaterialIndex = 0;
LodModel->Sections[0].BaseIndex = 0;
LodModel->Sections[0].NumTriangles = 0;
#else
new(LodRenderData->RenderSections) FSkelMeshRenderSection();
LodRenderData->RenderSections[0].MaterialIndex = 0;
LodRenderData->RenderSections[0].BaseIndex = 0;
LodRenderData->RenderSections[0].NumTriangles = 0;
#endif

SkeletalMesh-&gt;LODInfo.Add(FSkeletalMeshLODInfo());
FSkeletalMeshLODInfo& LodInfo = SkeletalMesh-&gt;LODInfo[0];

LodInfo.ScreenSize = 0.3f;
LodInfo.LODHysteresis = 0.2f;

//LodInfo.TriangleSortSettings.Add(FTriangleSortSettings());
LodInfo.LODMaterialMap.Add(0);

SkeletalMesh-&gt;Materials.Add(UMaterial::GetDefaultMaterial(MD_Surface));
//SkeletalMesh-&gt;RefSkeleton.Allocate(data-&gt;skinnedBindPose.jointCount);

SkeletalMesh-&gt;bUseFullPrecisionUVs = true;
SkeletalMesh-&gt;bHasBeenSimplified = false;
SkeletalMesh-&gt;bHasVertexColors = false;
SkeletalMesh-&gt;Materials[0].UVChannelData.bInitialized = true;

for (int BoneIndex = 0; BoneIndex &lt; (int)data-&gt;skinnedBindPose.jointCount; BoneIndex++)
{

#if WITH_EDITOR
LodModel->RequiredBones.Add(BoneIndex);
LodModel->ActiveBoneIndices.Add(BoneIndex);
LodModel->Sections[0].BoneMap.Add(BoneIndex);
#else
LodRenderData->RequiredBones.Add(BoneIndex);
LodRenderData->ActiveBoneIndices.Add(BoneIndex);
LodRenderData->RenderSections[0].BoneMap.Add(BoneIndex);
#endif

    FString BoneString = data-&gt;skinnedBindPose.jointNames[BoneIndex];
    FName BoneName = FName(*BoneString);

    FTransform Transform = FTransform::Identity;
    OvrAvatarHelpers::ConvertTransform(data-&gt;skinnedBindPose.jointTransform[BoneIndex], Transform);

    FReferenceSkeletonModifier Modifier = FReferenceSkeletonModifier(SkeletalMesh-&gt;RefSkeleton, nullptr);
    Modifier.Add(FMeshBoneInfo(BoneName, BoneString, data-&gt;skinnedBindPose.jointParents[BoneIndex]), Transform);
}

check(data-&gt;indexCount % 3 == 0);
check(data-&gt;vertexCount &gt; 0);

#if WITH_EDITOR
auto& MeshSection = LodModel->Sections[0];
MeshSection.BaseIndex = 0;
MeshSection.NumTriangles = data->indexCount / 3;
MeshSection.BaseVertexIndex = 0;
MeshSection.NumVertices = data->vertexCount;
MeshSection.MaxBoneInfluences = 4;
#else
auto& RenderMeshSection = LodRenderData->RenderSections[0];
RenderMeshSection.BaseIndex = 0;
RenderMeshSection.NumTriangles = data->indexCount / 3;
RenderMeshSection.BaseVertexIndex = 0;
RenderMeshSection.NumVertices = data->vertexCount;
RenderMeshSection.MaxBoneInfluences = 4;
#endif

const ovrAvatarMeshVertex* SourceVertex = data-&gt;vertexBuffer;
const uint32_t NumBlendWeights = 4;

FBox BoundBox = FBox();
BoundBox.Init();

#if WITH_EDITOR
MeshSection.SoftVertices.SetNumUninitialized(data->vertexCount);
FSoftSkinVertex* DestVertex = MeshSection.SoftVertices.GetData();

for (uint32_t VertIndex = 0; VertIndex &lt; data-&gt;vertexCount; VertIndex++, SourceVertex++, DestVertex++)
{
    DestVertex-&gt;Position = 100.0f * FVector(-SourceVertex-&gt;z, SourceVertex-&gt;x, SourceVertex-&gt;y);

    BoundBox += DestVertex-&gt;Position;

    FVector n = FVector(-SourceVertex-&gt;nz, SourceVertex-&gt;nx, SourceVertex-&gt;ny);
    FVector t = FVector(-SourceVertex-&gt;tz, SourceVertex-&gt;tx, SourceVertex-&gt;ty);
    FVector bt = FVector::CrossProduct(t, n) * FMath::Sign(SourceVertex-&gt;tw);
    DestVertex-&gt;TangentX = FPackedNormal(t);
    DestVertex-&gt;TangentY = FPackedNormal(bt);
    DestVertex-&gt;TangentZ = FPackedNormal(n);
    DestVertex-&gt;UVs[0] = FVector2D(SourceVertex-&gt;u, SourceVertex-&gt;v);

    for (uint32_t BlendIndex = 0; BlendIndex &lt; MAX_TOTAL_INFLUENCES; BlendIndex++)
    {
        DestVertex-&gt;InfluenceWeights[BlendIndex] = BlendIndex &lt; NumBlendWeights ? (uint8_t)(255.0f*SourceVertex-&gt;blendWeights[BlendIndex]) : 0;
        DestVertex-&gt;InfluenceBones[BlendIndex] = BlendIndex &lt; NumBlendWeights ? SourceVertex-&gt;blendIndices[BlendIndex] : 0;
    }
}

#else
LodRenderData->StaticVertexBuffers.PositionVertexBuffer.Init(data->vertexCount);
LodRenderData->StaticVertexBuffers.ColorVertexBuffer.Init(data->vertexCount);
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.Init(data->vertexCount, 1);

TArray&lt;TSkinWeightInfo&lt;true&gt;&gt; InWeights;
InWeights.AddUninitialized(data-&gt;vertexCount);
TMap&lt;int32, TArray&lt;int32&gt;&gt; OverlappingVertices;

for (uint32_t VertIndex = 0; VertIndex &lt; data-&gt;vertexCount; VertIndex++, SourceVertex++)
{
    FModelVertex ModelVertex;
    ModelVertex.Position = 100.0f * FVector(-SourceVertex-&gt;z, SourceVertex-&gt;x, SourceVertex-&gt;y);
    BoundBox += ModelVertex.Position;

    FVector n = FVector(-SourceVertex-&gt;nz, SourceVertex-&gt;nx, SourceVertex-&gt;ny);
    FVector t = FVector(-SourceVertex-&gt;tz, SourceVertex-&gt;tx, SourceVertex-&gt;ty);
    FVector bt = FVector::CrossProduct(t, n) * FMath::Sign(SourceVertex-&gt;tw);
    ModelVertex.TangentX = FPackedNormal(t);
    ModelVertex.TangentZ = FPackedNormal(n);
    ModelVertex.TexCoord = FVector2D(SourceVertex-&gt;u, SourceVertex-&gt;v);

    LodRenderData-&gt;StaticVertexBuffers.PositionVertexBuffer.VertexPosition(VertIndex) = ModelVertex.Position;
    LodRenderData-&gt;StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(VertIndex, ModelVertex.TangentX, ModelVertex.GetTangentY(), ModelVertex.TangentZ);
    LodRenderData-&gt;StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexUV(VertIndex, 0, ModelVertex.TexCoord);
    TArray&lt;int32&gt; Vertices;

    for (uint32_t BlendIndex = 0; BlendIndex &lt; MAX_TOTAL_INFLUENCES; BlendIndex++)
    {
        InWeights[VertIndex].InfluenceWeights[BlendIndex] = BlendIndex &lt; NumBlendWeights ? (uint8_t)(255.0f*SourceVertex-&gt;blendWeights[BlendIndex]) : 0;
        InWeights[VertIndex].InfluenceBones[BlendIndex] = BlendIndex &lt; NumBlendWeights ? SourceVertex-&gt;blendIndices[BlendIndex] : 0;
        Vertices.Add(BlendIndex &lt; NumBlendWeights ? SourceVertex-&gt;blendIndices[BlendIndex] : 0);
    }
    OverlappingVertices.Add(VertIndex, Vertices);
}
//    LodRenderData-&gt;SkinWeightVertexBuffer.SetNeedsCPUAccess(true);
LodRenderData-&gt;SkinWeightVertexBuffer.SetHasExtraBoneInfluences(true);
LodRenderData-&gt;SkinWeightVertexBuffer = InWeights;
RenderMeshSection.DuplicatedVerticesBuffer.Init(data-&gt;vertexCount, OverlappingVertices);
LodRenderData-&gt;StaticVertexBuffers.ColorVertexBuffer.InitFromSingleColor(FColor::Blue, data-&gt;vertexCount);

#endif

#if WITH_EDITOR
LodModel->NumVertices = data->vertexCount;
LodModel->NumTexCoords = 1;
#else
LodRenderData->MultiSizeIndexContainer.CreateIndexBuffer(sizeof(uint16_t));
#endif

for (uint32_t index = 0; index &lt; data-&gt;indexCount; index++)
{

#if WITH_EDITOR
LodModel->IndexBuffer.Add(data->indexBuffer[index]);
#else
LodRenderData->MultiSizeIndexContainer.GetIndexBuffer()->AddItem(data->indexBuffer[index]);
#endif
}

FBoxSphereBounds Bounds(BoundBox);
Bounds = Bounds.ExpandBy(100000.0f);
SkeletalMesh-&gt;SetImportedBounds(Bounds);

//const uint32 vert_flags = ESkeletalMeshVertexFlags::None | ESkeletalMeshVertexFlags::UseFullPrecisionUVs;
//LodModel-&gt;BuildVertexBuffers(vert_flags);

SkeletalMesh-&gt;ReleaseResources();
SkeletalMesh-&gt;InitResources();

SkeletalMesh-&gt;Skeleton = NewObject&lt;USkeleton&gt;();
SkeletalMesh-&gt;Skeleton-&gt;MergeAllBonesToBoneTree(SkeletalMesh);
SkeletalMesh-&gt;PostLoad();

}

@MaxPower42 @Proteus
Actually I’m noticing I have a crash in editor if I replace [FONT=courier new]InvalidateRenderData() with [FONT=courier new]ReleaseResources()/[FONT=courier new]InitResources(), also if I call [FONT=courier new]FSuspendRenderingThread().

The crash is in GPUSkinVertexFactory.cpp at line 287


const FMatrix& RefToLocal = ReferenceToLocalMatrices[RefToLocalIdx];

@Proteus I would really try adding “FSuspendRenderingThread suspend();” at the top of your function and see if it helps.

@masmil1988 That’s strange. I never had any problems with either of those and I’ve been using them reliably since many, many versions ago. I couldn’t do much testing with 4.19 yet, though. What little testing I could do, however, didn’t produce any errors related to those functions so far.

@MaxPower42 added FSuspendRenderingThread suspend(); changes nothing

State of the project as of today:

Editor: Crashes for Array index out of bounds: 0 from an array of size 0, at LoadMesh
Runtime: Works, but load default (ghost) Avatar; not able to load requested Avatar (see pic)

Seems that there’s still work to do to make it works on both editor and runtime

How to contribute ? Can I access the code to help (without any guaranty of a positive result).
Thanks @Proteus

Can someone please post here, in case Epic ever fix the linker error related to calling the assignment operator

LodRenderData->SkinWeightVertexBuffer = InWeights;

at runtime? … Or if they add an Init(count) function like for all the other buffers? Because I’m not going to port while I can’t run in-game code in PIE.

Ok guys let’s nail it. Cross-post with Oculus forums.

4.19 version compared with 4.18 version:
First problem: runtime; Avatar is displayed but only neutral material is applied
1)Material for Body_0, Alpha Texture wrong ID; only one layer instead of 5
2)Material for Body_1, Alpha Texture wrong ID; only one layer instead of 5
3)Material for Body_3 absent
4)Material for hand_left_0 and hand_right_0 Alpha Texture wrong, baseMaskType and MaskType None instead of FRESNEL, only one layer instead of 5

Second problem: editor; Crashes for Array index out of bounds: 0 from an array of size 0, at LoadMesh

Avatars for 4.18 (working) found at https://github.com/ProteusVRpublic/Avatars18
Avatars for 4.19 (with above problems) found at https://github.com/ProteusVRpublic/Avatars19
Avatars for 4.19, Oculus github version, commit 8f6e68f (with same above problems) found at https://github.com/ProteusVRpublic/Avatars19_Oculus
To save time, packaged build of each version found at Microsoft OneDrive - Access files anywhere. Create docs with free Office Online.

Any breakthrough is welcome!

Would it be too much to ask for a statement from the Epic team? There is runtime code that doesn’t compile in editor-builds… Is this intended? Are you going to fix it?

what happened FSkeletalMeshVertexBuffer unreal 4.19

code unreal 4.18

FSkeletalMeshVertexBuffer& buffer = smComponent->GetSkeletalMeshResource()->LODModels[0].VertexBufferGPUSkin;

but unreal 4.19 FSkeletalMeshVertexBuffer is undefined

Proteus, your code is much appreciated. I am also stuck on this upgrade. I wanted to point out that in your OculusAvatarFunctionLibrary.cpp from your github 4.19, you have


if (OVRP_SUCCESS(ovrp_GetNodePoseState3(ovrpStep_Render,0, OculusHMD::ToOvrpNode(ETrackedDeviceType::HMD), &(TrackingState->HeadPose))))


on line 16. It should be


if (OVRP_SUCCESS(ovrp_GetNodePoseState3(ovrpStep_Render,-1, OculusHMD::ToOvrpNode(ETrackedDeviceType::HMD), &(TrackingState->HeadPose))))


Parameter 2 is the frame index on this new version of GetNodePoseState and at 0 it only updates once a second. The -1 in parameter 2 is OVRP_CURRENT_FRAMEINDEX which makes it track smoothly by always pulling up the current frame. Perhaps this info will save you a few minutes.

Sorry if this is off the topic of the USkeletalMesh change but it seems like several people here are all trying to do what Oculus should be doing and upgrade the Oculus Avatar API.

Also, thanks MaxPower, masmil and everyone else. This thread is very helpful but I still wasn’t able to get Avatars going in 4.19 as it was crashing for me. Falling back on 4.18 for now but the Oculus repo of 4.18 has this new API for tracking.

@tomotor Thanks. Also, it is (again) different in latest UE4.19-Oculus branch.

Anyway guys listen all: I’ve had confirmation from Oculus Avatar team that nothing will be done for Unreal and the Avatars as we know them. HOWEVER it has been confirmed that a NEW plugin will be available for SDK 1.26 (currently at 1.24), together with the new flavor of Oculus Avatars. Count on me to test and implement it ASAP. I would expect that somewhere mid-summer.

So, I submitted a bug report for the linker error produced by SkinWeightVertexBuffer::Init(…) in editor builds, that forces you to use preprocessor-based different approaches for editor vs no-editor builds. I was eventually told that

"In order to use the FSkinWeightVertexBuffer you would need to mark it up with ENGINE_API.

ENGINE_API FSkinWeightVertexBuffer buffer;"

But I couldn’t make it work and haven’t heard anything else yet. No matter how and where I put this line, it doesn’t fix the linker errors and gives me more compiler errors when used inside a function. The logical use for me would be as a reference “ENGINE_API FSkinWeightVertexBuffer &buffer = LODRenderData->…”. But this just produces a compile-time error (something DLL-related). Next I tried using it as a declaration only “ENGINE_API FSkinWeightVertexBuffer;”. It compiles, but it doesn’t fix the original linker error…

Can anyone clarify what I’m supposed to do here??

I believe they meant that you must make the engine FSkinWeightVertexBuffer class exported as ENGINE_API.

In Engine\Source\Runtime\Engine\Public\Rendering\SkinWeightVertexBuffer.h, find this “class FSkinWeightVertexBuffer : public FVertexBuffer” and change it to “ENGINE_API class FSkinWeightVertexBuffer : public FVertexBuffer”, then save the header file and rebuild the engine.

Sorry, haven’t seen your reply earlier. Yeah, it took me a while to realize I was supposed to modify the engine itself for this… So far, I’ve always found some way without changing the engine, for the sake of future compatibility. However, I still got either compile time errors or the same linker errors. I tried putting the ENGINE_API in front of the class declaration or the function declaration.

Now I’m probably just gonna make the FWeightData (I think that’s what it was called…) public, so I can access it directly.