Hello all,
I’ve some simple code to create dynamically a StaticMesh from the BeginPlay of a C++ AActor class.
Unfortunately, the Static Mesh can receive shadow from other geometry but is unable to cast shadow.
I know that the problem comes from the initialisation of the StaticMesh because if instead of building it I load it from an asset (C:/Program Files/Epic Games/UE_5.1/Engine/Content/BasicShapes/Sphere.uasset) it works as expected.
Does anyone have an idea?
Cheers!
Here is a minimum relevant code snipset:
void AMyDynActor::CreateSphere(std::vector<FVector3f>& Vertices, std::vector<FVector3f>& Normals, std::vector<FVector2f>& UVs, std::vector<int32>& Triangles, float radius)
{
int sectorCount = 32;
int stackCount = 32;
// build vertices of sphere with smooth shading using parametric equation
// x = r * cos(u) * cos(v)
// y = r * cos(u) * sin(v)
// z = r * sin(u)
// where u: stack(latitude) angle (-90 <= u <= 90)
// v: sector(longitude) angle (0 <= v <= 360)
float x, y, z, xy; // vertex position
float s, t; // texCoord
float sectorStep = 2 * PI / sectorCount;
float stackStep = PI / stackCount;
float sectorAngle, stackAngle;
for (int i = 0; i <= stackCount; ++i)
{
stackAngle = PI / 2 - i * stackStep; // starting from pi/2 to -pi/2
xy = radius * cosf(stackAngle); // r * cos(u)
z = radius * sinf(stackAngle); // r * sin(u)
// add (sectorCount+1) vertices per stack
// the first and last vertices have same position and normal, but different tex coords
for (int j = 0; j <= sectorCount; ++j)
{
sectorAngle = j * sectorStep; // starting from 0 to 2pi
// vertex position
x = xy * cosf(sectorAngle); // r * cos(u) * cos(v)
y = xy * sinf(sectorAngle); // r * cos(u) * sin(v)
FVector3f pos(x, y, z);
Vertices.push_back(pos);
pos.Normalize();
Normals.push_back(pos);
// vertex tex coord between [0, 1]
s = 1.0 - (float)j / sectorCount;
t = (float)i / stackCount;
UVs.push_back(FVector2f(s, t));
}
}
//build grid
//id-sectorCount-1 id-sectorCount
// ----
// | / |
// | / |
// ----
//id-1 id
for (int i = 1; i <= stackCount; ++i)
{
for (int j = 1; j <= sectorCount; ++j)
{
int id0 = i + j * stackCount;
Triangles.push_back(id0 - sectorCount);
Triangles.push_back(id0 - 1);
Triangles.push_back(id0 - sectorCount - 1);
Triangles.push_back(id0);
Triangles.push_back(id0 - 1);
Triangles.push_back(id0 - sectorCount);
}
}
}
void AMyDynActor::FillMeshDescription(FMeshDescription& meshDesc, std::vector<FVector3f>& P, std::vector<FVector3f>& N, std::vector<FVector2f>& uv, std::vector<int32>& t)
{
FStaticMeshAttributes attributes(meshDesc);
attributes.Register();
//reserve stuff
meshDesc.ReserveNewVertices(P.size());
meshDesc.ReserveNewVertexInstances(P.size());
meshDesc.CreatePolygonGroup();
meshDesc.ReserveNewPolygons(t.size() / 3);
meshDesc.ReserveNewTriangles(t.size() / 3);
meshDesc.ReserveNewEdges(t.size());
for (int v = 0; v < P.size(); ++v)
{
meshDesc.CreateVertex();
meshDesc.CreateVertexInstance(v);
}
//fill traiangles data
for (int ti = 0; ti < t.size(); ti += 3)
{
meshDesc.CreateTriangle(0, { t[ti],t[ti + 1] ,t[ti + 2] });
}
//fill vertex data
auto positions = meshDesc.GetVertexPositions().GetRawArray();;
auto uvs = meshDesc.VertexInstanceAttributes().GetAttributesRef<FVector2f>(MeshAttribute::VertexInstance::TextureCoordinate).GetRawArray();
auto normals = meshDesc.VertexInstanceAttributes().GetAttributesRef<FVector3f>(MeshAttribute::VertexInstance::Normal).GetRawArray();
for (int v = 0; v < P.size(); ++v)
{
uvs[v] = uv[v];
positions[v] = P[v];
normals[v] = N[v];
}
}
// Called when the game starts or when spawned
void AMyDynActor::BeginPlay()
{
Super::BeginPlay();
//build sphere desc
std::vector<FVector3f> Vertices;
std::vector<FVector3f> Normals;
std::vector<FVector2f> UVs;
std::vector<int32> Triangles;
CreateSphere(Vertices, Normals, UVs, Triangles, 100);
FMeshDescription meshDesc;
FillMeshDescription(meshDesc, Vertices, Normals, UVs, Triangles);
//build static mesh
StaticMesh = NewObject<UStaticMesh>(this, FName("staticMesh"));
StaticMesh->SetRenderData(MakeUnique<FStaticMeshRenderData>());
StaticMesh->CreateBodySetup();
StaticMesh->bAllowCPUAccess = true;
StaticMesh->GetBodySetup()->CollisionTraceFlag = ECollisionTraceFlag::CTF_UseComplexAsSimple;
UStaticMesh::FBuildMeshDescriptionsParams mdParams;
StaticMesh->BuildFromMeshDescriptions({ &meshDesc }, mdParams);
//build mesh component
UStaticMeshComponent* mesh = NewObject<UStaticMeshComponent>(this, UStaticMeshComponent::StaticClass(), FName("importedMesh"));
mesh->RegisterComponent();
mesh->SetRelativeLocation(FVector(20000, 20000, 0));
mesh->SetWorldScale3D(FVector(50, 50, 1000));
mesh->SetStaticMesh(StaticMesh);
mesh->SetMaterial(0, Material);
mesh->CastShadow = true;
mesh->bCastFarShadow = true;
mesh->bCastDynamicShadow = true;
mesh->bCastShadowAsTwoSided = true;
this->SetRootComponent(mesh);
}
