├── .gitattributes ├── .gitignore ├── Config └── FilterPlugin.ini ├── Content ├── Blueprints │ └── BP_SplineMeshArea.uasset ├── Maps │ └── M_SplineArea.umap ├── Materials │ ├── M_SplineArea.uasset │ ├── M_SplineArea_Outline.uasset │ └── M_TeleportationArea.uasset └── Models │ ├── SM_Unit_Plane.fbx │ └── SM_Unit_Plane.uasset ├── HowToSeeSplineAreaContent.jpg ├── LICENSE ├── README.md ├── Resources └── Icon128.png ├── Source └── SplineArea │ ├── Private │ ├── ASplineArea.cpp │ └── SplineArea.cpp │ ├── Public │ ├── ASplineArea.h │ └── SplineArea.h │ └── SplineArea.Build.cs └── SplineArea.uplugin /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | -------------------------------------------------------------------------------- /Content/Blueprints/BP_SplineMeshArea.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Content/Blueprints/BP_SplineMeshArea.uasset -------------------------------------------------------------------------------- /Content/Maps/M_SplineArea.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Content/Maps/M_SplineArea.umap -------------------------------------------------------------------------------- /Content/Materials/M_SplineArea.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Content/Materials/M_SplineArea.uasset -------------------------------------------------------------------------------- /Content/Materials/M_SplineArea_Outline.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Content/Materials/M_SplineArea_Outline.uasset -------------------------------------------------------------------------------- /Content/Materials/M_TeleportationArea.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Content/Materials/M_TeleportationArea.uasset -------------------------------------------------------------------------------- /Content/Models/SM_Unit_Plane.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Content/Models/SM_Unit_Plane.fbx -------------------------------------------------------------------------------- /Content/Models/SM_Unit_Plane.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Content/Models/SM_Unit_Plane.uasset -------------------------------------------------------------------------------- /HowToSeeSplineAreaContent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/HowToSeeSplineAreaContent.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Robin Smekens 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HOW TO USE: 2 | - Drop BP_SplineMeshArea into your level 3 | - Use the transform tools to adjust the spline component 4 | - Or create a new actor deriving from BP_SplineMeshArea to adjust the materials use 5 | 6 | TEST PROJECT: 7 | - The plugin comes packaged with M_SplineArea (SplineArea/Content/Maps) 8 | 9 | ![SHOWING SPLINE AREA CONTENT](https://github.com/PizzaCutter/SplineArea/blob/master/HowToSeeSplineAreaContent.jpg?raw=true) 10 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PizzaCutter/SplineArea/6129566b0e2fdd8f111a1cab18725786df66a9e1/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/SplineArea/Private/ASplineArea.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Robin Smekens 2 | 3 | #include "ASplineArea.h" 4 | #include "ProceduralMeshComponent.h" 5 | #include "Components/SplineComponent.h" 6 | #include "Engine/StaticMesh.h" 7 | 8 | #include "Materials/MaterialInterface.h" 9 | #include "Components/InstancedStaticMeshComponent.h" 10 | #include "Kismet/KismetMathLibrary.h" 11 | #include "UObject/ConstructorHelpers.h" 12 | 13 | // Sets default values 14 | ASplineArea::ASplineArea() 15 | { 16 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 17 | PrimaryActorTick.bCanEverTick = false; 18 | 19 | RootComponent = CreateDefaultSubobject(TEXT("Root")); 20 | pAreaMesh = CreateDefaultSubobject(TEXT("AreaMesh")); 21 | pSpline = CreateDefaultSubobject(TEXT("Spline")); 22 | pAreaOutline = CreateDefaultSubobject(TEXT("AreaOutline")); 23 | 24 | pAreaMesh->SetupAttachment(RootComponent); 25 | pSpline->SetupAttachment(RootComponent); 26 | pAreaOutline->SetupAttachment(RootComponent); 27 | 28 | pAreaMesh->bUseAsyncCooking = true; 29 | ClearSpline(); 30 | pAreaOutline->SetCollisionEnabled(ECollisionEnabled::NoCollision); 31 | 32 | if (pAreaOutline == nullptr) 33 | { 34 | static ConstructorHelpers::FObjectFinder foundMesh( 35 | TEXT("StaticMesh'/SplineArea/Models/SM_Unit_Plane.SM_Unit_Plane'")); 36 | if (foundMesh.Succeeded()) 37 | { 38 | //UStaticMesh* areaOutlineMesh = foundMesh.Object; 39 | pAreaOutline->SetStaticMesh(foundMesh.Object); 40 | } 41 | } 42 | 43 | if (pAreaMeshMaterial == nullptr) 44 | { 45 | static ConstructorHelpers::FObjectFinder foundMaterial( 46 | TEXT("Material'/SplineArea/Materials/M_SplineArea.M_SplineArea'")); 47 | if (foundMaterial.Succeeded()) 48 | pAreaMeshMaterial = foundMaterial.Object; 49 | } 50 | 51 | if (pAreaOutlineMaterial == nullptr) 52 | { 53 | static ConstructorHelpers::FObjectFinder foundMaterial( 54 | TEXT("Material'/SplineArea/Materials/M_SplineArea_Outline.M_SplineArea_Outline'")); 55 | if (foundMaterial.Succeeded()) 56 | { 57 | pAreaOutlineMaterial = foundMaterial.Object; 58 | pAreaOutline->SetMaterial(0, pAreaOutlineMaterial); 59 | } 60 | } 61 | } 62 | 63 | // Called when the game starts or when spawned 64 | void ASplineArea::BeginPlay() 65 | { 66 | Super::BeginPlay(); 67 | } 68 | 69 | // Called every frame 70 | void ASplineArea::Tick(float DeltaTime) 71 | { 72 | Super::Tick(DeltaTime); 73 | } 74 | 75 | void ASplineArea::OnConstruction(const FTransform& Transform) 76 | { 77 | CreateTeleportationArea(); 78 | 79 | for (int i = 0; i < pSpline->GetNumberOfSplinePoints(); i++) 80 | { 81 | pSpline->SetSplinePointType(i, ESplinePointType::Linear, false); 82 | } 83 | } 84 | 85 | void ASplineArea::CreateTeleportationArea() 86 | { 87 | VisualizationTriangles.Reset(0); 88 | TArray meshTriangles; 89 | 90 | TriangulateSpline(); 91 | 92 | CreateAreaMesh(); 93 | CreateAreaOutline(); 94 | } 95 | 96 | void ASplineArea::ClearSpline() const 97 | { 98 | pSpline->ClearSplinePoints(true); 99 | 100 | TArray splinePoints; 101 | splinePoints.Add(FSplinePoint(0, FVector(-StandardSize, -StandardSize, 0.f))); 102 | splinePoints.Add(FSplinePoint(1, FVector(StandardSize, -StandardSize, 0.f))); 103 | splinePoints.Add(FSplinePoint(2, FVector(StandardSize, StandardSize, 0.f))); 104 | splinePoints.Add(FSplinePoint(3, FVector(-StandardSize, StandardSize, 0.f))); 105 | 106 | pSpline->AddPoints(splinePoints, false); 107 | for (int i = 0; i < pSpline->GetNumberOfSplinePoints(); i++) 108 | { 109 | pSpline->SetSplinePointType(i, ESplinePointType::Linear, false); 110 | } 111 | 112 | pSpline->SetClosedLoop(true); 113 | } 114 | 115 | /// 116 | /// If the index is larger then length it will loop around 117 | /// 118 | /// index to transform 119 | /// maximum value -> length of the array 120 | inline int CircularIndex(const int index, const int length) 121 | { 122 | int temp = index % length; 123 | int temp1 = temp + length; 124 | if (temp < 0) 125 | return temp1; 126 | 127 | return temp; 128 | } 129 | 130 | /// 131 | /// Checks if this is a valid point 132 | /// 133 | /// first point 134 | /// second point 135 | /// third point 136 | inline bool PointIsConvex(FVector prevPoint, FVector curPoint, FVector nextPoint) 137 | { 138 | FVector temp1 = prevPoint - curPoint; 139 | FVector temp2 = nextPoint - curPoint; 140 | temp1.Normalize(); 141 | temp2.Normalize(); 142 | FVector temp3 = FVector::CrossProduct(temp1, temp2); 143 | 144 | return temp3.Z < 0; 145 | } 146 | 147 | /// 148 | /// Checks if this is a valid point 149 | /// 150 | /// first point 151 | /// second point 152 | /// third point 153 | inline bool IsPointInTriangle(FVector t1, FVector t2, FVector t3, FVector p) 154 | { 155 | //Get two edges of the triangle and the line to the point 156 | const FVector v1 = t3 - t1; 157 | const FVector v2 = t2 - t1; 158 | const FVector v3 = p - t1; 159 | 160 | //Convert the triangle edges to a Barycentric Coordinate System 161 | const float daa = FVector::DotProduct(v1, v1); 162 | const float dab = FVector::DotProduct(v1, v2); 163 | const float dac = FVector::DotProduct(v1, v3); 164 | const float dbb = FVector::DotProduct(v2, v2); 165 | const float dbc = FVector::DotProduct(v2, v3); 166 | 167 | float u = (((dbb * dac) - (dab * dbc)) / ((daa * dbb) - (dab * dab))); 168 | float v = (((daa * dbc) - (dab * dac)) / ((daa * dbb) - (dab * dab))); 169 | 170 | //If the position of the point in the barycentric coord sys is greater than 1 is is not inside the triangle 171 | return (v >= 0.f) && (u >= 0.f) && ((u + v) < 1.f); 172 | } 173 | 174 | /// 175 | /// Check if any reflex points are inside the triangle created by the 3 points if so it is an ear 176 | /// 177 | /// Point index that you want to check 178 | /// Array that contains all the known reflex indices 179 | /// Array that contains all the spline points positional data 180 | inline bool IsPointAnEar(const int curPointIndex, const TArray& reflexIndices, const TArray& splinePoints) 181 | { 182 | bool IsEar = true; 183 | 184 | const int prevIndex = CircularIndex(curPointIndex - 1, splinePoints.Num()); 185 | const int nextIndex = CircularIndex(curPointIndex + 1, splinePoints.Num()); 186 | 187 | const FVector curPoint = splinePoints[curPointIndex]; 188 | const FVector prevPoint = splinePoints[prevIndex]; 189 | const FVector nextPoint = splinePoints[nextIndex]; 190 | 191 | for (auto i = 0; i < reflexIndices.Num(); i++) 192 | { 193 | IsEar = !IsPointInTriangle(curPoint, prevPoint, nextPoint, splinePoints[reflexIndices[i]]); 194 | if (!IsEar) 195 | return IsEar; 196 | } 197 | return IsEar; 198 | } 199 | 200 | void ASplineArea::TriangulateSpline() 201 | { 202 | TArray splinePoints = GetSplinePoints(); 203 | TArray reflexPoints = TArray{}; 204 | TArray convexPoints = TArray{}; 205 | TArray earPoints = TArray{}; 206 | 207 | TArray reflexIndices = TArray{}; 208 | TArray convexIndices = TArray{}; 209 | TArray earIndices = TArray{}; 210 | 211 | GetPolygonComponents(splinePoints, reflexPoints, convexPoints, earPoints, reflexIndices, convexIndices, earIndices); 212 | 213 | reflexPoints.Reset(); 214 | convexPoints.Reset(); 215 | earPoints.Reset(); 216 | 217 | TrianglesFromPoints(splinePoints, convexIndices, reflexIndices, earIndices); 218 | } 219 | 220 | void ASplineArea::GetPolygonComponents(const TArray& splinePoints, TArray& reflexPoints, 221 | TArray& convexPoints, TArray& 222 | earPoints, TArray& reflexIndices, TArray& convexIndices, 223 | TArray& earIndices) const 224 | { 225 | //Testing if point is convex or reflex 226 | { 227 | FVector prevPoint = FVector(); 228 | FVector nextPoint = FVector(); 229 | for (int curIndex = 0; curIndex < splinePoints.Num(); curIndex++) 230 | { 231 | FVector curPoint = splinePoints[curIndex]; 232 | const int prevIndex = CircularIndex(curIndex - 1, splinePoints.Num()); 233 | prevPoint = splinePoints[prevIndex]; 234 | const int nextIndex = CircularIndex(curIndex + 1, splinePoints.Num()); 235 | nextPoint = splinePoints[nextIndex]; 236 | 237 | if (PointIsConvex(prevPoint, curPoint, nextPoint)) 238 | { 239 | convexPoints.Add(curPoint); 240 | convexIndices.Add(curIndex); 241 | } 242 | else 243 | { 244 | reflexPoints.Add(curPoint); 245 | reflexIndices.Add(curIndex); 246 | } 247 | } 248 | } 249 | 250 | //Testing if point is an ear 251 | { 252 | for (int curIndex = 0; curIndex < convexIndices.Num(); curIndex++) 253 | { 254 | int curElement = convexIndices[curIndex]; 255 | 256 | if (IsPointAnEar(curElement, reflexIndices, splinePoints)) 257 | { 258 | earPoints.Add(splinePoints[curElement]); 259 | earIndices.Add(curElement); 260 | } 261 | } 262 | } 263 | } 264 | 265 | void ASplineArea::TrianglesFromPoints(TArray& splinePoints, const TArray& inConvexIndices, 266 | const TArray& inReflexIndices, const TArray< 267 | int>& inEarIndices) 268 | { 269 | int curPoint = 0; 270 | if (inEarIndices.Num() > 0) 271 | curPoint = inEarIndices[0]; 272 | const int prevPoint = CircularIndex(curPoint - 1, splinePoints.Num()); 273 | const int nextPoint = CircularIndex(curPoint + 1, splinePoints.Num()); 274 | 275 | if (splinePoints.Num() <= 3) 276 | { 277 | MeshTriangle triangle; 278 | triangle.point1 = splinePoints[0]; 279 | triangle.point2 = splinePoints[1]; 280 | triangle.point3 = splinePoints[2]; 281 | VisualizationTriangles.Add(triangle); 282 | } 283 | else 284 | { 285 | //Make a triangle of the ear point and its adjacent points 286 | MeshTriangle triangle; 287 | triangle.point1 = splinePoints[curPoint]; 288 | triangle.point2 = splinePoints[prevPoint]; 289 | triangle.point3 = splinePoints[nextPoint]; 290 | 291 | //Remove the ear 292 | splinePoints.RemoveAt(curPoint); 293 | 294 | //Recalcualte the polygon components with the ear missing for an optimisation a linked list data structure can be used instead of recalculating at every step 295 | TArray reflexPoints = TArray{}; 296 | TArray convexPoints = TArray{}; 297 | TArray earPoints = TArray{}; 298 | TArray reflexIndices = TArray{}; 299 | TArray convexIndices = TArray{}; 300 | TArray earIndices = TArray{}; 301 | GetPolygonComponents(splinePoints, reflexPoints, convexPoints, earPoints, reflexIndices, convexIndices, 302 | earIndices); 303 | TrianglesFromPoints(splinePoints, convexIndices, reflexIndices, earIndices); 304 | VisualizationTriangles.Add(triangle); 305 | } 306 | } 307 | 308 | TArray ASplineArea::GetSplinePoints() const 309 | { 310 | TArray splinePoints; 311 | for (int i = 0; i < pSpline->GetNumberOfSplinePoints(); i++) 312 | { 313 | splinePoints.Add(pSpline->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::Local)); 314 | } 315 | return splinePoints; 316 | } 317 | 318 | void ASplineArea::TrianglesToIndices(const TArray& triangles, TArray& vertices, 319 | TArray& indices) const 320 | { 321 | for (int i = 0; i < triangles.Num(); i++) 322 | { 323 | int newIndex = vertices.AddUnique(triangles[i].point1); 324 | if (newIndex < 0) 325 | indices.Add(newIndex); 326 | else 327 | indices.Add(vertices.Find(triangles[i].point1)); 328 | 329 | vertices.AddUnique(triangles[i].point2); 330 | if (newIndex < 0) 331 | indices.Add(newIndex); 332 | else 333 | indices.Add(vertices.Find(triangles[i].point2)); 334 | 335 | vertices.AddUnique(triangles[i].point3); 336 | if (newIndex < 0) 337 | indices.Add(newIndex); 338 | else 339 | indices.Add(vertices.Find(triangles[i].point3)); 340 | } 341 | } 342 | 343 | void ASplineArea::CreateAreaMesh() const 344 | { 345 | TArray vertices; 346 | TArray indices; 347 | TrianglesToIndices(VisualizationTriangles, vertices, indices); 348 | 349 | TArray normals; 350 | for (int i = 0; i < vertices.Num(); i++) 351 | { 352 | normals.Add(FVector(0, 0, 1)); 353 | } 354 | TArray uv; 355 | for (int i = 0; i < vertices.Num(); i++) 356 | { 357 | uv.Add(FVector2D(0, 0)); 358 | } 359 | TArray tangents; 360 | for (int i = 0; i < vertices.Num(); i++) 361 | { 362 | tangents.Add(FProcMeshTangent(0, 0, 1)); 363 | } 364 | TArray vertexColors; 365 | for (int i = 0; i < vertices.Num(); i++) 366 | { 367 | vertexColors.Add(FColor(0.75, 0.75, 0.75, 1.0)); 368 | } 369 | 370 | pAreaMesh->ClearMeshSection(0); 371 | pAreaMesh->CreateMeshSection(0, vertices, indices, normals, uv, vertexColors, tangents, true); 372 | pAreaMesh->SetMaterial(0, pAreaMeshMaterial); 373 | } 374 | 375 | void ASplineArea::CreateAreaOutline() const 376 | { 377 | if (bEnableOutline == false) 378 | { 379 | pAreaOutline->ClearInstances(); 380 | pAreaOutline->SetVisibility(false); 381 | return; 382 | } 383 | 384 | pAreaOutline->ClearInstances(); 385 | pAreaOutline->SetVisibility(true); 386 | 387 | const int instanceCount = pSpline->GetNumberOfSplinePoints(); 388 | for (int i = 0; i < instanceCount; i++) 389 | { 390 | FVector firstPoint = pSpline->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::World); 391 | FVector secondPoint = pSpline->GetLocationAtSplinePoint( 392 | CircularIndex(i + 1, pSpline->GetNumberOfSplinePoints()), ESplineCoordinateSpace::World); 393 | const FRotator rotationToPoint = UKismetMathLibrary::FindLookAtRotation(firstPoint, secondPoint); 394 | const float length = FVector::Dist(firstPoint, secondPoint); 395 | 396 | FTransform newTransform; 397 | newTransform.SetLocation(FMath::Lerp(firstPoint, secondPoint, 0.5f)); 398 | newTransform.SetRotation(FQuat(FRotator(rotationToPoint.Pitch, rotationToPoint.Yaw, 1.f))); 399 | newTransform.SetScale3D(FVector(length * 0.005f, OutlineWidth, 1.f)); 400 | 401 | pAreaOutline->AddInstanceWorldSpace(newTransform); 402 | } 403 | } 404 | 405 | void ASplineArea::SetAreaActive(bool newState) const 406 | { 407 | if (pAreaMesh == nullptr) 408 | return; 409 | if (pAreaOutline == nullptr) 410 | return; 411 | 412 | if (newState) 413 | { 414 | pAreaMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); 415 | pAreaMesh->SetVisibility(true); 416 | pAreaOutline->SetVisibility(true); 417 | } 418 | else 419 | { 420 | pAreaMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); 421 | pAreaOutline->SetCollisionEnabled(ECollisionEnabled::NoCollision); 422 | pAreaMesh->SetVisibility(false); 423 | pAreaOutline->SetVisibility(false); 424 | } 425 | } 426 | -------------------------------------------------------------------------------- /Source/SplineArea/Private/SplineArea.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Robin Smekens 2 | 3 | #include "SplineArea.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FSplineAreaModule" 6 | 7 | void FSplineAreaModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FSplineAreaModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FSplineAreaModule, SplineArea) -------------------------------------------------------------------------------- /Source/SplineArea/Public/ASplineArea.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Robin Smekens 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "ASplineArea.generated.h" 8 | 9 | class USplineComponent; 10 | class UProceduralMeshComponent; 11 | class UMaterialInterface; 12 | class UInstancedStaticMeshComponent; 13 | 14 | struct MeshTriangle 15 | { 16 | FVector point1 = FVector(); 17 | FVector point2 = FVector(); 18 | FVector point3 = FVector(); 19 | }; 20 | 21 | UCLASS() 22 | class SPLINEAREA_API ASplineArea : public AActor 23 | { 24 | GENERATED_BODY() 25 | 26 | public: 27 | // Sets default values for this actor's properties 28 | ASplineArea(); 29 | 30 | private: 31 | TArray VisualizationTriangles; 32 | float StandardSize = 50.f; 33 | 34 | protected: 35 | UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, Category = Default) 36 | USplineComponent* pSpline = nullptr; 37 | UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, Category = Default) 38 | UProceduralMeshComponent* pAreaMesh; 39 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Default) 40 | UMaterialInterface* pAreaMeshMaterial = nullptr; 41 | UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, Category = Default) 42 | UInstancedStaticMeshComponent* pAreaOutline = nullptr; 43 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Default) 44 | UMaterialInterface* pAreaOutlineMaterial = nullptr; 45 | 46 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Default) 47 | bool bEnableOutline = true; 48 | 49 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Default, meta = (EditCondition = "bEnableOutline")) 50 | float OutlineWidth = 2.f; 51 | 52 | protected: 53 | // Called when the game starts or when spawned 54 | virtual void BeginPlay() override; 55 | 56 | /// 57 | /// Creates starting array of reflex, convex, ear vertices. Returns the final mesh triangles 58 | /// 59 | void TriangulateSpline(); 60 | /// 61 | /// Create the procedural mesh based on the spline data (data produced by CreateTeleportationArea function) 62 | /// 63 | void CreateAreaMesh() const; 64 | /// 65 | /// Creates instances of meshes to create an outline effect around the generated area 66 | /// 67 | void CreateAreaOutline() const; 68 | 69 | /// 70 | /// Figures out which spline points are convex, reflex, ear. Based on this we create an index table for each type. 71 | /// 72 | /// Needs a copy of the positional data of the spline 73 | /// Empty array that will hold the reflex points afterwards 74 | /// Empty array that will hold the convex points afterwards 75 | /// Empty array that will hold the earPoints afterwards 76 | /// Empty array that will hold the reflex indices 77 | /// Empty array that will hold the convex indices 78 | /// Empty array that will hold the ear indices 79 | void GetPolygonComponents(const TArray& splinePoints, 80 | TArray& reflexPoints, TArray& convexPoints, TArray& earPoints, 81 | TArray& reflexIndices, TArray& convexIndices, TArray& earIndices) const; 82 | 83 | /// 84 | /// From the different types of vertices can figure out how the triangles need to laid out for the mesh 85 | /// 86 | /// Needs a copy of the positional data of the spline 87 | /// Empty array that will hold the reflex points afterwards 88 | /// Empty array that will hold the convex points afterwards 89 | /// Empty array that will hold the earPoints afterwards 90 | void TrianglesFromPoints(TArray& splinePoints, const TArray& inConvexIndices, 91 | const TArray& inReflexIndices, const TArray< 92 | int>& inEarIndices); 93 | 94 | /// 95 | /// Creates an index list from the triangle points 96 | /// 97 | /// Needs a copy of the positional data of the spline 98 | /// Empty array that will hold the reflex points afterwards 99 | /// Empty array that will hold the convex points afterwards 100 | void TrianglesToIndices(const TArray& triangles, TArray& vertices, 101 | TArray& indices) const; 102 | 103 | //FUNCTIONS 104 | public: 105 | // Called every frame 106 | virtual void Tick(float DeltaTime) override; 107 | virtual void OnConstruction(const FTransform& Transform); 108 | 109 | /// 110 | /// Disables/Enables the collision en visibility of the teleportation area based on the boolean 111 | /// 112 | UFUNCTION(BlueprintCallable) 113 | void SetAreaActive(bool newState) const; 114 | 115 | /// 116 | /// Calls all the other functions to generate the area 117 | /// 118 | UFUNCTION(BLueprintCallable) 119 | void CreateTeleportationArea(); 120 | 121 | /// 122 | /// Clears the current spline and adds 4 linear points in the shape of a square based on the StandardSize variable 123 | /// 124 | UFUNCTION(BlueprintCallable) 125 | void ClearSpline() const; 126 | 127 | /// 128 | /// Returns a TArray with all the positional data of the spline 129 | /// 130 | UFUNCTION(BlueprintPure) 131 | TArray GetSplinePoints() const; 132 | }; 133 | -------------------------------------------------------------------------------- /Source/SplineArea/Public/SplineArea.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Robin Smekens 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FSplineAreaModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Source/SplineArea/SplineArea.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Robin Smekens 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SplineArea : ModuleRules 6 | { 7 | public SplineArea(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | "CoreUObject", 38 | "Engine", 39 | "Slate", 40 | "SlateCore", 41 | "ProceduralMeshComponent" 42 | // ... add private dependencies that you statically link with here ... 43 | } 44 | ); 45 | 46 | 47 | DynamicallyLoadedModuleNames.AddRange( 48 | new string[] 49 | { 50 | // ... add any modules that your module loads dynamically here ... 51 | } 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /SplineArea.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 0.1, 4 | "VersionName": "0.1", 5 | "FriendlyName": "SplineArea", 6 | "Description": "Creating mesh using the procedural mesh component using the spline component", 7 | "Category": "Other", 8 | "CreatedBy": "Robin Smekens", 9 | "CreatedByURL": "www.robinsmekens.com", 10 | "DocsURL": "https://github.com/PizzaCutter/SplineArea", 11 | "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/01320bbc574848a192f95717b26abf4d", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": true, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "SplineArea", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default", 22 | "WhitelistPlatforms": [ 23 | "Win64" 24 | ] 25 | } 26 | ], 27 | "Plugins": [ 28 | { 29 | "Name": "ProceduralMeshComponent", 30 | "Enabled": true 31 | } 32 | ] 33 | } 34 | --------------------------------------------------------------------------------