I have an actor that adds both static mesh components and procedural mesh components to itself during BeginPlay. All components register functions defined in that actor to process both BeginCursorOver and EndCursorOver events. The project’s player controller is set to accept cursor over events. During Play, the cursor over callbacks are called for the static mesh components but not the procedural mesh components. Since both component types are derived from the PrimitiveComponent class (which defines OnBeginCursorOver and OnEndCursorOver events) I would expect the two component types to be equivalent.
Is there some aspect of the procedural component class that needs to be asserted for the system to detect the cursor hovering over it and fire these events?
UPDATE: I have found that cursor over events for a static mesh component occurs even when a procedural mesh component is occluding the static mesh in the viewport. Clearly the line-of-sight calculation for objects in the scene are not considering procedural mesh triangles for the mouse cursor.
Not a peep from anybody on this? Anyway, I found a workaround by converting the procedural mesh component to a static mesh and then adding a static mesh component to my actor instead of the procedural mesh component, doing a variation on the code borrowed from the ProceduralMeshComponentDetails.cpp as described in Procedural mesh not saving all of its sections to static mesh - Programming & Scripting - Epic Developer Community Forums . Unfortunately, this takes a lot of cpu time and memory, so it would be better if there was a way to get procedural meshes to succumb to mouse events.
The code follows below for interested parties.
UStaticMesh* AMyActor::MakeStaticMesh(UProceduralMeshComponent* procMeshComp, const char *linkName)
{
FString PackageName = FString(TEXT("/Game/Meshes/")) + ANSI_TO_TCHAR(linkName);
FString Name;
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
FString actorName = procMeshComp->GetOwner()->GetName();
FString levelName = procMeshComp->GetWorld()->GetMapName();
FString assetName = FString(TEXT("SM_")) + levelName + FString(TEXT("_") + actorName);
FString pathName = FString(TEXT("/Game/Meshes/"));
FString packageName = pathName + ANSI_TO_TCHAR(linkName);
UE_LOG(LogTemp, Log, TEXT("%s packageName = %s"), ANSI_TO_TCHAR(linkName), *packageName);
FRawMesh rawMesh;
TArray<UMaterialInterface*> MeshMaterials;
const int32 numSections = procMeshComp->GetNumSections();
UE_LOG(LogTemp, Log, TEXT("%d mesh sections for %s"), numSections, ANSI_TO_TCHAR(linkName));
int32 vertexBase = 0;
for (int32 sectionNdx = 0; sectionNdx < numSections; sectionNdx++)
{
FProcMeshSection* procMeshSection = procMeshComp->GetProcMeshSection(sectionNdx);
// Copy vertices
for (FProcMeshVertex& Vert : procMeshSection->ProcVertexBuffer)
{
rawMesh.VertexPositions.Add(Vert.Position);
}
// Copy 'wedge' info
int32 wedgeCount = procMeshSection->ProcIndexBuffer.Num();
for (int32 wedgeNdx=0; wedgeNdx < wedgeCount; wedgeNdx++)
{
int32 Index = procMeshSection->ProcIndexBuffer[wedgeNdx];
rawMesh.WedgeIndices.Add(Index + vertexBase);
FProcMeshVertex& ProcVertex = procMeshSection->ProcVertexBuffer[Index];
FVector TangentX = ProcVertex.Tangent.TangentX;
FVector TangentZ = ProcVertex.Normal;
FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * (ProcVertex.Tangent.bFlipTangentY ? -1.f : 1.f);
rawMesh.WedgeTangentX.Add(TangentX);
rawMesh.WedgeTangentY.Add(TangentY);
rawMesh.WedgeTangentZ.Add(TangentZ);
rawMesh.WedgeTexCoords[0].Add(ProcVertex.UV0);
rawMesh.WedgeColors.Add(ProcVertex.Color);
}
// copy face info
int32 triangeCount = wedgeCount / 3;
for (int32 triangeNdx=0; triangeNdx < triangeCount; triangeNdx++)
{
rawMesh.FaceMaterialIndices.Add(sectionNdx);
rawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
}
// Remember material
MeshMaterials.Add(procMeshComp->GetMaterial(sectionNdx));
// Update offset for creating one big index/vertex buffer
vertexBase += procMeshSection->ProcVertexBuffer.Num();
}
// If we got some valid data.
if (rawMesh.VertexPositions.Num() > 3 && rawMesh.WedgeIndices.Num() > 3)
{
// Then find/create it.
UPackage* package = CreatePackage(NULL, *packageName);
check(package);
// Create StaticMesh object
UStaticMesh* staticMesh = NewObject<UStaticMesh>(package, FName(*assetName), RF_Public | RF_Standalone);
staticMesh->InitResources();
staticMesh->LightingGuid = FGuid::NewGuid();
// Add source to new StaticMesh
FStaticMeshSourceModel* srcModel = new (staticMesh->SourceModels) FStaticMeshSourceModel();
srcModel->BuildSettings.bRecomputeNormals = false;
srcModel->BuildSettings.bRecomputeTangents = false;
srcModel->BuildSettings.bRemoveDegenerates = false;
srcModel->BuildSettings.bUseHighPrecisionTangentBasis = false;
srcModel->BuildSettings.bUseFullPrecisionUVs = false;
srcModel->BuildSettings.bGenerateLightmapUVs = true;
srcModel->BuildSettings.SrcLightmapIndex = 0;
srcModel->BuildSettings.DstLightmapIndex = 1;
srcModel->RawMeshBulkData->SaveRawMesh(rawMesh);
// Copy materials to new mesh
for (UMaterialInterface* Material : MeshMaterials)
{
staticMesh->StaticMaterials.Add(FStaticMaterial(Material));
}
//Set the Imported version before calling the build
staticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
// Build mesh from source
staticMesh->Build(false);
staticMesh->PostEditChange();
// Notify asset registry of new asset
FAssetRegistryModule::AssetCreated(staticMesh);
return staticMesh;
}
else
return nullptr;
}
This code requires the following includes
#include <Kismet/GameplayStatics.h>
#include <RawMesh/Public/RawMesh.h>
#include <AssetRegistryModule.h>
#include <AssetToolsModule.h>
Hey, it might be a bit late but might work for someone else running into the same issue. I just tested to bind an OnBeginCursorOver event for a Procedural mesh (PM) on BP and is working fine. I set up the PM in my c++ class and then extended it to BP. Make sure the Player Controller has enabled all the mouse events. I hope it helps.
Cheers.
Very simple like this…it works fine for me