首先是进入蓝图接口代码:
UDynamicMesh* UMyMeshPrimitiveFunctions::AppendBox(
UDynamicMesh* TargetMesh,//添加到哪个UDynamicMesh中去
FGeometryScriptPrimitiveOptions PrimitiveOptions,
FTransform Transform,//坐标,旋转,缩放。注意:UE5中心点位置:x:x/2,y:y/2,z:0
float DimensionX,//X长度
float DimensionY,//y长度
float DimensionZ,//z长度
int32 StepsX,//X方向的顶点数
int32 StepsY,//y方向的顶点数
int32 StepsZ,//z方向的顶点数
EGeometryScriptPrimitiveOriginMode Origin,
UGeometryScriptDebug* Debug){
if (TargetMesh == nullptr)
{
UE::Geometry::AppendError(Debug, EGeometryScriptErrorType::InvalidInputs, LOCTEXT("PrimitiveFunctions_AppendBox", "AppendBox: TargetMesh is Null"));
return TargetMesh;
}
//创建包围盒
UE::Geometry::FAxisAlignedBox3d ConvertBox(
FVector3d(-DimensionX / 2, -DimensionY / 2, 0),
FVector3d(DimensionX / 2, DimensionY / 2, DimensionZ));
FGridBoxMeshGenerator2 GridBoxGenerator;
//通过包围盒创建盒子
GridBoxGenerator.Box = UE::Geometry::FOrientedBox3d(ConvertBox);
//各个方向的边缘顶点的数量,这里默认为0,创建的时候最小会修正为2。
GridBoxGenerator.EdgeVertices = FIndex3i(FMath::Max(0, StepsX), FMath::Max(0, StepsY), FMath::Max(0, StepsZ));
GridBoxGenerator.bPolygroupPerQuad = (PrimitiveOptions.PolygroupMode == EGeometryScriptPrimitivePolygroupMode::PerQuad);
GridBoxGenerator.Generate();
FVector3d OriginShift = (Origin == EGeometryScriptPrimitiveOriginMode::Center) ? FVector3d(0, 0, -DimensionZ / 2) : FVector3d::Zero();
AppendPrimitive(TargetMesh, &GridBoxGenerator, Transform, PrimitiveOptions, OriginShift);
return TargetMesh;
}
然后是Generate代码
virtual FMeshShapeGenerator& Generate() override
{
//边缘顶点,修正数量最低为2
FIndex3i N = EdgeVertices;
N.A = FMath::Max(2, N.A);
N.B = FMath::Max(2, N.B);
N.C = FMath::Max(2, N.C);
//缩放
FVector3d Nscale(1.0 / (N.A - 1), 1.0 / (N.B - 1), 1.0 / (N.C - 1));
//内部,最低为0
FIndex3i NInternal = N;
NInternal.A -= 2; NInternal.B -= 2; NInternal.C -= 2;
FIndex3i NTri = N;
NTri.A--; NTri.B--; NTri.C--;
//24个 UV 和 法线,法线与UV相同数量
int NumUVsAndNormals = 2 * (N.A * N.B + N.B * N.C + N.C * N.A);
//正方形的8 顶点
int NumVertices = 8 + (NInternal.A + NInternal.B + NInternal.C) * 4 + (NInternal.A * NInternal.B + NInternal.B * NInternal.C + NInternal.C * NInternal.A) * 2;
//12个三角形,6个面,一个面2个三角形
int NumTriangles = 4 * (NTri.A * NTri.B + NTri.B * NTri.C + NTri.C * NTri.A);
//设置UE缓冲
SetBufferSizes(NumVertices, NumTriangles, NumUVsAndNormals, NumUVsAndNormals);
int FaceDimOrder[3]{ 1, 2, 0 }; // ordering from IndexUtil.cpp
int D[2][3]{ { 1,2,0 }, { 2,0,1 } }; // helper mapping to go from major dimension to the two canonical sub-dimensions in order; is just D[i][j] -> (j+i+1)%3 (TODO: replace with the modulus version?)
// allocate a big mapping from faces to vertices, to make life easier when creating triangles later
FIndex3i VerticesPerFace(N.B * N.C, N.A * N.C, N.A * N.B);
//顶点索引
TArray<TArray<int>> FaceVertIndices;
FaceVertIndices.SetNum(6);
for (int Dim = 0; Dim < 3; Dim++)
{
int FaceIdxForDim = FaceDimOrder[Dim] * 2;
int VertexNum = VerticesPerFace[Dim];
FaceVertIndices[FaceIdxForDim].SetNum(VertexNum);
FaceVertIndices[FaceIdxForDim + 1].SetNum(VertexNum);
}
auto ToFaceV = [&N, &D](int Dim, int D0, int D1)
{
return D0 + N[D[0][Dim]] * D1;
};
// create the corners and distribute them into the face mapping
for (int i = 0; i < 8; ++i)
{
Vertices[i] = Box.GetCorner(i);
FIndex3i CornerSides = FOrientedBox3d::GetCornerSide(i);
for (int Dim = 0; Dim < 3; Dim++)
{
int FaceIdx = FaceDimOrder[Dim] * 2 + CornerSides[Dim];
int D0Ind = CornerSides[D[0][Dim]] * (N[D[0][Dim]] - 1);
int D1Ind = CornerSides[D[1][Dim]] * (N[D[1][Dim]] - 1);
int CornerVInd = ToFaceV(Dim, D0Ind, D1Ind);
FaceVertIndices[FaceIdx][CornerVInd] = i;
}
}
// create the internal (non-corner) edge vertices and distribute them into the face mapping
int CurrentVertIndex = 8;
for (int Dim = 0; Dim < 3; Dim++)
{
int EdgeLen = N[Dim];
if (EdgeLen <= 2)
{
continue; // no internal edge vertices on this dimension
}
int MajorFaceInd = FaceDimOrder[Dim] * 2;
int FaceInds[2]{ -1,-1 };
int DSides[2]{ 0,0 };
for (DSides[0] = 0; DSides[0] < 2; DSides[0]++)
{
FaceInds[0] = FaceDimOrder[D[0][Dim]] * 2 + DSides[0];
for (DSides[1] = 0; DSides[1] < 2; DSides[1]++)
{
FaceInds[1] = FaceDimOrder[D[1][Dim]] * 2 + DSides[1];
int MajorCornerInd = ToFaceV(Dim, DSides[0] * (N[D[0][Dim]] - 1), DSides[1] * (N[D[1][Dim]] - 1));
FVector3d Corners[2]
{
Vertices[FaceVertIndices[MajorFaceInd][MajorCornerInd]],
Vertices[FaceVertIndices[MajorFaceInd + 1][MajorCornerInd]]
};
for (int EdgeVert = 1; EdgeVert + 1 < EdgeLen; EdgeVert++)
{
Vertices[CurrentVertIndex] = Lerp(Corners[0], Corners[1], EdgeVert * Nscale[Dim]);
for (int WhichFace = 0; WhichFace < 2; WhichFace++) // each edge is shared by two faces (w/ major axes of subdim 0 and subdim 1 respectively)
{
int FaceInd = FaceInds[WhichFace];
int FaceDim = D[WhichFace][Dim];
int SubDims[2];
SubDims[1 - WhichFace] = EdgeVert;
SubDims[WhichFace] = NTri[D[WhichFace][FaceDim]] * DSides[1 - WhichFace];
int FaceV = ToFaceV(FaceDim, SubDims[0], SubDims[1]);
FaceVertIndices[FaceInds[WhichFace]][FaceV] = CurrentVertIndex;
}
CurrentVertIndex++;
}
}
}
}
// create the internal (non-corner, non-edge) face vertices and distribute them into the face mapping
for (int Dim = 0; Dim < 3; Dim++)
{
int FaceIdxBase = FaceDimOrder[Dim] * 2;
int FaceInternalVNum = NInternal[D[0][Dim]] * NInternal[D[1][Dim]];
if (FaceInternalVNum <= 0)
{
continue;
}
for (int Side = 0; Side < 2; Side++)
{
int MajorFaceInd = FaceIdxBase + Side;
for (int D0 = 1; D0 + 1 < N[D[0][Dim]]; D0++)
{
int BotInd = ToFaceV(Dim, D0, 0);
int TopInd = ToFaceV(Dim, D0, N[D[1][Dim]] - 1);
FVector3d Edges[2]
{
Vertices[FaceVertIndices[MajorFaceInd][BotInd]],
Vertices[FaceVertIndices[MajorFaceInd][TopInd]]
};
for (int D1 = 1; D1 + 1 < N[D[1][Dim]]; D1++)
{
Vertices[CurrentVertIndex] = Lerp(Edges[0], Edges[1], D1 * Nscale[D[1][Dim]]);
FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0, D1)] = CurrentVertIndex;
CurrentVertIndex++;
}
}
}
}
double MaxDimension = MaxAbsElement(2.0 * Box.Extents);
float UVScale = (bScaleUVByAspectRatio) ? (1.0f / (float)MaxDimension) : 1.0f;
// create the face triangles and UVs+normals
int CurrentTriIdx = 0;
int CurrentUVIdx = 0;
int CurrentQuadIdx = 0;
for (int Dim = 0; Dim < 3; Dim++)
{
int FaceIdxBase = FaceDimOrder[Dim] * 2;
// UV-specific minor axes + flips; manually set to match default UnrealEngine cube texture arrangement
int Minor1Flip[3] = { -1, 1, 1 };
int Minor2Flip[3] = { -1, -1, 1 };
// UV scales for D0, D1
double FaceWidth = FMathd::Abs(Box.Extents[D[0][Dim]]) * 2.0;
double FaceHeight = FMathd::Abs(Box.Extents[D[1][Dim]]) * 2.0;
double WidthUVScale = FaceWidth * UVScale;
double HeightUVScale = FaceHeight * UVScale;
for (int Side = 0; Side < 2; Side++)
{
int SideOpp = 1 - Side;
float SideSign = Side * 2 - 1;
FVector3f Normal(0, 0, 0);
Normal[Dim] = (2 * Side - 1);
Normal = (FVector3f)Box.Frame.FromFrameVector((FVector3d)Normal);
int MajorFaceInd = FaceIdxBase + Side;
int FaceUVStartInd = CurrentUVIdx;
// set all the UVs and normals
FVector2f UV;
int UVXDim = Dim == 1 ? 1 : 0; // which dim (of D0,D1) follows the horizontal UV coordinate
int UVYDim = 1 - UVXDim; // which dim (of D0,D1) follows the vertical UV coordinate
for (int D0 = 0; D0 < N[D[0][Dim]]; D0++)
{
for (int D1 = 0; D1 < N[D[1][Dim]]; D1++)
{
// put the grid coordinates (centered at 0,0) into the UVs
UV[UVXDim] = D0 * Nscale[D[0][Dim]] - .5;
UV[UVYDim] = D1 * Nscale[D[1][Dim]] - .5;
// invert axes to match the desired UV patterns & so the opp faces are not backwards
UV.X *= SideSign * Minor1Flip[Dim];
UV.Y *= Minor2Flip[Dim];
// recenter and scale up
UV[UVXDim] = (UV[UVXDim] + .5) * WidthUVScale;
UV[UVYDim] = (UV[UVYDim] + .5) * HeightUVScale;
UVs[CurrentUVIdx] = UV;
Normals[CurrentUVIdx] = Normal;
UVParentVertex[CurrentUVIdx] = FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0, D1)];
NormalParentVertex[CurrentUVIdx] = FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0, D1)];
CurrentUVIdx++;
}
}
// set all the triangles
for (int D0 = 0; D0 + 1 < N[D[0][Dim]]; D0++)
{
for (int D1 = 0; D1 + 1 < N[D[1][Dim]]; D1++)
{
SetTriangle(CurrentTriIdx,
FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0, D1)],
FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0 + SideOpp, D1 + Side)],
FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0 + 1, D1 + 1)]
);
SetTriangleUVs(CurrentTriIdx,
FaceUVStartInd + D1 + (D0)*N[D[1][Dim]],
FaceUVStartInd + D1 + Side + (D0 + SideOpp) * N[D[1][Dim]],
FaceUVStartInd + D1 + 1 + (D0 + 1) * N[D[1][Dim]]
);
SetTriangleNormals(CurrentTriIdx,
FaceUVStartInd + D1 + (D0)*N[D[1][Dim]],
FaceUVStartInd + D1 + Side + (D0 + SideOpp) * N[D[1][Dim]],
FaceUVStartInd + D1 + 1 + (D0 + 1) * N[D[1][Dim]]
);
SetTrianglePolygon(CurrentTriIdx, (bPolygroupPerQuad) ? CurrentQuadIdx : MajorFaceInd);
CurrentTriIdx++;
SetTriangle(CurrentTriIdx,
FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0, D1)],
FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0 + 1, D1 + 1)],
FaceVertIndices[MajorFaceInd][ToFaceV(Dim, D0 + Side, D1 + SideOpp)]
);
SetTriangleUVs(CurrentTriIdx,
FaceUVStartInd + D1 + (D0)*N[D[1][Dim]],
FaceUVStartInd + D1 + 1 + (D0 + 1) * N[D[1][Dim]],
FaceUVStartInd + D1 + SideOpp + (D0 + Side) * N[D[1][Dim]]
);
SetTriangleNormals(CurrentTriIdx,
FaceUVStartInd + D1 + (D0)*N[D[1][Dim]],
FaceUVStartInd + D1 + 1 + (D0 + 1) * N[D[1][Dim]],
FaceUVStartInd + D1 + SideOpp + (D0 + Side) * N[D[1][Dim]]
);
SetTrianglePolygon(CurrentTriIdx, (bPolygroupPerQuad) ? CurrentQuadIdx : MajorFaceInd);
CurrentTriIdx++;
CurrentQuadIdx++;
}
}
}
}
return *this;
}
可以复用这些代码创建自己的三维图元