实现基本 Cube 体素渲染

master
_Redstone_c_ 3 years ago
commit 96e9af4438

15
.gitignore vendored

@ -0,0 +1,15 @@
Binaries
DerivedDataCache
Intermediate
Saved
Build
.vscode
.vs
*.VC.db
*.opensdf
*.opendb
*.sdf
*.sln
*.suo
*.xcodeproj
*.xcworkspace

@ -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.

@ -0,0 +1,4 @@
## UE4 Plugin: Voxel
[Voxel](http://gitblit.myredstone.top/summary/UE4-Plugins!Voxel.git) 是开发中的一个体素插件。
目前支持的 UE4 版本4.25.4

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -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<UVoxelSubsystem>();
LockstepSubsystem->BlockTypes.Add(Name, BlockType);
}

@ -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)

@ -0,0 +1,11 @@
#include "VoxelAgentInterface.h"
FIntVector IVoxelAgentInterface::GetAgentLocation_Implementation() const
{
return FIntVector(0);
}
FVector IVoxelAgentInterface::GetAgentPartialLocation_Implementation() const
{
return FVector(0.0f);
}

@ -0,0 +1,7 @@
#include "VoxelBlock.h"
#include "VoxelWorld.h"
const FVoxelBlock FVoxelBlock::InvalidBlock = FVoxelBlock(NAME_None);
const FVoxelBlockType FVoxelBlockType::InvalidBlockType = FVoxelBlockType(EVoxelBlockShape::Invalid);

@ -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<USceneComponent>(TEXT("RootComponent"));
MeshComponents.SetNum(16);
for (int32 i = 0; i < 16; ++i)
{
FString ComponentName = FString::Printf(TEXT("MeshComponent_%d"), i);
MeshComponents[i] = CreateDefaultSubobject<UProceduralMeshComponent>(FName(ComponentName));
MeshComponents[i]->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
}
FlushMeshFlags = -1;
}
void AVoxelChunk::BeginPlay()
{
UGameInstance* GameInstance = GetGameInstance();
VoxelSubsystem = GameInstance->GetSubsystem<UVoxelSubsystem>();
}
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<FIntPoint, AVoxelChunk*>& 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<uint8> 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<uint8> 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<FIntPoint, AVoxelChunk*>& 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<FProcMeshTangent>(),
true
);
}

@ -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<AVoxelWorld>();
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;
}
}

@ -0,0 +1,3 @@
#include "VoxelLog.h"
DEFINE_LOG_CATEGORY(LogVoxel);

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

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

@ -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<FIntPoint> 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<UVoxelSubsystem>();
}
void AVoxelWorld::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
ManageChunk();
FlushMeshs();
}
void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
TArray<FIntPoint> 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<AVoxelChunk>();
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<FIntPoint, AVoxelChunk*>& 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<FIntPoint, AVoxelChunk*>& 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();
}

@ -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
{