/******************************************************************************* The content of this file includes portions of the proprietary AUDIOKINETIC Wwise Technology released in source code form as part of the game integration package. The content of this file may not be used without valid licenses to the AUDIOKINETIC Wwise Technology. Note that the use of the game engine is subject to the Unreal(R) Engine End User License Agreement at https://www.unrealengine.com/en-US/eula/unreal License Usage Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use this file in accordance with the end user license agreement provided with the software or, alternatively, in accordance with the terms contained in a written agreement between you and Audiokinetic Inc. Copyright (c) 2023 Audiokinetic Inc. *******************************************************************************/ #include "AkGeometryData.h" #include "PhysicalMaterials/PhysicalMaterial.h" void GetBasicBoxGeometryData(TArray& Vertices, TArray& Triangles) { Vertices.Init(FVector(0, 0, 0), 8); Vertices[0] = FVector(-1, -1, -1); Vertices[1] = FVector(-1, -1, 1); Vertices[2] = FVector(-1, 1, -1); Vertices[3] = FVector(-1, 1, 1); Vertices[4] = FVector(1, -1, -1); Vertices[5] = FVector(1, -1, 1); Vertices[6] = FVector(1, 1, -1); Vertices[7] = FVector(1, 1, 1); Triangles.Init(FAkTriangle(), 12); Triangles[0] = { 0, 1, 3, AK_INVALID_SURFACE }; Triangles[1] = { 0, 1, 5, AK_INVALID_SURFACE }; Triangles[2] = { 0, 2, 3, AK_INVALID_SURFACE }; Triangles[3] = { 0, 2, 6, AK_INVALID_SURFACE }; Triangles[4] = { 0, 4, 5, AK_INVALID_SURFACE }; Triangles[5] = { 0, 4, 6, AK_INVALID_SURFACE }; Triangles[6] = { 1, 3, 7, AK_INVALID_SURFACE }; Triangles[7] = { 1, 5, 7, AK_INVALID_SURFACE }; Triangles[8] = { 2, 3, 7, AK_INVALID_SURFACE }; Triangles[9] = { 2, 6, 7, AK_INVALID_SURFACE }; Triangles[10] = { 4, 5, 7, AK_INVALID_SURFACE }; Triangles[11] = { 4, 6, 7, AK_INVALID_SURFACE }; } void GetBasicXYPlaneGeometryData(TArray& Vertices, TArray& Triangles) { Vertices.Init(FVector(0, 0, 0), 4); Vertices[0] = FVector(-1, -1, 1); Vertices[1] = FVector(-1, 1, 1); Vertices[2] = FVector(1, -1, 1); Vertices[3] = FVector(1, 1, 1); Triangles.Init(FAkTriangle(), 2); Triangles[0] = { 0, 1, 2, AK_INVALID_SURFACE }; Triangles[1] = { 1, 3, 2, AK_INVALID_SURFACE }; } void GetBasicXZPlaneGeometryData(TArray& Vertices, TArray& Triangles) { Vertices.Init(FVector(0, 0, 0), 4); Vertices[0] = FVector(-1, 1, -1); Vertices[1] = FVector(-1, 1, 1); Vertices[2] = FVector(1, 1, -1); Vertices[3] = FVector(1, 1, 1); Triangles.Init(FAkTriangle(), 2); Triangles[0] = { 0, 1, 2, AK_INVALID_SURFACE }; Triangles[1] = { 1, 3, 2, AK_INVALID_SURFACE }; } void GetBasicYZPlaneGeometryData(TArray& Vertices, TArray& Triangles) { Vertices.Init(FVector(0, 0, 0), 4); Vertices[0] = FVector(1, -1, -1); Vertices[1] = FVector(1, -1, 1); Vertices[2] = FVector(1, 1, -1); Vertices[3] = FVector(1, 1, 1); Triangles.Init(FAkTriangle(), 2); Triangles[0] = { 0, 1, 2, AK_INVALID_SURFACE }; Triangles[1] = { 1, 3, 2, AK_INVALID_SURFACE }; } /** Taken from GetOrientedHalfSphereMesh in PrimitiveDrawingUtils.cpp. See original for adding tangents and texture coords. */ void GenerateHalfSphereVerts(AkSurfIdx surfIdx, const FVector& Center, const FRotator& Orientation, const float Radius, int32 NumSides, int32 NumRings, float StartAngle, float EndAngle, FAkGeometryData& GeometryData) { if (NumSides <= 0 || NumRings <= 0) return; // The first/last arc are on top of each other. int32 numVerts = (NumSides + 1) * (NumRings + 1); TArray vertices; vertices.AddDefaulted(numVerts); TArray triangles; int32 BaseVertIndex = GeometryData.Vertices.Num(); // Calculate verts for one arc TArray arcVertices; for (int32 i = 0; i < NumRings + 1; i++) { float angle = StartAngle + ((float)i / NumRings) * (EndAngle - StartAngle); arcVertices.Add(FVector(0.0f, FMath::Sin(angle) * Radius, FMath::Cos(angle) * Radius) + Center); } // Then rotate this arc NumSides+1 times. for (int32 s = 0; s < NumSides + 1; s++) { FRotator ArcRotator(0, 360.f * (float)s / NumSides, 0); FRotationMatrix ArcRot(ArcRotator); for (int32 v = 0; v < NumRings + 1; v++) { int32 VIx = (NumRings + 1) * s + v; vertices[VIx] = ArcRot.TransformPosition(arcVertices[v]); } } // Add all of the vertices we generated to the geometry data. for (int32 vertIdx = 0; vertIdx < numVerts; vertIdx++) { GeometryData.Vertices.Add(vertices[vertIdx]); } // If StartAngle is 0, the top-most ring is the top pole, and all sides will start on the same vertex. const bool sidesStartAtTopPole = StartAngle < PI * 0.1f; // Similarly, if EndAngle is PI, the bottom-most ring is the bottom pole, and all sides will end on the same vertex. const bool sidesEndAtBottomPole = EndAngle >= PI * 0.9f; // Add all of the triangles we generated to the geometry data. for (uint16 s = 0; s < NumSides; s++) { // Add triangles between consecutive sides, from the top-most ring to the bottom. uint16 side0Start = (s + 0) * (NumRings + 1) + BaseVertIndex; uint16 side1Start = (s + 1) * (NumRings + 1) + BaseVertIndex; uint16 s0 = side0Start; uint16 s1 = side1Start; // s0 and s1 refer to the vertices running up and down the 'sides' of the sphere. // the line from vertex s0 to vertex s0 + 1 runs down one 'side' // the line from vertex s0 to vertex s1 runs along one 'ring' (from one 'side' to the other). // the line from vertex s0 to vertex s1 + 1 runs diagonally down a side and along a ring. // Add the initial triangle for this side strip (or triangles, if we're not starting on a pole) if (sidesStartAtTopPole) { GeometryData.Triangles.Add({ s0, uint16(s1 + 1), uint16(s0 + 1), surfIdx }); } else { GeometryData.Triangles.Add({ s0, s1, uint16(s0 + 1), surfIdx }); GeometryData.Triangles.Add({ s1, uint16(s1 + 1), uint16(s0 + 1), surfIdx }); } for (uint16 r = 1; r < NumRings - 1; r++) { s0 = side0Start + r; s1 = side1Start + r; GeometryData.Triangles.Add({ s0, s1, uint16(s0 + 1), surfIdx }); GeometryData.Triangles.Add({ s1, uint16(s1 + 1), uint16(s0 + 1), surfIdx }); } // Add the final triangle for this side strip (or triangles if we're not ending on a pole) s0 = side0Start + (NumRings - 1); s1 = side1Start + (NumRings - 1); GeometryData.Triangles.Add({ s0, s1, uint16(s0 + 1), surfIdx }); if (!sidesEndAtBottomPole) { GeometryData.Triangles.Add({ s1, uint16(s1 + 1), uint16(s0 + 1), surfIdx }); } } } /** Taken from BuildCylinderVerts in PrimitiveDrawingUtils.cpp. See original for adding tangents and texture coords. */ void GenerateCylinderVerts(AkSurfIdx surfIdx, const FVector& Base, const FVector& XAxis, const FVector& YAxis, const FVector& ZAxis, float Radius, float HalfHeight, uint32 Sides, FAkGeometryData& GeometryData) { const float AngleDelta = 2.0f * PI / Sides; FVector LastVertex = Base + XAxis * Radius; FVector2D TC = FVector2D(0.0f, 0.0f); float TCStep = 1.0f / Sides; FVector TopOffset = HalfHeight * ZAxis; int32 BaseVertIndex = GeometryData.Vertices.Num(); //Compute vertices for base circle. for (uint32 SideIndex = 0; SideIndex < Sides; SideIndex++) { const FVector Vertex = Base + (XAxis * FMath::Cos(AngleDelta * (SideIndex + 1)) + YAxis * FMath::Sin(AngleDelta * (SideIndex + 1))) * Radius; FVector Normal = Vertex - Base; Normal.Normalize(); GeometryData.Vertices.Add(Vertex - TopOffset); LastVertex = Vertex; TC.X += TCStep; } LastVertex = Base + XAxis * Radius; TC = FVector2D(0.0f, 1.0f); //Compute vertices for the top circle for (uint16 SideIndex = 0; SideIndex < Sides; SideIndex++) { const FVector Vertex = Base + (XAxis * FMath::Cos(AngleDelta * (SideIndex + 1)) + YAxis * FMath::Sin(AngleDelta * (SideIndex + 1))) * Radius; FVector Normal = Vertex - Base; Normal.Normalize(); GeometryData.Vertices.Add(Vertex + TopOffset); LastVertex = Vertex; TC.X += TCStep; } //Add sides. for (uint16 SideIndex = 0; SideIndex < Sides; SideIndex++) { uint16 V0 = BaseVertIndex + SideIndex; uint16 V1 = BaseVertIndex + ((SideIndex + 1) % Sides); uint16 V2 = V0 + Sides; uint16 V3 = V1 + Sides; GeometryData.Triangles.Add({ V0, V2, V1, surfIdx }); GeometryData.Triangles.Add({ V2, V3, V1, surfIdx }); } } void FAkGeometryData::AddBox(AkSurfIdx surfIdx, FVector center, FVector extent, FRotator rotation) { TArray boxVertices; TArray boxTriangles; if (extent.Z == 0.0f) GetBasicXYPlaneGeometryData(boxVertices, boxTriangles); else if (extent.Y == 0.0f) GetBasicXZPlaneGeometryData(boxVertices, boxTriangles); else if (extent.X == 0.0f) GetBasicYZPlaneGeometryData(boxVertices, boxTriangles); else GetBasicBoxGeometryData(boxVertices, boxTriangles); AkVertIdx initialVertIdx = Vertices.Num(); // move vertices according to the center and extents for (AkVertIdx idx = 0; idx < boxVertices.Num(); idx++) { FTransform transform(rotation, center, extent); FVector v = transform.TransformPosition(boxVertices[idx]); Vertices.Add(v); } for (AkTriIdx idx = 0; idx < boxTriangles.Num(); idx++) { boxTriangles[idx].Point0 += initialVertIdx; boxTriangles[idx].Point1 += initialVertIdx; boxTriangles[idx].Point2 += initialVertIdx; boxTriangles[idx].Surface = surfIdx; Triangles.Add(boxTriangles[idx]); } } void FAkGeometryData::AddSphere(AkSurfIdx surfIdx, const FVector& Center, const float Radius, int32 NumSides, int32 NumRings) { GenerateHalfSphereVerts(surfIdx, Center, FRotator::ZeroRotator, Radius, NumSides, NumRings, 0, PI, *this); } void FAkGeometryData::AddCapsule(AkSurfIdx surfIdx, const FVector& Origin, const FVector& XAxis, const FVector& YAxis, const FVector& ZAxis, float Radius, float HalfHeight, int32 NumSides) { const FVector BottomEnd = Origin - HalfHeight * ZAxis; const FVector TopEnd = Origin + HalfHeight * ZAxis; GenerateHalfSphereVerts(surfIdx, TopEnd, FRotationMatrix::MakeFromXY(XAxis, YAxis).Rotator(), Radius, NumSides, NumSides, 0, PI / 2, *this); GenerateCylinderVerts(surfIdx, Origin, XAxis, YAxis, ZAxis, Radius, HalfHeight, NumSides, *this); GenerateHalfSphereVerts(surfIdx, BottomEnd, FRotationMatrix::MakeFromXY(XAxis, YAxis).Rotator(), Radius, NumSides, NumSides, PI / 2, PI, *this); }