UE5 三维图元源码 - Box,运行时动态创建自己的三维图元。

1 篇文章 0 订阅

首先是进入蓝图接口代码:

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;
			}

可以复用这些代码创建自己的三维图元

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天道酬勤~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值