From 96e9af4438191e255cc4fd7af8cc14a6c5aa8fa9 Mon Sep 17 00:00:00 2001 From: _Redstone_c_ <2824517378@qq.com> Date: Thu, 3 Dec 2020 13:52:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9F=BA=E6=9C=AC=20Cube=20?= =?UTF-8?q?=E4=BD=93=E7=B4=A0=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 15 + LICENSE | 21 + README.md | 4 + Resources/Icon128.png | Bin 0 -> 12699 bytes .../Blueprint/VoxelBlueprintLibrary.cpp | 11 + Source/Voxel/Private/Voxel.cpp | 20 + Source/Voxel/Private/VoxelAgentInterface.cpp | 11 + Source/Voxel/Private/VoxelBlock.cpp | 7 + Source/Voxel/Private/VoxelChunk.cpp | 323 +++++++++++++ Source/Voxel/Private/VoxelHelper.cpp | 38 ++ Source/Voxel/Private/VoxelLog.cpp | 3 + Source/Voxel/Private/VoxelMesh.cpp | 426 ++++++++++++++++++ Source/Voxel/Private/VoxelSubsystem.cpp | 16 + Source/Voxel/Private/VoxelWorld.cpp | 317 +++++++++++++ .../Public/Blueprint/VoxelBlueprintLibrary.h | 20 + Source/Voxel/Public/Voxel.h | 15 + Source/Voxel/Public/VoxelAgentInterface.h | 27 ++ Source/Voxel/Public/VoxelBlock.h | 66 +++ Source/Voxel/Public/VoxelChunk.h | 74 +++ Source/Voxel/Public/VoxelHelper.h | 23 + Source/Voxel/Public/VoxelLog.h | 5 + Source/Voxel/Public/VoxelMesh.h | 47 ++ Source/Voxel/Public/VoxelSubsystem.h | 33 ++ Source/Voxel/Public/VoxelWorld.h | 88 ++++ Source/Voxel/Voxel.Build.cs | 54 +++ Voxel.uplugin | 30 ++ 26 files changed, 1694 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Resources/Icon128.png create mode 100644 Source/Voxel/Private/Blueprint/VoxelBlueprintLibrary.cpp create mode 100644 Source/Voxel/Private/Voxel.cpp create mode 100644 Source/Voxel/Private/VoxelAgentInterface.cpp create mode 100644 Source/Voxel/Private/VoxelBlock.cpp create mode 100644 Source/Voxel/Private/VoxelChunk.cpp create mode 100644 Source/Voxel/Private/VoxelHelper.cpp create mode 100644 Source/Voxel/Private/VoxelLog.cpp create mode 100644 Source/Voxel/Private/VoxelMesh.cpp create mode 100644 Source/Voxel/Private/VoxelSubsystem.cpp create mode 100644 Source/Voxel/Private/VoxelWorld.cpp create mode 100644 Source/Voxel/Public/Blueprint/VoxelBlueprintLibrary.h create mode 100644 Source/Voxel/Public/Voxel.h create mode 100644 Source/Voxel/Public/VoxelAgentInterface.h create mode 100644 Source/Voxel/Public/VoxelBlock.h create mode 100644 Source/Voxel/Public/VoxelChunk.h create mode 100644 Source/Voxel/Public/VoxelHelper.h create mode 100644 Source/Voxel/Public/VoxelLog.h create mode 100644 Source/Voxel/Public/VoxelMesh.h create mode 100644 Source/Voxel/Public/VoxelSubsystem.h create mode 100644 Source/Voxel/Public/VoxelWorld.h create mode 100644 Source/Voxel/Voxel.Build.cs create mode 100644 Voxel.uplugin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93e6106 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +Binaries +DerivedDataCache +Intermediate +Saved +Build +.vscode +.vs +*.VC.db +*.opensdf +*.opendb +*.sdf +*.sln +*.suo +*.xcodeproj +*.xcworkspace diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c8faa2a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 zhaizhenbo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b3d26c4 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +## UE4 Plugin: Voxel +[Voxel](http://gitblit.myredstone.top/summary/UE4-Plugins!Voxel.git) 是开发中的一个体素插件。 + +目前支持的 UE4 版本:4.25.4 diff --git a/Resources/Icon128.png b/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..1231d4aad4d0d462fb7b178eb5b30aa61a10df0b GIT binary patch literal 12699 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 a;7#~yJh_ha>A&A^fRdb=Y>l)<=>Gxy=2LS3 literal 0 HcmV?d00001 diff --git a/Source/Voxel/Private/Blueprint/VoxelBlueprintLibrary.cpp b/Source/Voxel/Private/Blueprint/VoxelBlueprintLibrary.cpp new file mode 100644 index 0000000..8466a35 --- /dev/null +++ b/Source/Voxel/Private/Blueprint/VoxelBlueprintLibrary.cpp @@ -0,0 +1,11 @@ +#include "Blueprint/VoxelBlueprintLibrary.h" + +#include "VoxelSubsystem.h" +#include "Kismet/GameplayStatics.h" + +void UVoxelBlueprintLibrary::AddBlockType(const UObject* WorldContextObject, const FName& Name, const FVoxelBlockType& BlockType) +{ + UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(WorldContextObject); + UVoxelSubsystem* LockstepSubsystem = GameInstance->GetSubsystem(); + LockstepSubsystem->BlockTypes.Add(Name, BlockType); +} diff --git a/Source/Voxel/Private/Voxel.cpp b/Source/Voxel/Private/Voxel.cpp new file mode 100644 index 0000000..8f99c68 --- /dev/null +++ b/Source/Voxel/Private/Voxel.cpp @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "Voxel.h" + +#define LOCTEXT_NAMESPACE "FVoxelModule" + +void FVoxelModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FVoxelModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FVoxelModule, Voxel) \ No newline at end of file diff --git a/Source/Voxel/Private/VoxelAgentInterface.cpp b/Source/Voxel/Private/VoxelAgentInterface.cpp new file mode 100644 index 0000000..f0e2b4d --- /dev/null +++ b/Source/Voxel/Private/VoxelAgentInterface.cpp @@ -0,0 +1,11 @@ +#include "VoxelAgentInterface.h" + +FIntVector IVoxelAgentInterface::GetAgentLocation_Implementation() const +{ + return FIntVector(0); +} + +FVector IVoxelAgentInterface::GetAgentPartialLocation_Implementation() const +{ + return FVector(0.0f); +} diff --git a/Source/Voxel/Private/VoxelBlock.cpp b/Source/Voxel/Private/VoxelBlock.cpp new file mode 100644 index 0000000..5392735 --- /dev/null +++ b/Source/Voxel/Private/VoxelBlock.cpp @@ -0,0 +1,7 @@ +#include "VoxelBlock.h" + +#include "VoxelWorld.h" + +const FVoxelBlock FVoxelBlock::InvalidBlock = FVoxelBlock(NAME_None); + +const FVoxelBlockType FVoxelBlockType::InvalidBlockType = FVoxelBlockType(EVoxelBlockShape::Invalid); diff --git a/Source/Voxel/Private/VoxelChunk.cpp b/Source/Voxel/Private/VoxelChunk.cpp new file mode 100644 index 0000000..476106e --- /dev/null +++ b/Source/Voxel/Private/VoxelChunk.cpp @@ -0,0 +1,323 @@ +#include "VoxelChunk.h" + +#include "VoxelLog.h" +#include "VoxelWorld.h" +#include "VoxelSubsystem.h" +#include "ProceduralMeshComponent.h" +#include "KismetProceduralMeshLibrary.h" + +AVoxelChunk::AVoxelChunk(const class FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + RootComponent = CreateDefaultSubobject(TEXT("RootComponent")); + + MeshComponents.SetNum(16); + for (int32 i = 0; i < 16; ++i) + { + FString ComponentName = FString::Printf(TEXT("MeshComponent_%d"), i); + MeshComponents[i] = CreateDefaultSubobject(FName(ComponentName)); + MeshComponents[i]->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); + } + + FlushMeshFlags = -1; +} + +void AVoxelChunk::BeginPlay() +{ + UGameInstance* GameInstance = GetGameInstance(); + VoxelSubsystem = GameInstance->GetSubsystem(); +} + +const FVoxelBlock & AVoxelChunk::GetBlockByRelativeLocation(const FIntVector & Location) const +{ + checkCode( + if (!FMath::IsWithin(Location.X, 0, 16) + || !FMath::IsWithin(Location.Y, 0, 16) + || !FMath::IsWithin(Location.Z, 0, 256)) + { + UE_LOG(LogVoxel, Warning, TEXT("The Block %d, %d, %d Is Invalid In Chunk %d, %d"), Location.X, Location.Y, Location.Z, ChunkLocation.X, ChunkLocation.Y); + return FVoxelBlock::InvalidBlock; + } + ); + + return Blocks[Location.X][Location.Y][Location.Z]; +} + +void AVoxelChunk::SetBlockByRelativeLocation(const FIntVector& Location, const FVoxelBlock& NewBlock) +{ + checkCode( + if (!FMath::IsWithin(Location.X, 0, 16) + || !FMath::IsWithin(Location.Y, 0, 16) + || !FMath::IsWithin(Location.Z, 0, 256)) + { + UE_LOG(LogVoxel, Warning, TEXT("The Block %d, %d, %d Is Invalid In Chunk %d, %d"), Location.X, Location.Y, Location.Z, ChunkLocation.X, ChunkLocation.Y); + return; + } + ); + + Blocks[Location.X][Location.Y][Location.Z] = NewBlock; + + FlushMeshFlags |= 1 << (Location.Z / 16); + + if (Location.Z % 16 == 0 && Location.Z / 16 != 0) + FlushMeshFlags |= 1 << (Location.Z / 16 - 1); + + if (Location.Z % 16 == 15 && Location.Z / 16 != 15) + FlushMeshFlags |= 1 << (Location.Z / 16 + 1); + + const TMap& Chunks = VoxelWorld->GetChunks(); + + if (Location.X == 15) + { + FIntPoint NeighborLocation = FIntPoint(ChunkLocation.X + 1, ChunkLocation.Y); + + if (Chunks.Contains(NeighborLocation)) + { + AVoxelChunk* Neighbor = Chunks[NeighborLocation]; + Neighbor->FlushMeshFlags |= 1 << (Location.Z / 16); + VoxelWorld->AddChunkToMeshFlushBuffer(NeighborLocation); + } + } + + if (Location.X == 0) + { + FIntPoint NeighborLocation = FIntPoint(ChunkLocation.X - 1, ChunkLocation.Y); + + if (Chunks.Contains(NeighborLocation)) + { + AVoxelChunk* Neighbor = Chunks[NeighborLocation]; + Neighbor->FlushMeshFlags |= 1 << (Location.Z / 16); + VoxelWorld->AddChunkToMeshFlushBuffer(NeighborLocation); + } + } + + if (Location.Y == 15) + { + FIntPoint NeighborLocation = FIntPoint(ChunkLocation.X, ChunkLocation.Y + 1); + + if (Chunks.Contains(NeighborLocation)) + { + AVoxelChunk* Neighbor = Chunks[NeighborLocation]; + Neighbor->FlushMeshFlags |= 1 << (Location.Z / 16); + VoxelWorld->AddChunkToMeshFlushBuffer(NeighborLocation); + } + } + + if (Location.Y == 0) + { + FIntPoint NeighborLocation = FIntPoint(ChunkLocation.X, ChunkLocation.Y - 1); + + if (Chunks.Contains(NeighborLocation)) + { + AVoxelChunk* Neighbor = Chunks[NeighborLocation]; + Neighbor->FlushMeshFlags |= 1 << (Location.Z / 16); + VoxelWorld->AddChunkToMeshFlushBuffer(NeighborLocation); + } + } + + VoxelWorld->AddChunkToMeshFlushBuffer(ChunkLocation); +} + +void AVoxelChunk::FlushMeshs() +{ + if (!FlushMeshFlags) return; + + for (int32 i = 0; i < 16; ++i) + { + int32 FlushMeshFlag = 1 << i; + + if (!(FlushMeshFlags & FlushMeshFlag)) + continue; + + FlushMeshSection(i); + } + + FlushMeshFlags = 0; +} + +void AVoxelChunk::FlushMaterials() +{ + for (int32 i = 0; i < 16; ++i) + { + for (int32 j = 0; j < VoxelSubsystem->Materials.Num(); ++j) + { + MeshComponents[i]->SetMaterial(j, VoxelSubsystem->Materials[j]); + } + } +} + +void AVoxelChunk::Load() +{ + QUICK_SCOPE_CYCLE_COUNTER(STAT_VoxelChunk_Load); + + if (VoxelWorld->GetWorldSetting().ArchiveFolder.IsEmpty()) + { + ArchiveFile.Empty(); + return; + } + + ArchiveFile = VoxelWorld->GetWorldSetting().ArchiveFolder / FString::Printf(TEXT("%08X%08X"), ChunkLocation.X, ChunkLocation.Y) + ChunkFileExtension; + + if (FPaths::FileExists(ArchiveFile)) + { + TArray ChunkDataBuffer; + if (FFileHelper::LoadFileToArray(ChunkDataBuffer, *ArchiveFile)) + { + FMemoryReader MemoryReader(ChunkDataBuffer); + + UScriptStruct* VoxelBlockStruct = FVoxelBlock::StaticStruct(); + + for (int32 X = 0; X < 16; ++X) + { + for (int32 Y = 0; Y < 16; ++Y) + { + for (int32 Z = 0; Z < 256; ++Z) + { + VoxelBlockStruct->SerializeItem(MemoryReader, &Blocks[X][Y][Z], nullptr); + } + } + } + + UE_LOG(LogVoxel, Log, TEXT("Load Chunk %d, %d From File '%s'"), ChunkLocation.X, ChunkLocation.Y, *ArchiveFile); + } + } + +} + +void AVoxelChunk::Unload() +{ + QUICK_SCOPE_CYCLE_COUNTER(STAT_VoxelChunk_Unload); + + if (ArchiveFile.IsEmpty()) return; + + TArray ChunkDataBuffer; + FMemoryWriter MemoryWriter(ChunkDataBuffer); + + UScriptStruct* VoxelBlockStruct = FVoxelBlock::StaticStruct(); + + for (int32 X = 0; X < 16; ++X) + { + for (int32 Y = 0; Y < 16; ++Y) + { + for (int32 Z = 0; Z < 256; ++Z) + { + VoxelBlockStruct->SerializeItem(MemoryWriter, &Blocks[X][Y][Z], nullptr); + } + } + } + + if (FFileHelper::SaveArrayToFile(ChunkDataBuffer, *ArchiveFile)) + { + UE_LOG(LogVoxel, Log, TEXT("Save Chunk %d, %d To File '%s'"), ChunkLocation.X, ChunkLocation.Y, *ArchiveFile); + } + else + { + UE_LOG(LogVoxel, Error, TEXT("Failed To Save Chunk %d, %d To File '%s'"), ChunkLocation.X, ChunkLocation.Y, *ArchiveFile); + } +} + +void AVoxelChunk::FlushMeshSection(int32 SectionIndex) +{ + static FVoxelMeshData CubeTopFaceBuffer = FVoxelMeshData::CubeTopFace; + static FVoxelMeshData CubeBottomFaceBuffer = FVoxelMeshData::CubeBottomFace; + static FVoxelMeshData CubeFrontFaceBuffer = FVoxelMeshData::CubeFrontFace; + static FVoxelMeshData CubeBackFaceBuffer = FVoxelMeshData::CubeBackFace; + static FVoxelMeshData CubeLeftFaceBuffer = FVoxelMeshData::CubeLeftFace; + static FVoxelMeshData CubeRightFaceBuffer = FVoxelMeshData::CubeRightFace; + + MeshSectionBuffer.Reset(); + + FIntPoint FrontChunkLocation = FIntPoint(ChunkLocation.X + 1, ChunkLocation.Y); + FIntPoint BackChunkLocation = FIntPoint(ChunkLocation.X - 1, ChunkLocation.Y); + FIntPoint RightChunkLocation = FIntPoint(ChunkLocation.X, ChunkLocation.Y + 1); + FIntPoint LeftChunkLocation = FIntPoint(ChunkLocation.X, ChunkLocation.Y - 1); + + const TMap& Chunks = VoxelWorld->GetChunks(); + AVoxelChunk* FrontChunk = Chunks.Contains(FrontChunkLocation) ? Chunks[FrontChunkLocation] : nullptr; + AVoxelChunk* BackChunk = Chunks.Contains(BackChunkLocation) ? Chunks[BackChunkLocation] : nullptr; + AVoxelChunk* RightChunk = Chunks.Contains(RightChunkLocation) ? Chunks[RightChunkLocation] : nullptr; + AVoxelChunk* LeftChunk = Chunks.Contains(LeftChunkLocation) ? Chunks[LeftChunkLocation] : nullptr; + + for (int32 X = 0; X < 16; ++X) + { + for (int32 Y = 0; Y < 16; ++Y) + { + for (int32 Z = 0 + SectionIndex * 16; Z < 16 + SectionIndex * 16; ++Z) + { + const FVoxelBlockType& CurrentVoxelBlock = VoxelSubsystem->GetBlockType(Blocks[X][Y][Z].Type); + + if (CurrentVoxelBlock.Shape != EVoxelBlockShape::Cube) continue; + + const FVoxelBlockType& TopVoxelBlock = VoxelSubsystem->GetBlockType(Z + 1 < 256 ? Blocks[X][Y][Z + 1].Type : TEXT("Air")); + const FVoxelBlockType& BottomVoxelBlock = VoxelSubsystem->GetBlockType(Z - 1 >= 0 ? Blocks[X][Y][Z - 1].Type : TEXT("Air")); + const FVoxelBlockType& FrontVoxelBlock = VoxelSubsystem->GetBlockType(X + 1 < 16 ? Blocks[X + 1][Y][Z].Type : (FrontChunk ? FrontChunk->GetBlockByRelativeLocation(FIntVector(0, Y, Z)).Type : TEXT("Air"))); + const FVoxelBlockType& BackVoxelBlock = VoxelSubsystem->GetBlockType(X - 1 >= 0 ? Blocks[X - 1][Y][Z].Type : (BackChunk ? BackChunk->GetBlockByRelativeLocation(FIntVector(15, Y, Z)).Type : TEXT("Air"))); + const FVoxelBlockType& LeftVoxelBlock = VoxelSubsystem->GetBlockType(Y - 1 >= 0 ? Blocks[X][Y - 1][Z].Type : (LeftChunk ? LeftChunk->GetBlockByRelativeLocation(FIntVector(X, 15, Z)).Type : TEXT("Air"))); + const FVoxelBlockType& RightVoxelBlock = VoxelSubsystem->GetBlockType(Y + 1 < 16 ? Blocks[X][Y + 1][Z].Type : (RightChunk ? RightChunk->GetBlockByRelativeLocation(FIntVector(X, 0, Z)).Type : TEXT("Air"))); + + if (TopVoxelBlock.Shape != EVoxelBlockShape::Cube) + { + for (FVector2D& UV3 : CubeTopFaceBuffer.UV3) + UV3 = CurrentVoxelBlock.CustomTopFaceUV; + + MeshSectionBuffer.Append(CubeTopFaceBuffer, FVector(X, Y, Z) * 100.0f); + } + + if (BottomVoxelBlock.Shape != EVoxelBlockShape::Cube) + { + for (FVector2D& UV3 : CubeBottomFaceBuffer.UV3) + UV3 = CurrentVoxelBlock.CustomBottomFaceUV; + + MeshSectionBuffer.Append(CubeBottomFaceBuffer, FVector(X, Y, Z) * 100.0f); + } + + if (FrontVoxelBlock.Shape != EVoxelBlockShape::Cube) + { + for (FVector2D& UV3 : CubeFrontFaceBuffer.UV3) + UV3 = CurrentVoxelBlock.CustomFrontFaceUV; + + MeshSectionBuffer.Append(CubeFrontFaceBuffer, FVector(X, Y, Z) * 100.0f); + } + + if (BackVoxelBlock.Shape != EVoxelBlockShape::Cube) + { + for (FVector2D& UV3 : CubeBackFaceBuffer.UV3) + UV3 = CurrentVoxelBlock.CustomBackFaceUV; + + MeshSectionBuffer.Append(CubeBackFaceBuffer, FVector(X, Y, Z) * 100.0f); + } + + if (LeftVoxelBlock.Shape != EVoxelBlockShape::Cube) + { + for (FVector2D& UV3 : CubeLeftFaceBuffer.UV3) + UV3 = CurrentVoxelBlock.CustomLeftFaceUV; + + MeshSectionBuffer.Append(CubeLeftFaceBuffer, FVector(X, Y, Z) * 100.0f); + } + + if (RightVoxelBlock.Shape != EVoxelBlockShape::Cube) + { + for (FVector2D& UV3 : CubeRightFaceBuffer.UV3) + UV3 = CurrentVoxelBlock.CustomRightFaceUV; + + MeshSectionBuffer.Append(CubeRightFaceBuffer, FVector(X, Y, Z) * 100.0f); + } + } + } + } + + MeshComponents[SectionIndex]->CreateMeshSection_LinearColor + ( + 0, + MeshSectionBuffer.Vertices, + MeshSectionBuffer.Triangles, + MeshSectionBuffer.Normals, + MeshSectionBuffer.UV0, + MeshSectionBuffer.UV1, + MeshSectionBuffer.UV2, + MeshSectionBuffer.UV3, + MeshSectionBuffer.VertexColors, + TArray(), + true + ); +} diff --git a/Source/Voxel/Private/VoxelHelper.cpp b/Source/Voxel/Private/VoxelHelper.cpp new file mode 100644 index 0000000..7365606 --- /dev/null +++ b/Source/Voxel/Private/VoxelHelper.cpp @@ -0,0 +1,38 @@ +#include "VoxelHelper.h" + +#include "VoxelWorld.h" + +AVoxelWorld * UVoxelHelper::CreateVoxelWorld(UObject* WorldContextObject, const FVoxelWorldSetting& WorldSetting) +{ + UWorld* World = WorldContextObject->GetWorld(); + + if (!World) return nullptr; + + AVoxelWorld* VoxelWorld = World->SpawnActor(); + + VoxelWorld->WorldSetting = WorldSetting; + + return VoxelWorld; +} + +void UVoxelHelper::WorldToRelativeLocation(const FIntVector & InWorldLocation, FIntPoint & OutChunkLocation, FIntVector & OutRelativeLocation) +{ + OutChunkLocation.X = InWorldLocation.X / 16; + OutChunkLocation.Y = InWorldLocation.Y / 16; + + OutRelativeLocation.X = InWorldLocation.X % 16; + OutRelativeLocation.Y = InWorldLocation.Y % 16; + OutRelativeLocation.Z = InWorldLocation.Z; + + if (OutRelativeLocation.X < 0) + { + OutRelativeLocation.X += 16; + OutChunkLocation.X -= 1; + } + + if (OutRelativeLocation.Y < 0) + { + OutRelativeLocation.Y += 16; + OutChunkLocation.Y -= 1; + } +} diff --git a/Source/Voxel/Private/VoxelLog.cpp b/Source/Voxel/Private/VoxelLog.cpp new file mode 100644 index 0000000..38d6f17 --- /dev/null +++ b/Source/Voxel/Private/VoxelLog.cpp @@ -0,0 +1,3 @@ +#include "VoxelLog.h" + +DEFINE_LOG_CATEGORY(LogVoxel); diff --git a/Source/Voxel/Private/VoxelMesh.cpp b/Source/Voxel/Private/VoxelMesh.cpp new file mode 100644 index 0000000..56d3298 --- /dev/null +++ b/Source/Voxel/Private/VoxelMesh.cpp @@ -0,0 +1,426 @@ +#include "VoxelMesh.h" + +const FVoxelMeshData FVoxelMeshData::CubeTopFace = +{ + // Vertices + { + { 0.0f, 0.0f, 100.0f }, + { 100.0f, 0.0f, 100.0f }, + { 0.0f, 100.0f, 100.0f }, + { 100.0f, 100.0f, 100.0f }, + }, + + // Triangles + { + 2, 1, 0, + 2, 3, 1, + }, + + // Normals + { + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 0.0f, 1.0f }, + }, + + // UV0 + { + { 0.0f, 0.0f }, + { 1.0f, 0.0f }, + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + }, + + // UV1 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV2 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV3 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // VertexColors + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + }, +}; + +const FVoxelMeshData FVoxelMeshData::CubeBottomFace = +{ + // Vertices + { + { 0.0f, 0.0f, 0.0f }, + { 100.0f, 0.0f, 0.0f }, + { 0.0f, 100.0f, 0.0f }, + { 100.0f, 100.0f, 0.0f }, + }, + + // Triangles + { + 0, 1, 2, + 1, 3, 2, + }, + + // Normals + { + { 0.0f, 0.0f, -1.0f }, + { 0.0f, 0.0f, -1.0f }, + { 0.0f, 0.0f, -1.0f }, + { 0.0f, 0.0f, -1.0f }, + }, + + // UV0 + { + { 0.0f, 0.0f }, + { 1.0f, 0.0f }, + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + }, + + // UV1 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV2 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV3 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // VertexColors + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + }, +}; + +const FVoxelMeshData FVoxelMeshData::CubeFrontFace = +{ + // Vertices + { + { 100.0f, 0.0f, 0.0f }, + { 100.0f, 0.0f, 100.0f }, + { 100.0f, 100.0f, 0.0f }, + { 100.0f, 100.0f, 100.0f }, + }, + + // Triangles + { + 0, 1, 2, + 1, 3, 2, + }, + + // Normals + { + { 1.0f, 0.0f, 0.0f }, + { 1.0f, 0.0f, 0.0f }, + { 1.0f, 0.0f, 0.0f }, + { 1.0f, 0.0f, 0.0f }, + }, + + // UV0 + { + { 0.0f, 1.0f }, + { 0.0f, 0.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + }, + + // UV1 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV2 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV3 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // VertexColors + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + }, +}; + +const FVoxelMeshData FVoxelMeshData::CubeBackFace = +{ + // Vertices + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 100.0f }, + { 0.0f, 100.0f, 0.0f }, + { 0.0f, 100.0f, 100.0f }, + }, + + // Triangles + { + 2, 1, 0, + 2, 3, 1, + }, + + // Normals + { + { -1.0f, 0.0f, 0.0f }, + { -1.0f, 0.0f, 0.0f }, + { -1.0f, 0.0f, 0.0f }, + { -1.0f, 0.0f, 0.0f }, + }, + + // UV0 + { + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 1.0f }, + { 0.0f, 0.0f }, + }, + + // UV1 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV2 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV3 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // VertexColors + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + }, +}; + +const FVoxelMeshData FVoxelMeshData::CubeLeftFace = +{ + // Vertices + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 100.0f }, + { 100.0f, 0.0f, 0.0f }, + { 100.0f, 0.0f, 100.0f }, + }, + + // Triangles + { + 0, 1, 2, + 1, 3, 2, + }, + + // Normals + { + { 0.0f, -1.0f, 0.0f }, + { 0.0f, -1.0f, 0.0f }, + { 0.0f, -1.0f, 0.0f }, + { 0.0f, -1.0f, 0.0f }, + }, + + // UV0 + { + { 0.0f, 1.0f }, + { 0.0f, 0.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + }, + + // UV1 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV2 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV3 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // VertexColors + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + }, +}; + +const FVoxelMeshData FVoxelMeshData::CubeRightFace = +{ + // Vertices + { + { 0.0f, 100.0f, 0.0f }, + { 0.0f, 100.0f, 100.0f }, + { 100.0f, 100.0f, 0.0f }, + { 100.0f, 100.0f, 100.0f }, + }, + + // Triangles + { + 2, 1, 0, + 2, 3, 1, + }, + + // Normals + { + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + }, + + // UV0 + { + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 1.0f }, + { 0.0f, 0.0f }, + }, + + // UV1 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV2 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // UV3 + { + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + + // VertexColors + { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + }, +}; + +void FVoxelMeshData::Reset() +{ + Vertices.Reset(); + Triangles.Reset(); + Normals.Reset(); + UV0.Reset(); + UV1.Reset(); + UV2.Reset(); + UV3.Reset(); + VertexColors.Reset(); +} + +void FVoxelMeshData::Append(const FVoxelMeshData & Source) +{ + Vertices.Append(Source.Vertices); + Triangles.Append(Source.Triangles); + Normals.Append(Source.Normals); + UV0.Append(Source.UV0); + UV1.Append(Source.UV1); + UV2.Append(Source.UV2); + UV3.Append(Source.UV3); + VertexColors.Append(Source.VertexColors); + + for (int32 i = -Source.Triangles.Num(); i < 0; ++i) + Triangles[Triangles.Num() + i] += Vertices.Num() - Source.Vertices.Num(); +} + +void FVoxelMeshData::Append(const FVoxelMeshData & Source, const FVector & LocationOffset) +{ + Append(Source); + + for (int32 i = -Source.Vertices.Num(); i < 0; ++i) + Vertices[Vertices.Num() + i] += LocationOffset; +} diff --git a/Source/Voxel/Private/VoxelSubsystem.cpp b/Source/Voxel/Private/VoxelSubsystem.cpp new file mode 100644 index 0000000..207c38b --- /dev/null +++ b/Source/Voxel/Private/VoxelSubsystem.cpp @@ -0,0 +1,16 @@ +#include "VoxelSubsystem.h" + +#include "VoxelBlock.h" + +UVoxelSubsystem::UVoxelSubsystem(const class FObjectInitializer& ObjectInitializer) +{ + BlockTypes.Add(TEXT("Air"), FVoxelBlockType()); +} + +const FVoxelBlockType & UVoxelSubsystem::GetBlockType(const FName & Name) const +{ + if (BlockTypes.Contains(Name)) + return BlockTypes[Name]; + + return FVoxelBlockType::InvalidBlockType; +} diff --git a/Source/Voxel/Private/VoxelWorld.cpp b/Source/Voxel/Private/VoxelWorld.cpp new file mode 100644 index 0000000..1545482 --- /dev/null +++ b/Source/Voxel/Private/VoxelWorld.cpp @@ -0,0 +1,317 @@ +#include "VoxelWorld.h" + +#include "VoxelLog.h" +#include "VoxelBlock.h" +#include "VoxelChunk.h" +#include "VoxelHelper.h" +#include "VoxelSubsystem.h" +#include "VoxelAgentInterface.h" + +const TArray ChunkLoadOrder = +{ + { 0, 0 }, + { 0, -1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { -1, -1 }, { 1, -1 }, { -1, 1 }, { 1, 1 }, + { 2, 0 }, { -2, 0 }, { 0, -2 }, { 0, 2 }, { -2, 1 }, { 1, 2 }, { 1, -2 }, { -1, -2 }, + { 2, 1 }, { 2, -1 }, { -1, 2 }, { -2, -1 }, { -2, -2 }, { 2, 2 }, { 2, -2 }, { -2, 2 }, + { 0, -3 }, { 3, 0 }, { -3, 0 }, { 0, 3 }, { 3, -1 }, { -3, 1 }, { 1, -3 }, { -1, -3 }, + { -1, 3 }, { 1, 3 }, { 3, 1 }, { -3, -1 }, { 3, -2 }, { 2, -3 }, { -3, 2 }, { -3, -2 }, + { 3, 2 }, { -2, -3 }, { -2, 3 }, { 2, 3 }, { 4, 0 }, { 0, 4 }, { -4, 0 }, { 0, -4 }, + { 4, 1 }, { -1, 4 }, { -4, 1 }, { 4, -1 }, { 1, -4 }, { 1, 4 }, { -4, -1 }, { -1, -4 }, + { 3, -3 }, { -3, 3 }, { -3, -3 }, { 3, 3 }, { 4, -2 }, { -2, 4 }, { -2, -4 }, { -4, -2 }, + { -4, 2 }, { 2, 4 }, { 2, -4 }, { 4, 2 }, { 3, -4 }, { -5, 0 }, { 0, 5 }, { -3, 4 }, + { 4, -3 }, { -4, 3 }, { 5, 0 }, { 4, 3 }, { -4, -3 }, { 0, -5 }, { 3, 4 }, { -3, -4 }, + { -5, -1 }, { -5, 1 }, { 5, -1 }, { 5, 1 }, { -1, 5 }, { -1, -5 }, { 1, -5 }, { 1, 5 }, + { 5, -2 }, { 5, 2 }, { 2, -5 }, { -2, -5 }, { -5, 2 }, { -5, -2 }, { 2, 5 }, { -2, 5 }, + { -4, 4 }, { 4, 4 }, { -4, -4 }, { 4, -4 }, { -5, -3 }, { -3, 5 }, { 5, 3 }, { 3, 5 }, + { -5, 3 }, { 3, -5 }, { 5, -3 }, { -3, -5 }, { -6, 0 }, { 0, 6 }, { 6, 0 }, { 0, -6 }, + { 6, -1 }, { -6, -1 }, { -6, 1 }, { -1, 6 }, { -1, -6 }, { 1, -6 }, { 1, 6 }, { 6, 1 }, + { 2, -6 }, { -6, -2 }, { 6, 2 }, { 2, 6 }, { -6, 2 }, { -2, 6 }, { -2, -6 }, { 6, -2 }, + { 4, -5 }, { 5, -4 }, { -5, 4 }, { 5, 4 }, { -5, -4 }, { -4, -5 }, { 4, 5 }, { -4, 5 }, + { -6, -3 }, { 3, -6 }, { -3, -6 }, { -6, 3 }, { 6, 3 }, { 3, 6 }, { 6, -3 }, { -3, 6 }, + { 0, 7 }, { 0, -7 }, { 7, 0 }, { -7, 0 }, { 5, -5 }, { 1, 7 }, { -7, -1 }, { -7, 1 }, + { 1, -7 }, { -1, -7 }, { -5, -5 }, { 7, 1 }, { 5, 5 }, { -1, 7 }, { 7, -1 }, { -5, 5 }, + { 6, -4 }, { -4, -6 }, { -6, 4 }, { -6, -4 }, { 6, 4 }, { 4, 6 }, { 4, -6 }, { -4, 6 }, + { -2, 7 }, { 7, 2 }, { -2, -7 }, { -7, -2 }, { 2, -7 }, { 2, 7 }, { 7, -2 }, { -7, 2 }, + { -7, 3 }, { 7, 3 }, { -7, -3 }, { -3, -7 }, { -3, 7 }, { 7, -3 }, { 3, 7 }, { 3, -7 }, + { 6, -5 }, { -5, 6 }, { 6, 5 }, { -6, -5 }, { -5, -6 }, { 5, -6 }, { 5, 6 }, { -6, 5 }, + { 0, -8 }, { 0, 8 }, { -8, 0 }, { 8, 0 }, { 7, 4 }, { -4, -7 }, { 7, -4 }, { 1, 8 }, + { -1, -8 }, { 1, -8 }, { 4, -7 }, { 4, 7 }, { -7, -4 }, { -7, 4 }, { -8, 1 }, { -8, -1 }, + { -1, 8 }, { 8, 1 }, { 8, -1 }, { -4, 7 }, { 2, -8 }, { -2, 8 }, { -2, -8 }, { -8, -2 }, + { 8, 2 }, { 2, 8 }, { -8, 2 }, { 8, -2 }, { -6, 6 }, { -6, -6 }, { 6, 6 }, { 6, -6 }, + { 8, -3 }, { 3, 8 }, { -3, 8 }, { 8, 3 }, { 3, -8 }, { -8, -3 }, { -8, 3 }, { -3, -8 }, + { -5, -7 }, { 5, 7 }, { 7, -5 }, { -7, -5 }, { 7, 5 }, { -7, 5 }, { -5, 7 }, { 5, -7 }, + { 4, 8 }, { -4, 8 }, { -8, 4 }, { -8, -4 }, { 4, -8 }, { 8, -4 }, { 8, 4 }, { -4, -8 }, + { 0, -9 }, { 0, 9 }, { -9, 0 }, { 9, 0 }, { -1, -9 }, { 1, 9 }, { 9, -1 }, { -1, 9 }, + { 1, -9 }, { -9, 1 }, { -9, -1 }, { 9, 1 }, { 6, 7 }, { -7, 6 }, { -9, -2 }, { 2, 9 }, + { -2, -9 }, { -2, 9 }, { 9, 2 }, { -6, -7 }, { 7, -6 }, { -6, 7 }, { 7, 6 }, { 6, -7 }, + { 2, -9 }, { 9, -2 }, { -9, 2 }, { -7, -6 }, { 8, -5 }, { -5, 8 }, { -8, -5 }, { 5, 8 }, + { -5, -8 }, { 8, 5 }, { 5, -8 }, { -8, 5 }, { -3, 9 }, { -9, 3 }, { -9, -3 }, { 9, -3 }, + { 3, 9 }, { -3, -9 }, { 3, -9 }, { 9, 3 }, { -4, 9 }, { -9, -4 }, { -4, -9 }, { 4, 9 }, + { 9, 4 }, { 4, -9 }, { -9, 4 }, { 9, -4 }, { 7, 7 }, { -7, 7 }, { -7, -7 }, { 7, -7 }, + { -6, -8 }, { 10, 0 }, { 8, -6 }, { -8, -6 }, { 8, 6 }, { 6, -8 }, { -6, 8 }, { 0, -10 }, + { 0, 10 }, { -8, 6 }, { 6, 8 }, { -10, 0 }, { 10, -1 }, { -10, -1 }, { -1, -10 }, { -10, 1 }, + { -1, 10 }, { 10, 1 }, { 1, 10 }, { 1, -10 }, { 2, 10 }, { -10, 2 }, { -10, -2 }, { -2, 10 }, + { 2, -10 }, { 10, -2 }, { -2, -10 }, { 10, 2 }, { -9, -5 }, { -9, 5 }, { -5, -9 }, { 9, -5 }, + { 9, 5 }, { 5, 9 }, { 5, -9 }, { -5, 9 }, { 3, 10 }, { -10, 3 }, { 3, -10 }, { -3, 10 }, + { 10, -3 }, { -10, -3 }, { 10, 3 }, { -3, -10 }, { 8, -7 }, { 8, 7 }, { 7, 8 }, { -7, 8 }, + { -7, -8 }, { 7, -8 }, { -8, 7 }, { -8, -7 }, { 4, -10 }, { 10, 4 }, { -4, -10 }, { -10, -4 }, + { 10, -4 }, { -4, 10 }, { 4, 10 }, { -10, 4 }, { -6, -9 }, { 6, -9 }, { 6, 9 }, { -9, -6 }, + { -9, 6 }, { 9, -6 }, { 9, 6 }, { -6, 9 }, { 0, 11 }, { 11, 0 }, { 0, -11 }, { -11, 0 }, + { 1, 11 }, { -1, -11 }, { -1, 11 }, { -11, -1 }, { 11, 1 }, { 11, -1 }, { 1, -11 }, { -11, 1 }, + { -5, -10 }, { -2, -11 }, { 11, 2 }, { -10, -5 }, { 2, 11 }, { 10, -5 }, { 5, -10 }, { 11, -2 }, + { -5, 10 }, { -11, 2 }, { 10, 5 }, { 5, 10 }, { 2, -11 }, { -11, -2 }, { -10, 5 }, { -2, 11 }, + { -8, -8 }, { -8, 8 }, { 8, 8 }, { 8, -8 }, { 7, 9 }, { -11, 3 }, { -11, -3 }, { 11, -3 }, + { 9, 7 }, { -3, -11 }, { 3, -11 }, { 7, -9 }, { -9, 7 }, { -7, -9 }, { -9, -7 }, { 11, 3 }, + { 9, -7 }, { -7, 9 }, { -3, 11 }, { 3, 11 }, { 10, -6 }, { 10, 6 }, { -10, -6 }, { -6, -10 }, + { -6, 10 }, { -10, 6 }, { 6, -10 }, { 6, 10 }, { -4, 11 }, { -11, 4 }, { -4, -11 }, { -11, -4 }, + { 11, -4 }, { 11, 4 }, { 4, -11 }, { 4, 11 }, { 12, 0 }, { 0, 12 }, { 0, -12 }, { -12, 0 }, + { 12, -1 }, { 8, -9 }, { -12, -1 }, { 1, 12 }, { -1, -12 }, { -12, 1 }, { 12, 1 }, { 9, -8 }, + { -1, 12 }, { 1, -12 }, { -9, 8 }, { -8, -9 }, { -9, -8 }, { -8, 9 }, { 9, 8 }, { 8, 9 }, + { -11, -5 }, { 5, -11 }, { 11, 5 }, { -5, 11 }, { -5, -11 }, { 5, 11 }, { -11, 5 }, { 11, -5 }, + { 12, -2 }, { 12, 2 }, { -2, 12 }, { -12, -2 }, { -12, 2 }, { -2, -12 }, { 2, -12 }, { 2, 12 }, + { 7, 10 }, { 7, -10 }, { -10, 7 }, { 10, -7 }, { -7, -10 }, { 10, 7 }, { -7, 10 }, { -10, -7 }, + { 12, -3 }, { 12, 3 }, { -3, 12 }, { 3, -12 }, { -12, 3 }, { -12, -3 }, { -3, -12 }, { 3, 12 }, + { -6, 11 }, { -11, -6 }, { -11, 6 }, { 6, 11 }, { 11, 6 }, { -6, -11 }, { 6, -11 }, { 11, -6 }, + { 12, -4 }, { 12, 4 }, { -12, 4 }, { 4, -12 }, { -4, -12 }, { -12, -4 }, { -4, 12 }, { 4, 12 }, + { 9, -9 }, { -9, -9 }, { 9, 9 }, { -9, 9 }, { -10, -8 }, { 8, -10 }, { -10, 8 }, { 10, -8 }, + { -8, 10 }, { 10, 8 }, { -8, -10 }, { 8, 10 }, { 12, -5 }, { 5, -12 }, { 0, -13 }, { 13, 0 }, + { -5, 12 }, { 5, 12 }, { -12, 5 }, { 0, 13 }, { -5, -12 }, { 12, 5 }, { -12, -5 }, { -13, 0 }, + { -11, -7 }, { 7, -11 }, { 11, -7 }, { -13, -1 }, { -13, 1 }, { 13, -1 }, { 13, 1 }, { 11, 7 }, + { -1, -13 }, { -7, 11 }, { -7, -11 }, { -1, 13 }, { 1, 13 }, { 1, -13 }, { 7, 11 }, { -11, 7 }, + { -2, 13 }, { 2, 13 }, { -13, 2 }, { 2, -13 }, { -2, -13 }, { 13, -2 }, { -13, -2 }, { 13, 2 }, + { -13, 3 }, { 3, 13 }, { 3, -13 }, { -13, -3 }, { -3, 13 }, { -3, -13 }, { 13, -3 }, { 13, 3 }, + { 6, -12 }, { -12, 6 }, { 12, -6 }, { -6, -12 }, { -12, -6 }, { -6, 12 }, { 6, 12 }, { 12, 6 }, + { 10, -9 }, { -10, 9 }, { -9, -10 }, { 9, -10 }, { 9, 10 }, { -9, 10 }, { -10, -9 }, { 10, 9 }, + { 8, 11 }, { 8, -11 }, { 11, -8 }, { 13, 4 }, { -8, 11 }, { -8, -11 }, { 4, 13 }, { -4, -13 }, + { -4, 13 }, { 11, 8 }, { 13, -4 }, { -11, -8 }, { -13, 4 }, { -11, 8 }, { 4, -13 }, { -13, -4 }, + { 12, -7 }, { 12, 7 }, { -12, -7 }, { 7, -12 }, { 7, 12 }, { -12, 7 }, { -7, 12 }, { -7, -12 }, + { 13, 5 }, { -5, 13 }, { -13, -5 }, { 5, -13 }, { -13, 5 }, { 5, 13 }, { 13, -5 }, { -5, -13 }, + { -14, 0 }, { 0, 14 }, { 0, -14 }, { 14, 0 }, { -14, 1 }, { 1, -14 }, { -14, -1 }, { -1, 14 }, + { -1, -14 }, { 1, 14 }, { 14, -1 }, { 14, 1 }, { -2, -14 }, { 2, -14 }, { 2, 14 }, { -10, -10 }, + { -14, 2 }, { 10, 10 }, { -14, -2 }, { 10, -10 }, { -2, 14 }, { -10, 10 }, { 14, -2 }, { 14, 2 }, + { 11, 9 }, { 11, -9 }, { -11, -9 }, { -11, 9 }, { 9, 11 }, { -9, 11 }, { -9, -11 }, { 9, -11 }, + { 6, -13 }, { -13, -6 }, { -14, 3 }, { -6, 13 }, { 6, 13 }, { 3, -14 }, { -3, 14 }, { -14, -3 }, + { 13, 6 }, { 14, -3 }, { 13, -6 }, { 3, 14 }, { -6, -13 }, { -13, 6 }, { -3, -14 }, { 14, 3 }, + { -8, -12 }, { 8, 12 }, { -8, 12 }, { -12, 8 }, { 12, -8 }, { -12, -8 }, { 8, -12 }, { 12, 8 }, + { 4, 14 }, { -14, 4 }, { 14, 4 }, { 14, -4 }, { -4, -14 }, { 4, -14 }, { -4, 14 }, { -14, -4 }, + { 7, 13 }, { -7, -13 }, { -13, 7 }, { -13, -7 }, { 13, -7 }, { -7, 13 }, { 7, -13 }, { 13, 7 }, + { -10, -11 }, { 5, -14 }, { -11, -10 }, { 5, 14 }, { -11, 10 }, { 14, -5 }, { -14, 5 }, { -14, -5 }, + { 10, -11 }, { 10, 11 }, { 11, -10 }, { 11, 10 }, { -5, 14 }, { -5, -14 }, { -10, 11 }, { 14, 5 }, + { 0, 15 }, { 9, 12 }, { -12, 9 }, { -9, 12 }, { 15, 0 }, { 12, -9 }, { 12, 9 }, { 0, -15 }, + { -15, 0 }, { -12, -9 }, { 9, -12 }, { -9, -12 }, { 1, 15 }, { 15, 1 }, { -15, -1 }, { -1, 15 }, + { -1, -15 }, { 15, -1 }, { -15, 1 }, { 1, -15 }, { -15, -2 }, { 2, -15 }, { 15, 2 }, { -2, -15 }, + { 2, 15 }, { 15, -2 }, { -2, 15 }, { -15, 2 }, { -6, 14 }, { 14, -6 }, { -14, 6 }, { -6, -14 }, + { 6, 14 }, { -14, -6 }, { 6, -14 }, { 14, 6 }, { 8, 13 }, { 13, 8 }, { -8, -13 }, { 13, -8 }, + { -13, -8 }, { -8, 13 }, { -13, 8 }, { 8, -13 }, { 3, 15 }, { 15, 3 }, { -3, -15 }, { -15, -3 }, + { -3, 15 }, { -15, 3 }, { 3, -15 }, { 15, -3 }, { 15, 4 }, { 4, -15 }, { -4, -15 }, { 4, 15 }, + { -4, 15 }, { 15, -4 }, { -15, 4 }, { -15, -4 }, { -11, -11 }, { 11, -11 }, { -11, 11 }, { 11, 11 }, + { 12, 10 }, { -12, 10 }, { -10, -12 }, { -10, 12 }, { 10, -12 }, { 10, 12 }, { -12, -10 }, { 12, -10 }, + { 7, -14 }, { 14, 7 }, { -14, 7 }, { 14, -7 }, { 7, 14 }, { -14, -7 }, { -7, -14 }, { -7, 14 }, + { -15, 5 }, { -13, -9 }, { -5, -15 }, { 13, -9 }, { -9, -13 }, { 9, -13 }, { 5, 15 }, { -9, 13 }, + { 9, 13 }, { 13, 9 }, { -5, 15 }, { -13, 9 }, { 5, -15 }, { 15, 5 }, { -15, -5 }, { 15, -5 }, + { 0, 16 }, { -16, 0 }, { 0, -16 }, { 16, 0 }, { -16, 1 }, { -16, -1 }, { 1, 16 }, { -1, 16 }, + { 1, -16 }, { -1, -16 }, { 16, -1 }, { 16, 1 }, { 2, -16 }, { -14, 8 }, { -2, 16 }, { 8, 14 }, + { 14, 8 }, { -2, -16 }, { -8, -14 }, { -8, 14 }, { -14, -8 }, { 16, 2 }, { 16, -2 }, { 2, 16 }, + { 14, -8 }, { -16, 2 }, { -16, -2 }, { 8, -14 }, { 6, 15 }, { 6, -15 }, { -6, -15 }, { 15, -6 }, + { -15, 6 }, { 15, 6 }, { -15, -6 }, { -6, 15 }, { -3, 16 }, { 3, 16 }, { 11, 12 }, { 12, -11 }, + { 12, 11 }, { -11, 12 }, { 16, -3 }, { 16, 3 }, { -16, 3 }, { -16, -3 }, { -3, -16 }, { -12, -11 }, + { 11, -12 }, { -11, -12 }, { -12, 11 }, { 3, -16 }, { -10, -13 }, { -10, 13 }, { -13, 10 }, { 13, -10 }, + { 10, -13 }, { 10, 13 }, { 13, 10 }, { -13, -10 }, { 16, -4 }, { -4, -16 }, { 16, 4 }, { 4, 16 }, + { 4, -16 }, { -4, 16 }, { -16, -4 }, { -16, 4 }, { 7, -15 }, { 15, -7 }, { -7, 15 }, { 15, 7 }, + { 7, 15 }, { -7, -15 }, { -15, 7 }, { -15, -7 }, { -14, 9 }, { -9, -14 }, { 9, -14 }, { -9, 14 }, + { 14, 9 }, { 14, -9 }, { 9, 14 }, { -14, -9 }, { -16, -5 }, { -5, -16 }, { 5, 16 }, { 16, -5 }, + { 5, -16 }, { -5, 16 }, { -16, 5 }, { 16, 5 }, { -12, 12 }, { 12, 12 }, { 12, -12 }, { -12, -12 }, + { -15, -8 }, { 8, 15 }, { 8, -15 }, { -8, 15 }, { -8, -15 }, { 15, -8 }, { 15, 8 }, { -15, 8 }, + { 13, -11 }, { 11, -13 }, { -11, 13 }, { -11, -13 }, { 11, 13 }, { 13, 11 }, { -13, 11 }, { -13, -11 }, + { 16, -6 }, { -6, 16 }, { 6, -16 }, { -16, 6 }, { 16, 6 }, { -16, -6 }, { 6, 16 }, { -6, -16 }, + { 14, 10 }, { -10, 14 }, { 14, -10 }, { -10, -14 }, { 10, -14 }, { -14, 10 }, { -14, -10 }, { 10, 14 }, + { -16, -7 }, { 16, -7 }, { -7, -16 }, { -7, 16 }, { 16, 7 }, { 7, 16 }, { -16, 7 }, { 7, -16 }, + { 9, -15 }, { 15, -9 }, { 9, 15 }, { 15, 9 }, { -15, 9 }, { -15, -9 }, { -9, -15 }, { -9, 15 }, + { 13, -12 }, { -12, 13 }, { -12, -13 }, { 13, 12 }, { 12, -13 }, { -13, -12 }, { 12, 13 }, { -13, 12 }, + { -14, 11 }, { 14, -11 }, { -14, -11 }, { -11, -14 }, { -11, 14 }, { 11, -14 }, { 14, 11 }, { 11, 14 }, + { -16, -8 }, { -16, 8 }, { -8, 16 }, { 8, -16 }, { 16, -8 }, { 8, 16 }, { -8, -16 }, { 16, 8 }, + { 15, 10 }, { -10, -15 }, { -15, 10 }, { -15, -10 }, { 10, 15 }, { 10, -15 }, { 15, -10 }, { -10, 15 }, + { 9, 16 }, { -9, -16 }, { 16, -9 }, { -16, -9 }, { -9, 16 }, { 9, -16 }, { -16, 9 }, { 16, 9 }, + { -13, -13 }, { -13, 13 }, { 13, 13 }, { 13, -13 }, { 12, 14 }, { -12, 14 }, { -12, -14 }, { 14, 12 }, + { 12, -14 }, { 14, -12 }, { -14, -12 }, { -14, 12 }, { 11, -15 }, { -11, -15 }, { -15, -11 }, { -11, 15 }, + { 15, 11 }, { 15, -11 }, { -15, 11 }, { 11, 15 }, { -10, 16 }, { -16, -10 }, { -10, -16 }, { 16, -10 }, + { 10, -16 }, { 10, 16 }, { 16, 10 }, { -16, 10 }, { 14, -13 }, { 13, 14 }, { 14, 13 }, { 13, -14 }, + { -13, -14 }, { -14, -13 }, { -13, 14 }, { -14, 13 }, { -15, -12 }, { 15, -12 }, { -12, -15 }, { -15, 12 }, + { -12, 15 }, { 12, -15 }, { 12, 15 }, { 15, 12 }, { -11, 16 }, { 16, -11 }, { 11, -16 }, { -11, -16 }, + { -16, 11 }, { -16, -11 }, { 11, 16 }, { 16, 11 }, { 14, 14 }, { -14, -14 }, { 14, -14 }, { -14, 14 }, + { 15, -13 }, { -13, -15 }, { -15, 13 }, { -13, 15 }, { 15, 13 }, { -15, -13 }, { 13, -15 }, { 13, 15 }, + { 16, -12 }, { 12, -16 }, { -12, -16 }, { 12, 16 }, { -12, 16 }, { -16, -12 }, { -16, 12 }, { 16, 12 }, + { -15, 14 }, { 14, -15 }, { -15, -14 }, { -14, 15 }, { 14, 15 }, { -14, -15 }, { 15, -14 }, { 15, 14 }, + { 16, -13 }, { 13, -16 }, { -16, 13 }, { 13, 16 }, { -16, -13 }, { -13, -16 }, { -13, 16 }, { 16, 13 }, + { -15, 15 }, { -15, -15 }, { 15, -15 }, { 15, 15 }, { 16, -14 }, { -16, 14 }, { 14, -16 }, { -16, -14 }, + { -14, -16 }, { -14, 16 }, { 14, 16 }, { 16, 14 }, { 16, -15 }, { -15, -16 }, { -16, 15 }, { -15, 16 }, + { -16, -15 }, { 15, -16 }, { 15, 16 }, { 16, 15 }, { -16, -16 }, { -16, 16 }, { 16, -16 }, { 16, 16 }, +}; + +AVoxelWorld::AVoxelWorld(const class FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + PrimaryActorTick.bCanEverTick = true; +} + +void AVoxelWorld::BeginPlay() +{ + Super::BeginPlay(); + + UGameInstance* GameInstance = GetGameInstance(); + VoxelSubsystem = GameInstance->GetSubsystem(); +} + +void AVoxelWorld::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + ManageChunk(); + + FlushMeshs(); + +} + +void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + TArray ChunksToUnload; + Chunks.GetKeys(ChunksToUnload); + + for (const FIntPoint& Chunk : ChunksToUnload) + UnloadChunk(Chunk); + + Super::EndPlay(EndPlayReason); +} + +const FVoxelBlock & AVoxelWorld::GetBlockByWorldLocation(const FIntVector & Location) const +{ + FIntPoint ChunkLocation; + FIntVector RelativeLocation; + + UVoxelHelper::WorldToRelativeLocation(Location, ChunkLocation, RelativeLocation); + + if (!Chunks.Contains(ChunkLocation)) + { + UE_LOG(LogVoxel, Warning, TEXT("The Chunk %d, %d Is Not Loaded, Block %d, %d, %d Is Invalid"), ChunkLocation.X, ChunkLocation.Y, Location.X, Location.Y, Location.Z); + return FVoxelBlock::InvalidBlock; + } + + return Chunks[ChunkLocation]->GetBlockByRelativeLocation(RelativeLocation); +} + +void AVoxelWorld::SetBlockByWorldLocation(const FIntVector& Location, const FVoxelBlock& NewBlock) +{ + FIntPoint ChunkLocation; + FIntVector RelativeLocation; + + UVoxelHelper::WorldToRelativeLocation(Location, ChunkLocation, RelativeLocation); + + if (!Chunks.Contains(ChunkLocation)) + { + UE_LOG(LogVoxel, Warning, TEXT("The Chunk %d, %d Is Not Loaded, Block %d, %d, %d Is Invalid"), ChunkLocation.X, ChunkLocation.Y, Location.X, Location.Y, Location.Z); + return; + } + + Chunks[ChunkLocation]->SetBlockByRelativeLocation(RelativeLocation, NewBlock); +} + +void AVoxelWorld::LoadChunk(const FIntPoint & ChunkLocation) +{ + if (Chunks.Contains(ChunkLocation)) return; + + UWorld* World = GetWorld(); + + check(World); + + AVoxelChunk* NewVoxelChunk = World->SpawnActor(); + NewVoxelChunk->VoxelWorld = this; + NewVoxelChunk->ChunkLocation = ChunkLocation; + NewVoxelChunk->SetActorLocation(FVector(ChunkLocation.X * 1600.0f, ChunkLocation.Y * 1600.0f, 0.0f)); + NewVoxelChunk->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform); + NewVoxelChunk->FlushMaterials(); + NewVoxelChunk->Load(); + AddChunkToMeshFlushBuffer(ChunkLocation); + + Chunks.Add(ChunkLocation, NewVoxelChunk); + + UE_LOG(LogVoxel, Log, TEXT("The Chunk %d, %d Is Loaded"), ChunkLocation.X, ChunkLocation.Y); +} + +void AVoxelWorld::UnloadChunk(const FIntPoint & ChunkLocation) +{ + if (!Chunks.Contains(ChunkLocation)) return; + + Chunks[ChunkLocation]->Unload(); + Chunks[ChunkLocation]->Destroy(); + + Chunks.Remove(ChunkLocation); + + UE_LOG(LogVoxel, Log, TEXT("The Chunk %d, %d Is Unload"), ChunkLocation.X, ChunkLocation.Y); +} + +void AVoxelWorld::FlushMaterials() +{ + for (const TPair& Chunk : Chunks) + { + Chunk.Value->FlushMaterials(); + } +} + +void AVoxelWorld::ManageChunk() +{ + FIntPoint RootChunk; + FIntVector RLocation; + UVoxelHelper::WorldToRelativeLocation(IVoxelAgentInterface::Execute_GetAgentLocation(CenterAgent.GetObject()), RootChunk, RLocation); + + // Load + for (const FIntPoint& Chunk : ChunkLoadOrder) + { + const FIntPoint WorldLocation = Chunk + RootChunk; + const FIntPoint RelativeLocation = Chunk; + + const bool bChunkLoaded = Chunks.Contains(WorldLocation); + const bool bInLoadDistance = FMath::Abs(RelativeLocation.X) < VoxelSubsystem->ChunkLoadDistance && FMath::Abs(RelativeLocation.Y) < VoxelSubsystem->ChunkLoadDistance; + + if (!bChunkLoaded && bInLoadDistance) + { + LoadChunk(WorldLocation); + break; + } + } + + // Unload + int32 ChunkUnloadDistance = VoxelSubsystem->ChunkLoadDistance + VoxelSubsystem->ChunkUnloadBuffer; + for (const TPair& Chunk : Chunks) + { + const FIntPoint WorldLocation = Chunk.Key; + const FIntPoint RelativeLocation = WorldLocation - RootChunk; + + const bool bInUnloadDistance = FMath::Abs(RelativeLocation.X) > ChunkUnloadDistance && FMath::Abs(RelativeLocation.Y) > ChunkUnloadDistance; + + if (bInUnloadDistance) + { + UnloadChunk(WorldLocation); + break; + } + } +} + +void AVoxelWorld::FlushMeshs() +{ + QUICK_SCOPE_CYCLE_COUNTER(STAT_VoxelWorld_FlushMeshs); + + for (const FIntPoint& ChunkLocation : MeshFlushBuffer) + { + if (!Chunks.Contains(ChunkLocation)) + continue; + + AVoxelChunk* ChunkActor = Chunks[ChunkLocation]; + + if (!IsValid(ChunkActor)) + continue; + + ChunkActor->FlushMeshs(); + } + + MeshFlushBuffer.Reset(); +} diff --git a/Source/Voxel/Public/Blueprint/VoxelBlueprintLibrary.h b/Source/Voxel/Public/Blueprint/VoxelBlueprintLibrary.h new file mode 100644 index 0000000..088abf1 --- /dev/null +++ b/Source/Voxel/Public/Blueprint/VoxelBlueprintLibrary.h @@ -0,0 +1,20 @@ +#pragma once + +#include "CoreMinimal.h" +#include "VoxelBlock.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelBlueprintLibrary.generated.h" + +struct FVoxelBlockType; + +UCLASS() +class UVoxelBlueprintLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Voxel|Subsystem", meta = (WorldContext = "WorldContextObject")) + static void AddBlockType(const UObject* WorldContextObject, const FName& Name, const FVoxelBlockType& BlockType); + +}; diff --git a/Source/Voxel/Public/Voxel.h b/Source/Voxel/Public/Voxel.h new file mode 100644 index 0000000..319c669 --- /dev/null +++ b/Source/Voxel/Public/Voxel.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FVoxelModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Source/Voxel/Public/VoxelAgentInterface.h b/Source/Voxel/Public/VoxelAgentInterface.h new file mode 100644 index 0000000..4673fa5 --- /dev/null +++ b/Source/Voxel/Public/VoxelAgentInterface.h @@ -0,0 +1,27 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "VoxelAgentInterface.generated.h" + +UINTERFACE(MinimalAPI) +class UVoxelAgentInterface : public UInterface +{ + GENERATED_BODY() +}; + +class VOXEL_API IVoxelAgentInterface +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Voxel|Agent") + FIntVector GetAgentLocation() const; + FIntVector GetAgentLocation_Implementation() const; + + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Voxel|Agent") + FVector GetAgentPartialLocation() const; + FVector GetAgentPartialLocation_Implementation() const; + +}; diff --git a/Source/Voxel/Public/VoxelBlock.h b/Source/Voxel/Public/VoxelBlock.h new file mode 100644 index 0000000..a5e98ae --- /dev/null +++ b/Source/Voxel/Public/VoxelBlock.h @@ -0,0 +1,66 @@ +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMesh.h" +#include "UObject/Object.h" +#include "VoxelBlock.generated.h" + +class AVoxelWorld; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelBlock +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + FName Type; + + FVoxelBlock() : FVoxelBlock(FName(TEXT("Air"))) { } + + FVoxelBlock(FName InType) : Type(InType) { } + + const static FVoxelBlock InvalidBlock; + + bool IsValidBlock() const { return Type != NAME_None; } +}; + +UENUM(BlueprintType) +enum class EVoxelBlockShape : uint8 +{ + Invalid, + Air, + Cube, +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelBlockType +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + EVoxelBlockShape Shape; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + FVector2D CustomTopFaceUV; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + FVector2D CustomBottomFaceUV; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + FVector2D CustomFrontFaceUV; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + FVector2D CustomBackFaceUV; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + FVector2D CustomLeftFaceUV; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Block") + FVector2D CustomRightFaceUV; + + FVoxelBlockType() : FVoxelBlockType(EVoxelBlockShape::Air) { } + + FVoxelBlockType(EVoxelBlockShape InShape) : Shape(InShape) { } + + const static FVoxelBlockType InvalidBlockType; +}; diff --git a/Source/Voxel/Public/VoxelChunk.h b/Source/Voxel/Public/VoxelChunk.h new file mode 100644 index 0000000..8b6648c --- /dev/null +++ b/Source/Voxel/Public/VoxelChunk.h @@ -0,0 +1,74 @@ +#pragma once + +#include "CoreMinimal.h" +#include "VoxelBlock.h" +#include "GameFramework/Actor.h" +#include "VoxelChunk.generated.h" + +class AVoxelWorld; +class UVoxelSubsystem; +class UProceduralMeshComponent; + +const FString ChunkFileExtension = TEXT(".voxelchunk"); + +UCLASS(BlueprintType) +class VOXEL_API AVoxelChunk : public AActor +{ + GENERATED_BODY() + + friend AVoxelWorld; + +public: + + AVoxelChunk(const class FObjectInitializer& ObjectInitializer); + + virtual void BeginPlay() override; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Chunk") + AVoxelWorld* GetVoxelWorld() const { return VoxelWorld; } + + UFUNCTION(BlueprintCallable, Category = "Voxel|Chunk") + FIntPoint GetChunkLocation() const { return ChunkLocation; } + + UFUNCTION(BlueprintCallable, Category = "Voxel|Chunk") + const FVoxelBlock& GetBlockByRelativeLocation(const FIntVector& Location) const; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Chunk") + void SetBlockByRelativeLocation(const FIntVector& Location, const FVoxelBlock& NewBlock); + + UFUNCTION(CallInEditor, BlueprintCallable, Category = "Voxel|Chunk") + void FlushMeshs(); + + UFUNCTION(CallInEditor, BlueprintCallable, Category = "Voxel|Chunk") + void FlushMaterials(); + +private: + + UPROPERTY() + UVoxelSubsystem* VoxelSubsystem; + + UPROPERTY() + AVoxelWorld* VoxelWorld; + + UPROPERTY() + TArray MeshComponents; + + FString ArchiveFile; + + FIntPoint ChunkLocation; + + uint16 FlushMeshFlags; + + FVoxelBlock Blocks[16][16][256]; + + FVoxelMeshData MeshSectionBuffer; + +private: + + void Load(); + + void Unload(); + + void FlushMeshSection(int32 SectionIndex); + +}; diff --git a/Source/Voxel/Public/VoxelHelper.h b/Source/Voxel/Public/VoxelHelper.h new file mode 100644 index 0000000..9c017c1 --- /dev/null +++ b/Source/Voxel/Public/VoxelHelper.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CoreMinimal.h" +#include "VoxelWorld.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelHelper.generated.h" + +class AVoxelWorld; + +UCLASS() +class UVoxelHelper : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Voxel|Helper", meta = (WorldContext = "WorldContextObject")) + static AVoxelWorld* CreateVoxelWorld(UObject* WorldContextObject, const FVoxelWorldSetting& WorldSetting); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Helper") + static void WorldToRelativeLocation(const FIntVector& InWorldLocation, FIntPoint& OutChunkLocation, FIntVector& OutRelativeLocation); + +}; diff --git a/Source/Voxel/Public/VoxelLog.h b/Source/Voxel/Public/VoxelLog.h new file mode 100644 index 0000000..5fd2279 --- /dev/null +++ b/Source/Voxel/Public/VoxelLog.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Logging/LogMacros.h" + +VOXEL_API DECLARE_LOG_CATEGORY_EXTERN(LogVoxel, Log, All); diff --git a/Source/Voxel/Public/VoxelMesh.h b/Source/Voxel/Public/VoxelMesh.h new file mode 100644 index 0000000..6023959 --- /dev/null +++ b/Source/Voxel/Public/VoxelMesh.h @@ -0,0 +1,47 @@ +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMesh.generated.h" + +USTRUCT(BlueprintType) +struct FVoxelMeshData +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray Vertices; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray Triangles; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray Normals; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray UV0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray UV1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray UV2; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray UV3; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Mesh") + TArray VertexColors; + + const static FVoxelMeshData CubeTopFace; + const static FVoxelMeshData CubeBottomFace; + const static FVoxelMeshData CubeFrontFace; + const static FVoxelMeshData CubeBackFace; + const static FVoxelMeshData CubeLeftFace; + const static FVoxelMeshData CubeRightFace; + + void Reset(); + + void Append(const FVoxelMeshData& Source); + + void Append(const FVoxelMeshData& Source, const FVector& LocationOffset); +}; diff --git a/Source/Voxel/Public/VoxelSubsystem.h b/Source/Voxel/Public/VoxelSubsystem.h new file mode 100644 index 0000000..b50ed9a --- /dev/null +++ b/Source/Voxel/Public/VoxelSubsystem.h @@ -0,0 +1,33 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "VoxelSubsystem.generated.h" + +struct FVoxelBlockType; + +UCLASS() +class UVoxelSubsystem : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + + UVoxelSubsystem(const class FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Subsystem") + int32 ChunkLoadDistance = 16; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Subsystem") + int32 ChunkUnloadBuffer = 2; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Subsystem") + TArray Materials; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Subsystem") + TMap BlockTypes; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Subsystem") + const FVoxelBlockType& GetBlockType(const FName& Name) const; + +}; diff --git a/Source/Voxel/Public/VoxelWorld.h b/Source/Voxel/Public/VoxelWorld.h new file mode 100644 index 0000000..9e3773e --- /dev/null +++ b/Source/Voxel/Public/VoxelWorld.h @@ -0,0 +1,88 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Info.h" +#include "VoxelWorld.generated.h" + +struct FVoxelBlock; + +class AVoxelChunk; +class UVoxelHelper; +class UVoxelBlockType; +class UVoxelSubsystem; +class IVoxelAgentInterface; + +USTRUCT(BlueprintType) +struct FVoxelWorldSetting +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|World") + FString ArchiveFolder; + +}; + +UCLASS(BlueprintType) +class VOXEL_API AVoxelWorld : public AInfo +{ + GENERATED_BODY() + + friend UVoxelHelper; + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|World") + TScriptInterface CenterAgent; + + AVoxelWorld(const class FObjectInitializer& ObjectInitializer); + + virtual void BeginPlay() override; + + virtual void Tick(float DeltaSeconds) override; + + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + + UFUNCTION(BlueprintCallable, Category = "Voxel|World") + const FVoxelWorldSetting& GetWorldSetting() const { return WorldSetting; } + + UFUNCTION(BlueprintCallable, Category = "Voxel|World") + const TMap& GetChunks() const { return Chunks; } + + UFUNCTION(BlueprintCallable, Category = "Voxel|World") + const FVoxelBlock& GetBlockByWorldLocation(const FIntVector& Location) const; + + UFUNCTION(BlueprintCallable, Category = "Voxel|World") + void SetBlockByWorldLocation(const FIntVector& Location, const FVoxelBlock& NewBlock); + + UFUNCTION(BlueprintCallable, Category = "Voxel|World") + void AddChunkToMeshFlushBuffer(const FIntPoint& ChunkLocation) { MeshFlushBuffer.Add(ChunkLocation); } + + UFUNCTION(CallInEditor, BlueprintCallable, Category = "Voxel|World") + void FlushMaterials(); + +public: + + void LoadChunk(const FIntPoint& ChunkLocation); + void UnloadChunk(const FIntPoint& ChunkLocation); + +private: + + UPROPERTY(EditAnywhere, Category = "Voxel|World") + FVoxelWorldSetting WorldSetting; + + UPROPERTY() + UVoxelSubsystem* VoxelSubsystem; + + UPROPERTY() + TMap Chunks; + + UPROPERTY() + TSet MeshFlushBuffer; + +private: + + void ManageChunk(); + + void FlushMeshs(); + +}; diff --git a/Source/Voxel/Voxel.Build.cs b/Source/Voxel/Voxel.Build.cs new file mode 100644 index 0000000..d1f207a --- /dev/null +++ b/Source/Voxel/Voxel.Build.cs @@ -0,0 +1,54 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class Voxel : ModuleRules +{ + public Voxel(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "ProceduralMeshComponent", + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Voxel.uplugin b/Voxel.uplugin new file mode 100644 index 0000000..4ebeb71 --- /dev/null +++ b/Voxel.uplugin @@ -0,0 +1,30 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "Voxel", + "Description": "", + "Category": "Other", + "CreatedBy": "_Redstone_c_", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "Voxel", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "ProceduralMeshComponent", + "Enabled": true + } + ] +} \ No newline at end of file