virtual void InitRHI() { FRHIResourceCreateInfo CreateInfo; VertexBufferRHI = RHICreateVertexBuffer(12 * sizeof(FPackedNormal), BUF_Dynamic, CreateInfo); // Copy the vertex data into the vertex buffer. FPackedNormal* TangentBufferData = (FPackedNormal*)RHILockVertexBuffer(VertexBufferRHI, 0, 12 * sizeof(FPackedNormal), RLM_WriteOnly); for(int32 FaceIndex = 0;FaceIndex < 6;++FaceIndex) { const FVector UnprojectedTangentX = FVector(+1,-1,0).SafeNormal(); const FVector UnprojectedTangentY(-1,-1,-1); const FVector FaceNormal = FaceNormals[FaceIndex].ToFloat(); const FVector ProjectedFaceTangentX = (UnprojectedTangentX - FaceNormal * (UnprojectedTangentX | FaceNormal)).SafeNormal(); *TangentBufferData++ = FPackedNormal(ProjectedFaceTangentX); *TangentBufferData++ = FPackedNormal(FVector4(FaceNormal, FMath::Sign(UnprojectedTangentY | (FaceNormal ^ ProjectedFaceTangentX)))); } RHIUnlockVertexBuffer(VertexBufferRHI); }
void ASimpleCylinderActor::GenerateCylinder(TArray<FRuntimeMeshVertexSimple>& InVertices, TArray<int32>& InTriangles, float InHeight, float InWidth, int32 InCrossSectionCount, bool bInCapEnds, bool bInDoubleSided, bool bInSmoothNormals/* = true*/) { // ------------------------------------------------------- // Basic setup int32 VertexIndex = 0; int32 TriangleIndex = 0; // ------------------------------------------------------- // Make a cylinder section const float AngleBetweenQuads = (2.0f / (float)(InCrossSectionCount)) * PI; const float VMapPerQuad = 1.0f / (float)InCrossSectionCount; FVector Offset = FVector(0, 0, InHeight); // Start by building up vertices that make up the cylinder sides for (int32 QuadIndex = 0; QuadIndex < InCrossSectionCount; QuadIndex++) { float Angle = (float)QuadIndex * AngleBetweenQuads; float NextAngle = (float)(QuadIndex + 1) * AngleBetweenQuads; // Set up the vertices FVector p0 = FVector(FMath::Cos(Angle) * InWidth, FMath::Sin(Angle) * InWidth, 0.f); FVector p1 = FVector(FMath::Cos(NextAngle) * InWidth, FMath::Sin(NextAngle) * InWidth, 0.f); FVector p2 = p1 + Offset; FVector p3 = p0 + Offset; // Set up the quad triangles int32 VertIndex1 = VertexIndex++; int32 VertIndex2 = VertexIndex++; int32 VertIndex3 = VertexIndex++; int32 VertIndex4 = VertexIndex++; InVertices[VertIndex1].Position = p0; InVertices[VertIndex2].Position = p1; InVertices[VertIndex3].Position = p2; InVertices[VertIndex4].Position = p3; // Now create two triangles from those four vertices // The order of these (clockwise/counter-clockwise) dictates which way the normal will face. InTriangles[TriangleIndex++] = VertIndex4; InTriangles[TriangleIndex++] = VertIndex3; InTriangles[TriangleIndex++] = VertIndex1; InTriangles[TriangleIndex++] = VertIndex3; InTriangles[TriangleIndex++] = VertIndex2; InTriangles[TriangleIndex++] = VertIndex1; // UVs. Note that Unreal UV origin (0,0) is top left InVertices[VertIndex1].UV0 = FVector2D(1.0f - (VMapPerQuad * QuadIndex), 1.0f); InVertices[VertIndex2].UV0 = FVector2D(1.0f - (VMapPerQuad * (QuadIndex + 1)), 1.0f); InVertices[VertIndex3].UV0 = FVector2D(1.0f - (VMapPerQuad * (QuadIndex + 1)), 0.0f); InVertices[VertIndex4].UV0 = FVector2D(1.0f - (VMapPerQuad * QuadIndex), 0.0f); // Normals FVector NormalCurrent = FVector::CrossProduct(InVertices[VertIndex1].Position - InVertices[VertIndex3].Position, InVertices[VertIndex2].Position - InVertices[VertIndex3].Position).GetSafeNormal(); if (bInSmoothNormals) { // To smooth normals we give the vertices different values than the polygon they belong to. // GPUs know how to interpolate between those. // I do this here as an average between normals of two adjacent polygons float NextNextAngle = (float)(QuadIndex + 2) * AngleBetweenQuads; FVector p4 = FVector(FMath::Cos(NextNextAngle) * InWidth, FMath::Sin(NextNextAngle) * InWidth, 0.f); // p1 to p4 to p2 FVector NormalNext = FVector::CrossProduct(p1 - p2, p4 - p2).GetSafeNormal(); FVector AverageNormalRight = (NormalCurrent + NormalNext) / 2; AverageNormalRight = AverageNormalRight.GetSafeNormal(); float PreviousAngle = (float)(QuadIndex - 1) * AngleBetweenQuads; FVector pMinus1 = FVector(FMath::Cos(PreviousAngle) * InWidth, FMath::Sin(PreviousAngle) * InWidth, 0.f); // p0 to p3 to pMinus1 FVector NormalPrevious = FVector::CrossProduct(p0 - pMinus1, p3 - pMinus1).GetSafeNormal(); FVector AverageNormalLeft = (NormalCurrent + NormalPrevious) / 2; AverageNormalLeft = AverageNormalLeft.GetSafeNormal(); InVertices[VertIndex1].Normal = FPackedNormal(AverageNormalLeft); InVertices[VertIndex2].Normal = FPackedNormal(AverageNormalRight); InVertices[VertIndex3].Normal = FPackedNormal(AverageNormalRight); InVertices[VertIndex4].Normal = FPackedNormal(AverageNormalLeft); } else { // If not smoothing we just set the vertex normal to the same normal as the polygon they belong to InVertices[VertIndex1].Normal = InVertices[VertIndex2].Normal = InVertices[VertIndex3].Normal = InVertices[VertIndex4].Normal = FPackedNormal(NormalCurrent); } // Tangents (perpendicular to the surface) FVector SurfaceTangent = p0 - p1; SurfaceTangent = SurfaceTangent.GetSafeNormal(); InVertices[VertIndex1].Tangent = InVertices[VertIndex2].Tangent = InVertices[VertIndex3].Tangent = InVertices[VertIndex4].Tangent = FPackedNormal(SurfaceTangent); // ------------------------------------------------------- // If double sided, create extra polygons but face the normals the other way. if (bInDoubleSided) { VertIndex1 = VertexIndex++; VertIndex2 = VertexIndex++; VertIndex3 = VertexIndex++; VertIndex4 = VertexIndex++; InVertices[VertIndex1].Position = p0; InVertices[VertIndex2].Position = p1; InVertices[VertIndex3].Position = p2; InVertices[VertIndex4].Position = p3; // Reverse the poly order to face them the other way InTriangles[TriangleIndex++] = VertIndex4; InTriangles[TriangleIndex++] = VertIndex1; InTriangles[TriangleIndex++] = VertIndex3; InTriangles[TriangleIndex++] = VertIndex3; InTriangles[TriangleIndex++] = VertIndex1; InTriangles[TriangleIndex++] = VertIndex2; // UVs (Unreal 1,1 is top left) InVertices[VertIndex1].UV0 = FVector2D(1.0f - (VMapPerQuad * QuadIndex), 1.0f); InVertices[VertIndex2].UV0 = FVector2D(1.0f - (VMapPerQuad * (QuadIndex + 1)), 1.0f); InVertices[VertIndex3].UV0 = FVector2D(1.0f - (VMapPerQuad * (QuadIndex + 1)), 0.0f); InVertices[VertIndex4].UV0 = FVector2D(1.0f - (VMapPerQuad * QuadIndex), 0.0f); // Just simple (unsmoothed) normal for these InVertices[VertIndex1].Normal = InVertices[VertIndex2].Normal = InVertices[VertIndex3].Normal = InVertices[VertIndex4].Normal = FPackedNormal(NormalCurrent); // Tangents (perpendicular to the surface) FVector SurfaceTangentDbl = p0 - p1; SurfaceTangentDbl = SurfaceTangentDbl.GetSafeNormal(); InVertices[VertIndex1].Tangent = InVertices[VertIndex2].Tangent = InVertices[VertIndex3].Tangent = InVertices[VertIndex4].Tangent = FPackedNormal(SurfaceTangentDbl); } // ------------------------------------------------------- // Caps are closed here by triangles that start at 0, then use the points along the circle for the other two corners. // A better looking method uses a vertex in the center of the circle, but uses two more polygons. We will demonstrate that in a different sample. if (QuadIndex != 0 && QuadIndex != InCrossSectionCount - 1 && bInCapEnds) { // Bottom cap FVector capVertex0 = FVector(FMath::Cos(0) * InWidth, FMath::Sin(0) * InWidth, 0.f); FVector capVertex1 = FVector(FMath::Cos(Angle) * InWidth, FMath::Sin(Angle) * InWidth, 0.f); FVector capVertex2 = FVector(FMath::Cos(NextAngle) * InWidth, FMath::Sin(NextAngle) * InWidth, 0.f); VertIndex1 = VertexIndex++; VertIndex2 = VertexIndex++; VertIndex3 = VertexIndex++; InVertices[VertIndex1].Position = capVertex0; InVertices[VertIndex2].Position = capVertex1; InVertices[VertIndex3].Position = capVertex2; InTriangles[TriangleIndex++] = VertIndex1; InTriangles[TriangleIndex++] = VertIndex2; InTriangles[TriangleIndex++] = VertIndex3; InVertices[VertIndex1].UV0 = FVector2D(0.5f - (FMath::Cos(0) / 2.0f), 0.5f - (FMath::Sin(0) / 2.0f)); InVertices[VertIndex2].UV0 = FVector2D(0.5f - (FMath::Cos(-Angle) / 2.0f), 0.5f - (FMath::Sin(-Angle) / 2.0f)); InVertices[VertIndex3].UV0 = FVector2D(0.5f - (FMath::Cos(-NextAngle) / 2.0f), 0.5f - (FMath::Sin(-NextAngle) / 2.0f)); FVector CapNormalCurrent = FVector::CrossProduct(InVertices[VertIndex1].Position - InVertices[VertIndex3].Position, InVertices[VertIndex2].Position - InVertices[VertIndex3].Position).GetSafeNormal(); InVertices[VertIndex1].Normal = InVertices[VertIndex2].Normal = InVertices[VertIndex3].Normal = FPackedNormal(CapNormalCurrent); // Tangents (perpendicular to the surface) FVector SurfaceTangentCap = p0 - p1; SurfaceTangentCap = SurfaceTangentCap.GetSafeNormal(); InVertices[VertIndex1].Tangent = InVertices[VertIndex2].Tangent = InVertices[VertIndex3].Tangent = FPackedNormal(SurfaceTangentCap); // Top cap capVertex0 = capVertex0 + Offset; capVertex1 = capVertex1 + Offset; capVertex2 = capVertex2 + Offset; VertIndex1 = VertexIndex++; VertIndex2 = VertexIndex++; VertIndex3 = VertexIndex++; InVertices[VertIndex1].Position = capVertex0; InVertices[VertIndex2].Position = capVertex1; InVertices[VertIndex3].Position = capVertex2; InTriangles[TriangleIndex++] = VertIndex3; InTriangles[TriangleIndex++] = VertIndex2; InTriangles[TriangleIndex++] = VertIndex1; InVertices[VertIndex1].UV0 = FVector2D(0.5f - (FMath::Cos(0) / 2.0f), 0.5f - (FMath::Sin(0) / 2.0f)); InVertices[VertIndex2].UV0 = FVector2D(0.5f - (FMath::Cos(Angle) / 2.0f), 0.5f - (FMath::Sin(Angle) / 2.0f)); InVertices[VertIndex3].UV0 = FVector2D(0.5f - (FMath::Cos(NextAngle) / 2.0f), 0.5f - (FMath::Sin(NextAngle) / 2.0f)); CapNormalCurrent = FVector::CrossProduct(InVertices[VertIndex1].Position - InVertices[VertIndex3].Position, InVertices[VertIndex2].Position - InVertices[VertIndex3].Position).GetSafeNormal(); InVertices[VertIndex1].Normal = InVertices[VertIndex2].Normal = InVertices[VertIndex3].Normal = FPackedNormal(CapNormalCurrent); // Tangents (perpendicular to the surface) SurfaceTangentCap = p0 - p1; SurfaceTangentCap = SurfaceTangentCap.GetSafeNormal(); InVertices[VertIndex1].Tangent = InVertices[VertIndex2].Tangent = InVertices[VertIndex3].Tangent = FPackedNormal(SurfaceTangentCap); } } }
void ABranchingLinesActor::GenerateCylinder(TArray<FRuntimeMeshVertexSimple>& Vertices, TArray<int32>& Triangles, FVector StartPoint, FVector EndPoint, float InWidth, int32 InCrossSectionCount, int32& VertexIndex, int32& TriangleIndex, bool bInSmoothNormals/* = true*/) { // Make a cylinder section const float AngleBetweenQuads = (2.0f / (float)(InCrossSectionCount)) * PI; const float UMapPerQuad = 1.0f / (float)InCrossSectionCount; FVector StartOffset = StartPoint - FVector(0, 0, 0); FVector Offset = EndPoint - StartPoint; // Find angle between vectors FVector LineDirection = (StartPoint - EndPoint); LineDirection.Normalize(); FVector RotationAngle = LineDirection.Rotation().Add(90.f, 0.f, 0.f).Euler(); // Start by building up vertices that make up the cylinder sides for (int32 QuadIndex = 0; QuadIndex < InCrossSectionCount; QuadIndex++) { float Angle = (float)QuadIndex * AngleBetweenQuads; float NextAngle = (float)(QuadIndex + 1) * AngleBetweenQuads; // Set up the vertices FVector p0 = (CachedCrossSectionPoints[QuadIndex] * InWidth) + StartOffset; p0 = RotatePointAroundPivot(p0, StartPoint, RotationAngle); FVector p1 = CachedCrossSectionPoints[QuadIndex + 1] * InWidth + StartOffset; p1 = RotatePointAroundPivot(p1, StartPoint, RotationAngle); FVector p2 = p1 + Offset; FVector p3 = p0 + Offset; // Set up the quad triangles int32 VertIndex1 = VertexIndex++; int32 VertIndex2 = VertexIndex++; int32 VertIndex3 = VertexIndex++; int32 VertIndex4 = VertexIndex++; Vertices[VertIndex1].Position = p0; Vertices[VertIndex2].Position = p1; Vertices[VertIndex3].Position = p2; Vertices[VertIndex4].Position = p3; // Now create two triangles from those four vertices // The order of these (clockwise/counter-clockwise) dictates which way the normal will face. Triangles[TriangleIndex++] = VertIndex4; Triangles[TriangleIndex++] = VertIndex3; Triangles[TriangleIndex++] = VertIndex1; Triangles[TriangleIndex++] = VertIndex3; Triangles[TriangleIndex++] = VertIndex2; Triangles[TriangleIndex++] = VertIndex1; // UVs. Note that Unreal UV origin (0,0) is top left Vertices[VertIndex1].UV0 = FVector2D(1.0f - (UMapPerQuad * QuadIndex), 1.0f); Vertices[VertIndex2].UV0 = FVector2D(1.0f - (UMapPerQuad * (QuadIndex + 1)), 1.0f); Vertices[VertIndex3].UV0 = FVector2D(1.0f - (UMapPerQuad * (QuadIndex + 1)), 0.0f); Vertices[VertIndex4].UV0 = FVector2D(1.0f - (UMapPerQuad * QuadIndex), 0.0f); // Normals FVector NormalCurrent = FVector::CrossProduct(Vertices[VertIndex1].Position - Vertices[VertIndex3].Position, Vertices[VertIndex2].Position - Vertices[VertIndex3].Position).GetSafeNormal(); if (bInSmoothNormals) { // To smooth normals we give the vertices different values than the polygon they belong to. // GPUs know how to interpolate between those. // I do this here as an average between normals of two adjacent polygons float NextNextAngle = (float)(QuadIndex + 2) * AngleBetweenQuads; FVector p4 = (CachedCrossSectionPoints[QuadIndex + 2] * InWidth) + StartOffset; p4 = RotatePointAroundPivot(p4, StartPoint, RotationAngle); // p1 to p4 to p2 FVector NormalNext = FVector::CrossProduct(p1 - p2, p4 - p2).GetSafeNormal(); FVector AverageNormalRight = (NormalCurrent + NormalNext) / 2; AverageNormalRight = AverageNormalRight.GetSafeNormal(); float PreviousAngle = (float)(QuadIndex - 1) * AngleBetweenQuads; FVector pMinus1 = FVector(FMath::Cos(PreviousAngle) * InWidth, FMath::Sin(PreviousAngle) * InWidth, 0.f) + StartOffset; pMinus1 = RotatePointAroundPivot(pMinus1, StartPoint, RotationAngle); // p0 to p3 to pMinus1 FVector NormalPrevious = FVector::CrossProduct(p0 - pMinus1, p3 - pMinus1).GetSafeNormal(); FVector AverageNormalLeft = (NormalCurrent + NormalPrevious) / 2; AverageNormalLeft = AverageNormalLeft.GetSafeNormal(); Vertices[VertIndex1].Normal = FPackedNormal(AverageNormalLeft); Vertices[VertIndex2].Normal = FPackedNormal(AverageNormalRight); Vertices[VertIndex3].Normal = FPackedNormal(AverageNormalRight); Vertices[VertIndex4].Normal = FPackedNormal(AverageNormalLeft); } else { // If not smoothing we just set the vertex normal to the same normal as the polygon they belong to Vertices[VertIndex1].Normal = Vertices[VertIndex2].Normal = Vertices[VertIndex3].Normal = Vertices[VertIndex4].Normal = FPackedNormal(NormalCurrent); } // Tangents (perpendicular to the surface) FVector SurfaceTangent = p0 - p1; SurfaceTangent = SurfaceTangent.GetSafeNormal(); Vertices[VertIndex1].Tangent = Vertices[VertIndex2].Tangent = Vertices[VertIndex3].Tangent = Vertices[VertIndex4].Tangent = FPackedNormal(SurfaceTangent); } }