├── Resources └── Icon128.png ├── Source └── VoxelGeometry │ ├── Private │ ├── DensityFunctions.cpp │ ├── PerlinNoise.cpp │ ├── QEF.cpp │ ├── VoxelChunkActor.cpp │ ├── VoxelGeometry.cpp │ ├── VoxelGeometryFunctionLibrary.cpp │ ├── VoxelGeometryPrivatePCH.h │ ├── VoxelOctree.cpp │ └── VoxelSVD.cpp │ ├── Public │ ├── DensityFunctions.h │ ├── PerlinNoise.h │ ├── QEF.h │ ├── VoxelChunkActor.h │ ├── VoxelGeometry.h │ ├── VoxelGeometryFunctionLibrary.h │ ├── VoxelOctree.h │ └── VoxelSVD.h │ └── VoxelGeometry.Build.cs └── VoxelGeometry.uplugin /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ForestCSharp/UE4-Voxel-Geometry/5cfd1c89940ab067d0dff9e9b695d2a06af2a60a/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/DensityFunctions.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "VoxelGeometryPrivatePCH.h" 4 | #include "VoxelGeometry.h" 5 | #include "DensityFunctions.h" 6 | #include "PerlinNoise.h" 7 | #include "UnrealMath.h" 8 | 9 | // ---------------------------------------------------------------------------- 10 | 11 | float Sphere(const FVector& worldPosition, const FVector& origin, float radius) 12 | { 13 | return 0.0f; 14 | return ((worldPosition - origin).Size() - radius); 15 | } 16 | 17 | // ---------------------------------------------------------------------------- 18 | 19 | float Cuboid(const FVector& worldPosition, const FVector& origin, const FVector& halfDimensions) 20 | { 21 | const FVector& pos = worldPosition - origin; 22 | 23 | const FVector& d = pos.GetAbs() - halfDimensions; 24 | const float m = FMath::Max(d.X, FMath::Max(d.Y, d.Z)); 25 | 26 | return 0.0f; 27 | return FMath::Min(FMath::Max(d.X, FMath::Max(d.Y, d.Z)), 0.0f) + d.ComponentMax(FVector(0.0f)).Size(); 28 | } 29 | 30 | // ---------------------------------------------------------------------------- 31 | 32 | static PerlinNoise PN; 33 | 34 | float FractalNoise( 35 | const int octaves, 36 | const float frequency, 37 | const float lacunarity, 38 | const float persistence, 39 | const FVector2D& position) 40 | { 41 | const float SCALE = 1.f / 128.f; 42 | FVector2D p = position * SCALE; 43 | float noise = 0.f; 44 | 45 | float amplitude = 1.f; 46 | p *= frequency; 47 | 48 | for (int i = 0; i < octaves; i++) 49 | { 50 | noise += PN.noise(FVector(p, 0.0f)) * amplitude; 51 | p *= lacunarity; 52 | amplitude *= persistence; 53 | } 54 | 55 | // move into [0, 1] range 56 | return 0.5f + (0.5f * noise); 57 | } 58 | 59 | // ---------------------------------------------------------------------------- 60 | 61 | float Density_Func(const FVector& worldPosition) 62 | { 63 | return (PN.Octave(worldPosition * .005, 3, 0.5) - 0.5f); 64 | 65 | //const float MAX_HEIGHT = 20.f; 66 | //const float noise = FractalNoise(4, 0.5343f, 2.2324f, 0.68324f, FVector2D(worldPosition.X, worldPosition.Z)); 67 | //const float terrain = worldPosition.Z - (MAX_HEIGHT * noise); 68 | 69 | //const float cube = Cuboid(worldPosition, FVector(-4., 10.f, -4.f), FVector(12.f)); 70 | //const float sphere = Sphere(worldPosition, FVector(15.f, 2.5f, 1.f), 16.f); 71 | 72 | //return terrain; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/PerlinNoise.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "VoxelGeometryPrivatePCH.h" 4 | #include "VoxelGeometry.h" 5 | #include "PerlinNoise.h" 6 | 7 | #include "AllowWindowsPlatformTypes.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "HideWindowsPlatformTypes.h" 13 | 14 | // THIS IS A DIRECT TRANSLATION TO C++11 FROM THE REFERENCE 15 | // JAVA IMPLEMENTATION OF THE IMPROVED PERLIN FUNCTION (see http://mrl.nyu.edu/~perlin/noise/) 16 | // THE ORIGINAL JAVA IMPLEMENTATION IS COPYRIGHT 2002 KEN PERLIN 17 | 18 | // I ADDED AN EXTRA METHOD THAT GENERATES A NEW PERMUTATION VECTOR (THIS IS NOT PRESENT IN THE ORIGINAL IMPLEMENTATION) 19 | 20 | // Initialize with the reference values for the permutation vector 21 | PerlinNoise::PerlinNoise() { 22 | 23 | // Initialize the permutation vector with the reference values 24 | p = { 25 | 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142, 26 | 8,99,37,240,21,10,23,190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117, 27 | 35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71, 28 | 134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41, 29 | 55,46,245,40,244,102,143,54, 65,25,63,161,1,216,80,73,209,76,132,187,208, 89, 30 | 18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226, 31 | 250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182, 32 | 189,28,42,223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 33 | 43,172,9,129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246, 34 | 97,228,251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239, 35 | 107,49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 36 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 }; 37 | // Duplicate the permutation vector 38 | p.insert(p.end(), p.begin(), p.end()); 39 | } 40 | 41 | // Generate a new permutation vector based on the value of seed 42 | PerlinNoise::PerlinNoise(unsigned int seed) { 43 | p.resize(256); 44 | 45 | // Fill p with values from 0 to 255 46 | std::iota(p.begin(), p.end(), 0); 47 | 48 | // Initialize a random engine with seed 49 | std::default_random_engine engine(seed); 50 | 51 | // Suffle using the above random engine 52 | std::shuffle(p.begin(), p.end(), engine); 53 | 54 | // Duplicate the permutation vector 55 | p.insert(p.end(), p.begin(), p.end()); 56 | } 57 | 58 | double PerlinNoise::noise(double x, double y, double z) { 59 | // Find the unit cube that contains the point 60 | int X = (int)floor(x) & 255; 61 | int Y = (int)floor(y) & 255; 62 | int Z = (int)floor(z) & 255; 63 | 64 | // Find relative x, y,z of point in cube 65 | x -= floor(x); 66 | y -= floor(y); 67 | z -= floor(z); 68 | 69 | // Compute fade curves for each of x, y, z 70 | double u = fade(x); 71 | double v = fade(y); 72 | double w = fade(z); 73 | 74 | // Hash coordinates of the 8 cube corners 75 | int A = p[X] + Y; 76 | int AA = p[A] + Z; 77 | int AB = p[A + 1] + Z; 78 | int B = p[X + 1] + Y; 79 | int BA = p[B] + Z; 80 | int BB = p[B + 1] + Z; 81 | 82 | // Add blended results from 8 corners of cube 83 | double res = lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)), lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))), lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)), lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)))); 84 | return (res + 1.0) / 2.0; 85 | } 86 | 87 | double PerlinNoise::fade(double t) { 88 | return t * t * t * (t * (t * 6 - 15) + 10); 89 | } 90 | 91 | double PerlinNoise::lerp(double t, double a, double b) { 92 | return a + t * (b - a); 93 | } 94 | 95 | double PerlinNoise::grad(int hash, double x, double y, double z) { 96 | int h = hash & 15; 97 | // Convert lower 4 bits of hash into 12 gradient directions 98 | double u = h < 8 ? x : y, 99 | v = h < 4 ? y : h == 12 || h == 14 ? x : z; 100 | return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); 101 | } 102 | 103 | double PerlinNoise::Octave(FVector Pos, int octaves, float persistence) 104 | { 105 | float total = 0; 106 | float frequency = 1; 107 | float amplitude = 1; 108 | float maxValue = 0; // Used for normalizing result to 0.0 to 1.0 109 | for (int i = 0; i 32 | */ 33 | #include "qef.h" 34 | namespace svd 35 | { 36 | 37 | QefData::QefData() 38 | { 39 | this->clear(); 40 | } 41 | 42 | QefData::QefData(const float ata_00, const float ata_01, 43 | const float ata_02, const float ata_11, const float ata_12, 44 | const float ata_22, const float atb_x, const float atb_y, 45 | const float atb_z, const float btb, const float massPoint_x, 46 | const float massPoint_y, const float massPoint_z, 47 | const int numPoints) 48 | { 49 | this->set(ata_00, ata_01, ata_02, ata_11, ata_12, ata_22, atb_x, atb_y, 50 | atb_z, btb, massPoint_x, massPoint_y, massPoint_z, numPoints); 51 | } 52 | 53 | QefData::QefData(const QefData &rhs) 54 | { 55 | this->set(rhs); 56 | } 57 | 58 | QefData& QefData::operator=(const QefData& rhs) 59 | { 60 | this->set(rhs); 61 | return *this; 62 | } 63 | 64 | void QefData::add(const QefData &rhs) 65 | { 66 | this->ata_00 += rhs.ata_00; 67 | this->ata_01 += rhs.ata_01; 68 | this->ata_02 += rhs.ata_02; 69 | this->ata_11 += rhs.ata_11; 70 | this->ata_12 += rhs.ata_12; 71 | this->ata_22 += rhs.ata_22; 72 | this->atb_x += rhs.atb_x; 73 | this->atb_y += rhs.atb_y; 74 | this->atb_z += rhs.atb_z; 75 | this->btb += rhs.btb; 76 | this->massPoint_x += rhs.massPoint_x; 77 | this->massPoint_y += rhs.massPoint_y; 78 | this->massPoint_z += rhs.massPoint_z; 79 | this->numPoints += rhs.numPoints; 80 | } 81 | 82 | void QefData::clear() 83 | { 84 | this->set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 85 | } 86 | 87 | void QefData::set(const float ata_00, const float ata_01, 88 | const float ata_02, const float ata_11, const float ata_12, 89 | const float ata_22, const float atb_x, const float atb_y, 90 | const float atb_z, const float btb, const float massPoint_x, 91 | const float massPoint_y, const float massPoint_z, 92 | const int numPoints) 93 | { 94 | this->ata_00 = ata_00; 95 | this->ata_01 = ata_01; 96 | this->ata_02 = ata_02; 97 | this->ata_11 = ata_11; 98 | this->ata_12 = ata_12; 99 | this->ata_22 = ata_22; 100 | this->atb_x = atb_x; 101 | this->atb_y = atb_y; 102 | this->atb_z = atb_z; 103 | this->btb = btb; 104 | this->massPoint_x = massPoint_x; 105 | this->massPoint_y = massPoint_y; 106 | this->massPoint_z = massPoint_z; 107 | this->numPoints = numPoints; 108 | } 109 | 110 | void QefData::set(const QefData &rhs) 111 | { 112 | this->set(rhs.ata_00, rhs.ata_01, rhs.ata_02, rhs.ata_11, rhs.ata_12, 113 | rhs.ata_22, rhs.atb_x, rhs.atb_y, rhs.atb_z, rhs.btb, 114 | rhs.massPoint_x, rhs.massPoint_y, rhs.massPoint_z, 115 | rhs.numPoints); 116 | } 117 | 118 | QefSolver::QefSolver() : data(), ata(), atb(), massPoint(), x(), 119 | hasSolution(false) {} 120 | 121 | static void normalize(float &nx, float &ny, float &nz) 122 | { 123 | Vec3 tmpv(nx, ny, nz); 124 | VecUtils::normalize(tmpv); 125 | nx = tmpv.x; 126 | ny = tmpv.y; 127 | nz = tmpv.z; 128 | } 129 | 130 | void QefSolver::add(const float px, const float py, const float pz, 131 | float nx, float ny, float nz) 132 | { 133 | this->hasSolution = false; 134 | normalize(nx, ny, nz); 135 | this->data.ata_00 += nx * nx; 136 | this->data.ata_01 += nx * ny; 137 | this->data.ata_02 += nx * nz; 138 | this->data.ata_11 += ny * ny; 139 | this->data.ata_12 += ny * nz; 140 | this->data.ata_22 += nz * nz; 141 | const float dot = nx * px + ny * py + nz * pz; 142 | this->data.atb_x += dot * nx; 143 | this->data.atb_y += dot * ny; 144 | this->data.atb_z += dot * nz; 145 | this->data.btb += dot * dot; 146 | this->data.massPoint_x += px; 147 | this->data.massPoint_y += py; 148 | this->data.massPoint_z += pz; 149 | ++this->data.numPoints; 150 | } 151 | 152 | void QefSolver::add(const Vec3 &p, const Vec3 &n) 153 | { 154 | this->add(p.x, p.y, p.z, n.x, n.y, n.z); 155 | } 156 | 157 | void QefSolver::add(const QefData &rhs) 158 | { 159 | this->hasSolution = false; 160 | this->data.add(rhs); 161 | } 162 | 163 | QefData QefSolver::getData() 164 | { 165 | return data; 166 | } 167 | 168 | float QefSolver::getError() 169 | { 170 | if (!this->hasSolution) { 171 | //TODO: UE4 Error Logging 172 | } 173 | 174 | return this->getError(this->x); 175 | } 176 | 177 | float QefSolver::getError(const Vec3 &pos) 178 | { 179 | if (!this->hasSolution) { 180 | this->setAta(); 181 | this->setAtb(); 182 | } 183 | 184 | Vec3 atax; 185 | MatUtils::vmul_symmetric(atax, this->ata, pos); 186 | return VecUtils::dot(pos, atax) - 2 * VecUtils::dot(pos, this->atb) 187 | + this->data.btb; 188 | } 189 | 190 | void QefSolver::reset() 191 | { 192 | this->hasSolution = false; 193 | this->data.clear(); 194 | } 195 | 196 | void QefSolver::setAta() 197 | { 198 | this->ata.setSymmetric(this->data.ata_00, this->data.ata_01, 199 | this->data.ata_02, this->data.ata_11, this->data.ata_12, 200 | this->data.ata_22); 201 | } 202 | 203 | void QefSolver::setAtb() 204 | { 205 | this->atb.set(this->data.atb_x, this->data.atb_y, this->data.atb_z); 206 | } 207 | 208 | float QefSolver::solve(Vec3 &outx, const float svd_tol, 209 | const int svd_sweeps, const float pinv_tol) 210 | { 211 | if (this->data.numPoints == 0) { 212 | //TODO: UE4 Error logging 213 | } 214 | 215 | this->massPoint.set(this->data.massPoint_x, this->data.massPoint_y, 216 | this->data.massPoint_z); 217 | VecUtils::scale(this->massPoint, 1.0f / this->data.numPoints); 218 | this->setAta(); 219 | this->setAtb(); 220 | Vec3 tmpv; 221 | MatUtils::vmul_symmetric(tmpv, this->ata, this->massPoint); 222 | VecUtils::sub(this->atb, this->atb, tmpv); 223 | this->x.clear(); 224 | const float result = Svd::solveSymmetric(this->ata, this->atb, 225 | this->x, svd_tol, svd_sweeps, pinv_tol); 226 | VecUtils::addScaled(this->x, 1, this->massPoint); 227 | this->setAtb(); 228 | outx.set(x); 229 | this->hasSolution = true; 230 | return result; 231 | } 232 | } -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/VoxelChunkActor.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "VoxelGeometryPrivatePCH.h" 4 | #include "VoxelGeometry.h" 5 | #include "VoxelChunkActor.h" 6 | #include "ProceduralMeshComponent.h" 7 | #include "VoxelOctree.h" 8 | 9 | 10 | // Sets default values 11 | AVoxelChunkActor::AVoxelChunkActor() 12 | { 13 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 14 | PrimaryActorTick.bCanEverTick = true; 15 | 16 | ProceduralMeshComponent = CreateDefaultSubobject(TEXT("ProceduralMeshComponent")); 17 | 18 | OctreeSize = 4; 19 | } 20 | 21 | // Called when the game starts or when spawned 22 | void AVoxelChunkActor::BeginPlay() 23 | { 24 | Super::BeginPlay(); 25 | } 26 | 27 | // Called every frame 28 | void AVoxelChunkActor::Tick( float DeltaTime ) 29 | { 30 | Super::Tick( DeltaTime ); 31 | } 32 | 33 | static FChunkGeometry TestGeo; 34 | 35 | void AVoxelChunkActor::BuildChunkAsync(UUFNNoiseGenerator* NoiseGenerator, float Threshold) 36 | { 37 | VertexBuffer VBuff; 38 | IndexBuffer IBuff; 39 | 40 | OctreeNode* OctreeRoot = BuildOctree(GetActorLocation(), OctreeSize, Threshold); 41 | if (!OctreeRoot) 42 | { 43 | return; 44 | } 45 | 46 | GenerateMeshFromOctree(OctreeRoot, VBuff, IBuff); 47 | 48 | TArray Verts, Norms; 49 | for (int i = 0; i < VBuff.Num(); ++i) 50 | { 51 | Verts.Add(VBuff[i].xyz); 52 | Norms.Add(VBuff[i].normal); 53 | } 54 | 55 | if (IsValid(ProceduralMeshComponent)) 56 | { 57 | ProceduralMeshComponent->ClearAllMeshSections(); 58 | const TArray UV0; 59 | const TArray VertexColors; 60 | const TArray Tangents; 61 | ProceduralMeshComponent->CreateMeshSection(0, Verts, IBuff, Norms, UV0, VertexColors, Tangents, true); 62 | } 63 | 64 | DestroyOctree(OctreeRoot); 65 | } 66 | 67 | void AVoxelChunkActor::BuildChunkAsyncMarchingCubes(UUFNNoiseGenerator* NoiseGenerator, float Threshold, int32 VoxelsPerSide, int32 ChunkSize) 68 | { 69 | if (!IsValid(NoiseGenerator)) 70 | { 71 | return; 72 | } 73 | 74 | TFunction Task = 75 | [&] 76 | { 77 | float FloatChunkSize = (float)ChunkSize; 78 | float VoxPerSide = (float)VoxelsPerSide; 79 | FVector V(0.01f, 0.01f, 0.01f); 80 | FVector ActorLoc = GetActorLocation(); 81 | TestGeo = UVoxelGeometryFunctionLibrary::GenerateChunkGeometry(NoiseGenerator, V, ActorLoc, FloatChunkSize, VoxPerSide, 0.5f); 82 | }; 83 | 84 | TFunction Callback = 85 | [&, this] 86 | { 87 | TFunction GameThreadCode = 88 | [&, this] 89 | { 90 | if (IsValid(this) && IsValid(this->ProceduralMeshComponent)) 91 | { 92 | this->ChunkGeometry = TestGeo; 93 | 94 | TArray Normals; 95 | const TArray UV0; 96 | const TArray VertexColors; 97 | const TArray Tangents; 98 | 99 | this->ProceduralMeshComponent->CreateMeshSection(0, this->ChunkGeometry.Vertices, this->ChunkGeometry.Indices, Normals, UV0, VertexColors, Tangents, true); 100 | this->bChunkIsBuilding = false; 101 | } 102 | }; 103 | 104 | FFunctionGraphTask::CreateAndDispatchWhenReady(GameThreadCode, TStatId(), nullptr, ENamedThreads::GameThread); 105 | }; 106 | 107 | if (!bChunkIsBuilding) 108 | { 109 | bChunkIsBuilding = true; 110 | Async(EAsyncExecution::Thread, Task, Callback); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/VoxelGeometry.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "VoxelGeometryPrivatePCH.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FVoxelGeometryModule" 6 | 7 | void FVoxelGeometryModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FVoxelGeometryModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FVoxelGeometryModule, VoxelGeometry) -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/VoxelGeometryFunctionLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "VoxelGeometryPrivatePCH.h" 4 | #include "VoxelGeometry.h" 5 | #include "VoxelGeometryFunctionLibrary.h" 6 | #include "ParallelFor.h" 7 | #include "ThreadingBase.h" 8 | #include "PerlinNoise.h" 9 | #include "Engine.h" 10 | 11 | // Fill out your copyright notice in the Description page of Project Settings. 12 | 13 | TArray UVoxelGeometryFunctionLibrary::PolygoniseCell(FMarchingCubesCell& Cell, float& IsoLevel) 14 | { 15 | int edgeTable[256] = { 16 | 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 17 | 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 18 | 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 19 | 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 20 | 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 21 | 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 22 | 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 23 | 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 24 | 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 25 | 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 26 | 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 27 | 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 28 | 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 29 | 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 30 | 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 31 | 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 32 | 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 33 | 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 34 | 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 35 | 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 36 | 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 37 | 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 38 | 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 39 | 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, 40 | 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 41 | 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, 42 | 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 43 | 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, 44 | 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 45 | 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, 46 | 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 47 | 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 }; 48 | 49 | int triTable[256][16] = 50 | { { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 51 | { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 52 | { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 53 | { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 54 | { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 55 | { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 56 | { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 57 | { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 58 | { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 59 | { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 60 | { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 61 | { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 62 | { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 63 | { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 64 | { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 65 | { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 66 | { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 67 | { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 68 | { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 69 | { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 70 | { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 71 | { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, 72 | { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 73 | { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 74 | { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 75 | { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 76 | { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 77 | { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, 78 | { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, 79 | { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, 80 | { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 81 | { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 82 | { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 83 | { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 84 | { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 85 | { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 86 | { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 87 | { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 88 | { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 89 | { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, 90 | { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 91 | { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 92 | { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 93 | { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, 94 | { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, 95 | { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, 96 | { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 97 | { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 98 | { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 99 | { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 100 | { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 101 | { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 102 | { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, 103 | { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, 104 | { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, 105 | { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 106 | { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, 107 | { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 108 | { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, 109 | { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 110 | { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, 111 | { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, 112 | { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, 113 | { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 114 | { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 115 | { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 116 | { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 117 | { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 118 | { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 119 | { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, 120 | { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 121 | { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, 122 | { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 123 | { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 124 | { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 125 | { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, 126 | { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 127 | { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 128 | { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, 129 | { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 130 | { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 131 | { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, 132 | { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 133 | { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 134 | { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, 135 | { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, 136 | { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, 137 | { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, 138 | { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 139 | { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 140 | { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, 141 | { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, 142 | { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 143 | { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, 144 | { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, 145 | { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, 146 | { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 147 | { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, 148 | { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 149 | { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 150 | { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 151 | { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, 152 | { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 153 | { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 154 | { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, 155 | { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, 156 | { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 157 | { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, 158 | { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, 159 | { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, 160 | { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 161 | { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 162 | { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 163 | { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, 164 | { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, 165 | { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 166 | { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 167 | { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, 168 | { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 169 | { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 170 | { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 171 | { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, 172 | { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, 173 | { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, 174 | { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, 175 | { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 176 | { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, 177 | { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 178 | { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 179 | { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 180 | { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 181 | { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 182 | { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 183 | { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 184 | { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 185 | { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, 186 | { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 187 | { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 188 | { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, 189 | { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, 190 | { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 191 | { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, 192 | { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, 193 | { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 194 | { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 195 | { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 196 | { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, 197 | { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, 198 | { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, 199 | { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, 200 | { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, 201 | { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, 202 | { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 203 | { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 204 | { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, 205 | { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 206 | { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, 207 | { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 208 | { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, 209 | { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 210 | { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 211 | { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 212 | { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 213 | { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, 214 | { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 215 | { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, 216 | { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, 217 | { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, 218 | { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, 219 | { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, 220 | { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, 221 | { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, 222 | { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, 223 | { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, 224 | { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, 225 | { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, 226 | { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 227 | { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, 228 | { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, 229 | { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 230 | { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, 231 | { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, 232 | { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, 233 | { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, 234 | { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, 235 | { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 236 | { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, 237 | { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 238 | { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, 239 | { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, 240 | { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 241 | { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 242 | { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 243 | { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, 244 | { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, 245 | { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, 246 | { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 247 | { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, 248 | { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, 249 | { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, 250 | { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 251 | { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, 252 | { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, 253 | { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, 254 | { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 255 | { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 256 | { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 257 | { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 258 | { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 259 | { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, 260 | { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, 261 | { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, 262 | { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, 263 | { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, 264 | { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, 265 | { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 266 | { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, 267 | { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 268 | { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, 269 | { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, 270 | { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 271 | { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 272 | { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, 273 | { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 274 | { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 275 | { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, 276 | { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, 277 | { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, 278 | { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, 279 | { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, 280 | { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 281 | { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, 282 | { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, 283 | { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, 284 | { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, 285 | { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 286 | { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 287 | { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, 288 | { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 289 | { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 290 | { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 291 | { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 292 | { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 293 | { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 294 | { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 295 | { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, 296 | { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 297 | { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 298 | { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 299 | { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 300 | { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, 301 | { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 302 | { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 303 | { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 304 | { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 305 | { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }; 306 | 307 | TArray Triangles; 308 | 309 | TArray VertList; 310 | VertList.Init(FVector::ZeroVector, 12); 311 | 312 | /* 313 | Determine the index into the edge table which 314 | tells us which vertices are inside of the surface 315 | */ 316 | int32 cubeindex = 0; 317 | if (Cell.Values[0] < IsoLevel) cubeindex |= 1; 318 | if (Cell.Values[1] < IsoLevel) cubeindex |= 2; 319 | if (Cell.Values[2] < IsoLevel) cubeindex |= 4; 320 | if (Cell.Values[3] < IsoLevel) cubeindex |= 8; 321 | if (Cell.Values[4] < IsoLevel) cubeindex |= 16; 322 | if (Cell.Values[5] < IsoLevel) cubeindex |= 32; 323 | if (Cell.Values[6] < IsoLevel) cubeindex |= 64; 324 | if (Cell.Values[7] < IsoLevel) cubeindex |= 128; 325 | 326 | /* Trivial Case: Cube is entirely in/out of the surface */ 327 | if (edgeTable[cubeindex] == 0) 328 | { 329 | return Triangles; 330 | } 331 | 332 | /* Find the vertices where the surface intersects the cube */ 333 | if (edgeTable[cubeindex] & 1) 334 | VertList[0] = 335 | VertexInterp(IsoLevel, Cell.Positions[0], Cell.Positions[1], Cell.Values[0], Cell.Values[1]); 336 | if (edgeTable[cubeindex] & 2) 337 | VertList[1] = 338 | VertexInterp(IsoLevel, Cell.Positions[1], Cell.Positions[2], Cell.Values[1], Cell.Values[2]); 339 | if (edgeTable[cubeindex] & 4) 340 | VertList[2] = 341 | VertexInterp(IsoLevel, Cell.Positions[2], Cell.Positions[3], Cell.Values[2], Cell.Values[3]); 342 | if (edgeTable[cubeindex] & 8) 343 | VertList[3] = 344 | VertexInterp(IsoLevel, Cell.Positions[3], Cell.Positions[0], Cell.Values[3], Cell.Values[0]); 345 | if (edgeTable[cubeindex] & 16) 346 | VertList[4] = 347 | VertexInterp(IsoLevel, Cell.Positions[4], Cell.Positions[5], Cell.Values[4], Cell.Values[5]); 348 | if (edgeTable[cubeindex] & 32) 349 | VertList[5] = 350 | VertexInterp(IsoLevel, Cell.Positions[5], Cell.Positions[6], Cell.Values[5], Cell.Values[6]); 351 | if (edgeTable[cubeindex] & 64) 352 | VertList[6] = 353 | VertexInterp(IsoLevel, Cell.Positions[6], Cell.Positions[7], Cell.Values[6], Cell.Values[7]); 354 | if (edgeTable[cubeindex] & 128) 355 | VertList[7] = 356 | VertexInterp(IsoLevel, Cell.Positions[7], Cell.Positions[4], Cell.Values[7], Cell.Values[4]); 357 | if (edgeTable[cubeindex] & 256) 358 | VertList[8] = 359 | VertexInterp(IsoLevel, Cell.Positions[0], Cell.Positions[4], Cell.Values[0], Cell.Values[4]); 360 | if (edgeTable[cubeindex] & 512) 361 | VertList[9] = 362 | VertexInterp(IsoLevel, Cell.Positions[1], Cell.Positions[5], Cell.Values[1], Cell.Values[5]); 363 | if (edgeTable[cubeindex] & 1024) 364 | VertList[10] = 365 | VertexInterp(IsoLevel, Cell.Positions[2], Cell.Positions[6], Cell.Values[2], Cell.Values[6]); 366 | if (edgeTable[cubeindex] & 2048) 367 | VertList[11] = 368 | VertexInterp(IsoLevel, Cell.Positions[3], Cell.Positions[7], Cell.Values[3], Cell.Values[7]); 369 | 370 | /* add Vertices to output */ 371 | for (int i = 0; triTable[cubeindex][i] != -1; i += 3) { 372 | Triangles.Add(VertList[triTable[cubeindex][i]]); 373 | Triangles.Add(VertList[triTable[cubeindex][i + 1]]); 374 | Triangles.Add(VertList[triTable[cubeindex][i + 2]]); 375 | } 376 | 377 | return Triangles; 378 | } 379 | 380 | FVector UVoxelGeometryFunctionLibrary::VertexInterp(float IsoLevel, UPARAM(ref)FVector& Pos1, UPARAM(ref)FVector& Pos2, UPARAM(ref)float& Val1, UPARAM(ref)float& Val2) 381 | { 382 | if (FMath::Abs(IsoLevel - Val1) < 0.00001) 383 | return(Pos1); 384 | if (FMath::Abs(IsoLevel - Val2) < 0.00001) 385 | return(Pos2); 386 | if (FMath::Abs(Val1 - Val2) < 0.00001) 387 | return(Pos1); 388 | 389 | FVector p; 390 | double mu; 391 | mu = (IsoLevel - Val1) / (Val2 - Val1); 392 | p.X = Pos1.X + mu * (Pos2.X - Pos1.X); 393 | p.Y = Pos1.Y + mu * (Pos2.Y - Pos1.Y); 394 | p.Z = Pos1.Z + mu * (Pos2.Z - Pos1.Z); 395 | 396 | return p; 397 | } 398 | 399 | FChunkGeometry UVoxelGeometryFunctionLibrary::GenerateChunkGeometry(UUFNNoiseGenerator* NoiseGenerator, UPARAM(ref)FVector& StartPos, UPARAM(ref)FVector& SamplePos, UPARAM(ref) float & ChunkSize, UPARAM(ref) float & VoxelsPerSide, float IsoLevel) 400 | { 401 | if (!IsValid(NoiseGenerator)) 402 | { 403 | return FChunkGeometry(); 404 | } 405 | 406 | PerlinNoise PN; 407 | float CubeSize = ChunkSize / VoxelsPerSide; 408 | 409 | FChunkGeometry OutGeometry; 410 | FMarchingCubesCell Cell; 411 | 412 | Cell.Positions.Init(FVector(0), 8); 413 | Cell.Values.Init(0.0f, 8); 414 | 415 | FCriticalSection NoiseGenCriticalSection; 416 | FCriticalSection VoxelCriticalSection; 417 | 418 | ParallelFor(VoxelsPerSide, [&](int32 iX) 419 | { 420 | //NOTE: mutable pass by value was causing ue4 compile error 421 | FMarchingCubesCell XCellCopy(Cell); 422 | 423 | XCellCopy.Positions[0].X = StartPos.X + ((iX + 1) * CubeSize); 424 | XCellCopy.Positions[1].X = StartPos.X + ((iX + 1) * CubeSize); 425 | XCellCopy.Positions[2].X = StartPos.X + (iX * CubeSize); 426 | XCellCopy.Positions[3].X = StartPos.X + (iX * CubeSize); 427 | XCellCopy.Positions[4].X = StartPos.X + ((iX + 1) * CubeSize); 428 | XCellCopy.Positions[5].X = StartPos.X + ((iX + 1) * CubeSize); 429 | XCellCopy.Positions[6].X = StartPos.X + (iX * CubeSize); 430 | XCellCopy.Positions[7].X = StartPos.X + (iX * CubeSize); 431 | 432 | ParallelFor(VoxelsPerSide, [&](int32 iY) 433 | { 434 | //NOTE: mutable pass by value was causing ue4 compile error 435 | FMarchingCubesCell YCellCopy(XCellCopy); 436 | 437 | YCellCopy.Positions[0].Y = StartPos.Y + (iY * CubeSize); 438 | YCellCopy.Positions[1].Y = StartPos.Y + ((iY + 1) * CubeSize); 439 | YCellCopy.Positions[2].Y = StartPos.Y + ((iY + 1) * CubeSize); 440 | YCellCopy.Positions[3].Y = StartPos.Y + (iY * CubeSize); 441 | YCellCopy.Positions[4].Y = StartPos.Y + (iY * CubeSize); 442 | YCellCopy.Positions[5].Y = StartPos.Y + ((iY + 1) * CubeSize); 443 | YCellCopy.Positions[6].Y = StartPos.Y + ((iY + 1) * CubeSize); 444 | YCellCopy.Positions[7].Y = StartPos.Y + (iY * CubeSize); 445 | 446 | ParallelFor(VoxelsPerSide, [&](int32 iZ) 447 | { 448 | //NOTE: mutable pass by value was causing ue4 compile error 449 | FMarchingCubesCell ZCellCopy(YCellCopy); 450 | 451 | ZCellCopy.Positions[0].Z = StartPos.Z + (iZ * CubeSize); 452 | ZCellCopy.Positions[1].Z = StartPos.Z + (iZ * CubeSize); 453 | ZCellCopy.Positions[2].Z = StartPos.Z + (iZ * CubeSize); 454 | ZCellCopy.Positions[3].Z = StartPos.Z + (iZ * CubeSize); 455 | ZCellCopy.Positions[4].Z = StartPos.Z + ((iZ + 1) * CubeSize); 456 | ZCellCopy.Positions[5].Z = StartPos.Z + ((iZ + 1) * CubeSize); 457 | ZCellCopy.Positions[6].Z = StartPos.Z + ((iZ + 1) * CubeSize); 458 | ZCellCopy.Positions[7].Z = StartPos.Z + ((iZ + 1) * CubeSize); 459 | 460 | 461 | if (IsValid(NoiseGenerator)) 462 | { 463 | for (int i = 0; i < 8; ++i) 464 | { 465 | ZCellCopy.Values[i] = NoiseGenerator->GetNoise3D(ZCellCopy.Positions[i].X + SamplePos.X, ZCellCopy.Positions[i].Y + SamplePos.Y, ZCellCopy.Positions[i].Z + SamplePos.Z); 466 | } 467 | } 468 | 469 | TArray NewVerts = PolygoniseCell(ZCellCopy, IsoLevel); 470 | //TArray NewNormals; 471 | 472 | FScopeLock Lock(&VoxelCriticalSection); 473 | OutGeometry.Vertices.Append(NewVerts); 474 | //TODO: Normal generation 475 | }); 476 | }); 477 | }); 478 | 479 | //Indices are merely triangle verts in order 480 | int32 TriangleCount = OutGeometry.Vertices.Num(); 481 | for (int i = 0; i < TriangleCount; ++i) 482 | { 483 | OutGeometry.Indices.Add(i); 484 | } 485 | 486 | return OutGeometry; 487 | } -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/VoxelGeometryPrivatePCH.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "VoxelGeometry.h" 4 | 5 | // You should place include statements to your module's private header files here. You only need to 6 | // add includes for headers that are used in most of your module's source files though. -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/VoxelOctree.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Implementations of Octree member functions. 4 | 5 | Copyright (C) 2011 Tao Ju 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public License 9 | (LGPL) as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | */ 21 | 22 | #include "VoxelGeometryPrivatePCH.h" 23 | #include "VoxelGeometry.h" 24 | #include "VoxelOctree.h" 25 | #include "DensityFunctions.h" 26 | 27 | //For AddOnScreenDebugMessage 28 | #include "Engine.h" 29 | 30 | // ---------------------------------------------------------------------------- 31 | 32 | const int MATERIAL_AIR = 0; 33 | const int MATERIAL_SOLID = 1; 34 | 35 | const float QEF_ERROR = 1e-6f; 36 | const int QEF_SWEEPS = 4; 37 | 38 | // ---------------------------------------------------------------------------- 39 | 40 | const FVector CHILD_MIN_OFFSETS[] = 41 | { 42 | // needs to match the vertMap from Dual Contouring impl 43 | FVector(0, 0, 0), 44 | FVector(0, 0, 1), 45 | FVector(0, 1, 0), 46 | FVector(0, 1, 1), 47 | FVector(1, 0, 0), 48 | FVector(1, 0, 1), 49 | FVector(1, 1, 0), 50 | FVector(1, 1, 1), 51 | }; 52 | 53 | // ---------------------------------------------------------------------------- 54 | // data from the original DC impl, drives the contouring process 55 | 56 | const int edgevmap[12][2] = 57 | { 58 | { 0,4 },{ 1,5 },{ 2,6 },{ 3,7 }, // x-axis 59 | { 0,2 },{ 1,3 },{ 4,6 },{ 5,7 }, // y-axis 60 | { 0,1 },{ 2,3 },{ 4,5 },{ 6,7 } // z-axis 61 | }; 62 | 63 | const int edgemask[3] = { 5, 3, 6 }; 64 | 65 | const int vertMap[8][3] = 66 | { 67 | { 0,0,0 }, 68 | { 0,0,1 }, 69 | { 0,1,0 }, 70 | { 0,1,1 }, 71 | { 1,0,0 }, 72 | { 1,0,1 }, 73 | { 1,1,0 }, 74 | { 1,1,1 } 75 | }; 76 | 77 | const int faceMap[6][4] = { { 4, 8, 5, 9 },{ 6, 10, 7, 11 },{ 0, 8, 1, 10 },{ 2, 9, 3, 11 },{ 0, 4, 2, 6 },{ 1, 5, 3, 7 } }; 78 | const int cellProcFaceMask[12][3] = { { 0,4,0 },{ 1,5,0 },{ 2,6,0 },{ 3,7,0 },{ 0,2,1 },{ 4,6,1 },{ 1,3,1 },{ 5,7,1 },{ 0,1,2 },{ 2,3,2 },{ 4,5,2 },{ 6,7,2 } }; 79 | const int cellProcEdgeMask[6][5] = { { 0,1,2,3,0 },{ 4,5,6,7,0 },{ 0,4,1,5,1 },{ 2,6,3,7,1 },{ 0,2,4,6,2 },{ 1,3,5,7,2 } }; 80 | 81 | const int faceProcFaceMask[3][4][3] = { 82 | { { 4,0,0 },{ 5,1,0 },{ 6,2,0 },{ 7,3,0 } }, 83 | { { 2,0,1 },{ 6,4,1 },{ 3,1,1 },{ 7,5,1 } }, 84 | { { 1,0,2 },{ 3,2,2 },{ 5,4,2 },{ 7,6,2 } } 85 | }; 86 | 87 | const int faceProcEdgeMask[3][4][6] = { 88 | { { 1,4,0,5,1,1 },{ 1,6,2,7,3,1 },{ 0,4,6,0,2,2 },{ 0,5,7,1,3,2 } }, 89 | { { 0,2,3,0,1,0 },{ 0,6,7,4,5,0 },{ 1,2,0,6,4,2 },{ 1,3,1,7,5,2 } }, 90 | { { 1,1,0,3,2,0 },{ 1,5,4,7,6,0 },{ 0,1,5,0,4,1 },{ 0,3,7,2,6,1 } } 91 | }; 92 | 93 | const int edgeProcEdgeMask[3][2][5] = { 94 | { { 3,2,1,0,0 },{ 7,6,5,4,0 } }, 95 | { { 5,1,4,0,1 },{ 7,3,6,2,1 } }, 96 | { { 6,4,2,0,2 },{ 7,5,3,1,2 } }, 97 | }; 98 | 99 | const int processEdgeMask[3][4] = { { 3,2,1,0 },{ 7,5,6,4 },{ 11,10,9,8 } }; 100 | 101 | // ------------------------------------------------------------------------------- 102 | 103 | OctreeNode* SimplifyOctree(OctreeNode* node, float threshold) 104 | { 105 | if (!node) 106 | { 107 | return NULL; 108 | } 109 | 110 | if (node->type != Node_Internal) 111 | { 112 | // can't simplify! 113 | return node; 114 | } 115 | 116 | svd::QefSolver qef; 117 | int signs[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; 118 | int midsign = -1; 119 | int edgeCount = 0; 120 | bool isCollapsible = true; 121 | 122 | for (int i = 0; i < 8; i++) 123 | { 124 | node->children[i] = SimplifyOctree(node->children[i], threshold); 125 | if (node->children[i]) 126 | { 127 | OctreeNode* child = node->children[i]; 128 | if (child->type == Node_Internal) 129 | { 130 | isCollapsible = false; 131 | } 132 | else 133 | { 134 | qef.add(child->drawInfo->qef); 135 | 136 | midsign = (child->drawInfo->corners >> (7 - i)) & 1; 137 | signs[i] = (child->drawInfo->corners >> i) & 1; 138 | 139 | edgeCount++; 140 | } 141 | } 142 | } 143 | 144 | if (!isCollapsible) 145 | { 146 | // at least one child is an internal node, can't collapse 147 | return node; 148 | } 149 | 150 | svd::Vec3 qefPosition; 151 | qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); 152 | 153 | FVector OutputVert = FVector(qefPosition.x, qefPosition.y, qefPosition.z); 154 | if (GEngine != nullptr) 155 | { 156 | //GEngine->AddOnScreenDebugMessage(-1, 1000000.0f, FColor::Cyan, OutputVert.ToString()); 157 | } 158 | 159 | float error = qef.getError(); 160 | 161 | // convert to glm vec3 for ease of use 162 | FVector position(qefPosition.x, qefPosition.y, qefPosition.z); 163 | 164 | // at this point the masspoint will actually be a sum, so divide to make it the average 165 | if (error > threshold) 166 | { 167 | // this collapse breaches the threshold 168 | return node; 169 | } 170 | 171 | if (position.X < node->min.X || position.X >(node->min.X + node->size) || 172 | position.Y < node->min.Y || position.Y >(node->min.Y + node->size) || 173 | position.Z < node->min.Z || position.Z >(node->min.Z + node->size)) 174 | { 175 | const auto& mp = qef.getMassPoint(); 176 | position = FVector(mp.x, mp.y, mp.z); 177 | } 178 | 179 | // change the node from an internal node to a 'psuedo leaf' node 180 | OctreeDrawInfo* drawInfo = new OctreeDrawInfo; 181 | 182 | for (int i = 0; i < 8; i++) 183 | { 184 | if (signs[i] == -1) 185 | { 186 | // Undetermined, use centre sign instead 187 | drawInfo->corners |= (midsign << i); 188 | } 189 | else 190 | { 191 | drawInfo->corners |= (signs[i] << i); 192 | } 193 | } 194 | 195 | drawInfo->averageNormal = FVector(0.f); 196 | for (int i = 0; i < 8; i++) 197 | { 198 | if (node->children[i]) 199 | { 200 | OctreeNode* child = node->children[i]; 201 | if (child->type == Node_Psuedo || 202 | child->type == Node_Leaf) 203 | { 204 | drawInfo->averageNormal += child->drawInfo->averageNormal; 205 | } 206 | } 207 | } 208 | 209 | drawInfo->averageNormal.Normalize(1.e-40f); 210 | drawInfo->position = position; 211 | drawInfo->qef = qef.getData(); 212 | 213 | for (int i = 0; i < 8; i++) 214 | { 215 | DestroyOctree(node->children[i]); 216 | node->children[i] = nullptr; 217 | } 218 | 219 | node->type = Node_Psuedo; 220 | node->drawInfo = drawInfo; 221 | 222 | return node; 223 | } 224 | 225 | // ---------------------------------------------------------------------------- 226 | 227 | void GenerateVertexIndices(OctreeNode* node, VertexBuffer& vertexBuffer) 228 | { 229 | if (!node) 230 | { 231 | return; 232 | } 233 | 234 | if (node->type != Node_Leaf) 235 | { 236 | for (int i = 0; i < 8; i++) 237 | { 238 | GenerateVertexIndices(node->children[i], vertexBuffer); 239 | } 240 | } 241 | 242 | if (node->type != Node_Internal) 243 | { 244 | OctreeDrawInfo* d = node->drawInfo; 245 | if (!d) 246 | { 247 | printf("Error! Could not add vertex!\n"); 248 | exit(EXIT_FAILURE); 249 | } 250 | 251 | d->index = vertexBuffer.Num(); 252 | vertexBuffer.Add(MeshVertex(d->position, d->averageNormal)); 253 | } 254 | } 255 | 256 | // ---------------------------------------------------------------------------- 257 | 258 | void ContourProcessEdge(OctreeNode* node[4], int dir, IndexBuffer& indexBuffer) 259 | { 260 | int minSize = 1000000; // arbitrary big number 261 | int minIndex = 0; 262 | int indices[4] = { -1, -1, -1, -1 }; 263 | bool flip = false; 264 | bool signChange[4] = { false, false, false, false }; 265 | 266 | for (int i = 0; i < 4; i++) 267 | { 268 | const int edge = processEdgeMask[dir][i]; 269 | const int c1 = edgevmap[edge][0]; 270 | const int c2 = edgevmap[edge][1]; 271 | 272 | const int m1 = (node[i]->drawInfo->corners >> c1) & 1; 273 | const int m2 = (node[i]->drawInfo->corners >> c2) & 1; 274 | 275 | if (node[i]->size < minSize) 276 | { 277 | minSize = node[i]->size; 278 | minIndex = i; 279 | flip = m1 != MATERIAL_AIR; 280 | } 281 | 282 | indices[i] = node[i]->drawInfo->index; 283 | 284 | signChange[i] = 285 | (m1 == MATERIAL_AIR && m2 != MATERIAL_AIR) || 286 | (m1 != MATERIAL_AIR && m2 == MATERIAL_AIR); 287 | } 288 | 289 | if (signChange[minIndex]) 290 | { 291 | if (!flip) 292 | { 293 | indexBuffer.Add(indices[0]); 294 | indexBuffer.Add(indices[1]); 295 | indexBuffer.Add(indices[3]); 296 | 297 | indexBuffer.Add(indices[0]); 298 | indexBuffer.Add(indices[3]); 299 | indexBuffer.Add(indices[2]); 300 | } 301 | else 302 | { 303 | indexBuffer.Add(indices[0]); 304 | indexBuffer.Add(indices[3]); 305 | indexBuffer.Add(indices[1]); 306 | 307 | indexBuffer.Add(indices[0]); 308 | indexBuffer.Add(indices[2]); 309 | indexBuffer.Add(indices[3]); 310 | } 311 | } 312 | } 313 | 314 | // ---------------------------------------------------------------------------- 315 | 316 | void ContourEdgeProc(OctreeNode* node[4], int dir, IndexBuffer& indexBuffer) 317 | { 318 | if (!node[0] || !node[1] || !node[2] || !node[3]) 319 | { 320 | return; 321 | } 322 | 323 | if (node[0]->type != Node_Internal && 324 | node[1]->type != Node_Internal && 325 | node[2]->type != Node_Internal && 326 | node[3]->type != Node_Internal) 327 | { 328 | ContourProcessEdge(node, dir, indexBuffer); 329 | } 330 | else 331 | { 332 | for (int i = 0; i < 2; i++) 333 | { 334 | OctreeNode* edgeNodes[4]; 335 | const int c[4] = 336 | { 337 | edgeProcEdgeMask[dir][i][0], 338 | edgeProcEdgeMask[dir][i][1], 339 | edgeProcEdgeMask[dir][i][2], 340 | edgeProcEdgeMask[dir][i][3], 341 | }; 342 | 343 | for (int j = 0; j < 4; j++) 344 | { 345 | if (node[j]->type == Node_Leaf || node[j]->type == Node_Psuedo) 346 | { 347 | edgeNodes[j] = node[j]; 348 | } 349 | else 350 | { 351 | edgeNodes[j] = node[j]->children[c[j]]; 352 | } 353 | } 354 | 355 | ContourEdgeProc(edgeNodes, edgeProcEdgeMask[dir][i][4], indexBuffer); 356 | } 357 | } 358 | } 359 | 360 | // ---------------------------------------------------------------------------- 361 | 362 | void ContourFaceProc(OctreeNode* node[2], int dir, IndexBuffer& indexBuffer) 363 | { 364 | if (!node[0] || !node[1]) 365 | { 366 | return; 367 | } 368 | 369 | if (node[0]->type == Node_Internal || 370 | node[1]->type == Node_Internal) 371 | { 372 | for (int i = 0; i < 4; i++) 373 | { 374 | OctreeNode* faceNodes[2]; 375 | const int c[2] = 376 | { 377 | faceProcFaceMask[dir][i][0], 378 | faceProcFaceMask[dir][i][1], 379 | }; 380 | 381 | for (int j = 0; j < 2; j++) 382 | { 383 | if (node[j]->type != Node_Internal) 384 | { 385 | faceNodes[j] = node[j]; 386 | } 387 | else 388 | { 389 | faceNodes[j] = node[j]->children[c[j]]; 390 | } 391 | } 392 | 393 | ContourFaceProc(faceNodes, faceProcFaceMask[dir][i][2], indexBuffer); 394 | } 395 | 396 | const int orders[2][4] = 397 | { 398 | { 0, 0, 1, 1 }, 399 | { 0, 1, 0, 1 }, 400 | }; 401 | for (int i = 0; i < 4; i++) 402 | { 403 | OctreeNode* edgeNodes[4]; 404 | const int c[4] = 405 | { 406 | faceProcEdgeMask[dir][i][1], 407 | faceProcEdgeMask[dir][i][2], 408 | faceProcEdgeMask[dir][i][3], 409 | faceProcEdgeMask[dir][i][4], 410 | }; 411 | 412 | const int* order = orders[faceProcEdgeMask[dir][i][0]]; 413 | for (int j = 0; j < 4; j++) 414 | { 415 | if (node[order[j]]->type == Node_Leaf || 416 | node[order[j]]->type == Node_Psuedo) 417 | { 418 | edgeNodes[j] = node[order[j]]; 419 | } 420 | else 421 | { 422 | edgeNodes[j] = node[order[j]]->children[c[j]]; 423 | } 424 | } 425 | 426 | ContourEdgeProc(edgeNodes, faceProcEdgeMask[dir][i][5], indexBuffer); 427 | } 428 | } 429 | } 430 | 431 | // ---------------------------------------------------------------------------- 432 | 433 | void ContourCellProc(OctreeNode* node, IndexBuffer& indexBuffer) 434 | { 435 | if (node == NULL) 436 | { 437 | return; 438 | } 439 | 440 | if (node->type == Node_Internal) 441 | { 442 | for (int i = 0; i < 8; i++) 443 | { 444 | ContourCellProc(node->children[i], indexBuffer); 445 | } 446 | 447 | for (int i = 0; i < 12; i++) 448 | { 449 | OctreeNode* faceNodes[2]; 450 | const int c[2] = { cellProcFaceMask[i][0], cellProcFaceMask[i][1] }; 451 | 452 | faceNodes[0] = node->children[c[0]]; 453 | faceNodes[1] = node->children[c[1]]; 454 | 455 | ContourFaceProc(faceNodes, cellProcFaceMask[i][2], indexBuffer); 456 | } 457 | 458 | for (int i = 0; i < 6; i++) 459 | { 460 | OctreeNode* edgeNodes[4]; 461 | const int c[4] = 462 | { 463 | cellProcEdgeMask[i][0], 464 | cellProcEdgeMask[i][1], 465 | cellProcEdgeMask[i][2], 466 | cellProcEdgeMask[i][3], 467 | }; 468 | 469 | for (int j = 0; j < 4; j++) 470 | { 471 | edgeNodes[j] = node->children[c[j]]; 472 | } 473 | 474 | ContourEdgeProc(edgeNodes, cellProcEdgeMask[i][4], indexBuffer); 475 | } 476 | } 477 | } 478 | 479 | // ---------------------------------------------------------------------------- 480 | 481 | FVector ApproximateZeroCrossingPosition(const FVector& p0, const FVector& p1) 482 | { 483 | // approximate the zero crossing by finding the min value along the edge 484 | float minValue = 100000.f; 485 | float t = 0.f; 486 | float currentT = 0.f; 487 | const int steps = 8; 488 | const float increment = 1.f / (float)steps; 489 | while (currentT <= 1.f) 490 | { 491 | const FVector p = p0 + ((p1 - p0) * currentT); 492 | const float density = FMath::Abs(Density_Func(p)); 493 | if (density < minValue) 494 | { 495 | minValue = density; 496 | t = currentT; 497 | } 498 | 499 | currentT += increment; 500 | } 501 | 502 | return p0 + ((p1 - p0) * t); 503 | } 504 | 505 | // ---------------------------------------------------------------------------- 506 | 507 | FVector CalculateSurfaceNormal(const FVector& p) 508 | { 509 | const float H = 0.001f; 510 | const float dx = Density_Func(p + FVector(H, 0.f, 0.f)) - Density_Func(p - FVector(H, 0.f, 0.f)); 511 | const float dy = Density_Func(p + FVector(0.f, H, 0.f)) - Density_Func(p - FVector(0.f, H, 0.f)); 512 | const float dz = Density_Func(p + FVector(0.f, 0.f, H)) - Density_Func(p - FVector(0.f, 0.f, H)); 513 | 514 | FVector Normal(dx, dy, dz); 515 | Normal.Normalize(1.e-40f); 516 | return Normal; 517 | } 518 | 519 | // ---------------------------------------------------------------------------- 520 | 521 | OctreeNode* ConstructLeaf(OctreeNode* leaf) 522 | { 523 | if (!leaf || leaf->size != 1) 524 | { 525 | return nullptr; 526 | } 527 | 528 | int corners = 0; 529 | for (int i = 0; i < 8; i++) 530 | { 531 | const FVector cornerPos = leaf->min + CHILD_MIN_OFFSETS[i]; 532 | const float density = Density_Func(FVector(cornerPos)); 533 | const int material = density < 0.f ? MATERIAL_SOLID : MATERIAL_AIR; 534 | corners |= (material << i); 535 | } 536 | 537 | if (corners == 0 || corners == 255) 538 | { 539 | // voxel is full inside or outside the volume 540 | delete leaf; 541 | leaf = nullptr; 542 | return nullptr; 543 | } 544 | 545 | // otherwise the voxel contains the surface, so find the edge intersections 546 | const int MAX_CROSSINGS = 6; 547 | int edgeCount = 0; 548 | FVector averageNormal(0.f); 549 | svd::QefSolver qef; 550 | 551 | for (int i = 0; i < 12 && edgeCount < MAX_CROSSINGS; i++) 552 | { 553 | const int c1 = edgevmap[i][0]; 554 | const int c2 = edgevmap[i][1]; 555 | 556 | const int m1 = (corners >> c1) & 1; 557 | const int m2 = (corners >> c2) & 1; 558 | 559 | if ((m1 == MATERIAL_AIR && m2 == MATERIAL_AIR) || 560 | (m1 == MATERIAL_SOLID && m2 == MATERIAL_SOLID)) 561 | { 562 | // no zero crossing on this edge 563 | continue; 564 | } 565 | 566 | const FVector p1 = FVector(leaf->min + CHILD_MIN_OFFSETS[c1]); 567 | const FVector p2 = FVector(leaf->min + CHILD_MIN_OFFSETS[c2]); 568 | const FVector p = ApproximateZeroCrossingPosition(p1, p2); 569 | const FVector n = CalculateSurfaceNormal(p); 570 | qef.add(p.X, p.Y, p.Z, n.X, n.Y, n.Z); 571 | 572 | averageNormal += n; 573 | 574 | edgeCount++; 575 | } 576 | 577 | svd::Vec3 qefPosition; 578 | qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); 579 | 580 | OctreeDrawInfo* drawInfo = new OctreeDrawInfo; 581 | //Convert svd vec3 to FVector 582 | drawInfo->position = FVector(qefPosition.x, qefPosition.y, qefPosition.z); 583 | drawInfo->qef = qef.getData(); 584 | 585 | const FVector min = FVector(leaf->min); 586 | const FVector max = FVector(leaf->min + FVector(leaf->size)); 587 | if (drawInfo->position.X < min.X || drawInfo->position.X > max.X || 588 | drawInfo->position.Y < min.Y || drawInfo->position.Y > max.Y || 589 | drawInfo->position.X < min.X || drawInfo->position.X > max.X) 590 | { 591 | const auto& mp = qef.getMassPoint(); 592 | drawInfo->position = FVector(mp.x, mp.y, mp.z); 593 | } 594 | 595 | drawInfo->averageNormal = (averageNormal / (float)edgeCount); 596 | drawInfo->averageNormal.Normalize(1.e-40f); 597 | drawInfo->corners = corners; 598 | 599 | leaf->type = Node_Leaf; 600 | leaf->drawInfo = drawInfo; 601 | 602 | return leaf; 603 | } 604 | 605 | // ------------------------------------------------------------------------------- 606 | 607 | OctreeNode* ConstructOctreeNodes(OctreeNode* node) 608 | { 609 | if (!node) 610 | { 611 | return nullptr; 612 | } 613 | 614 | if (node->size == 1) 615 | { 616 | return ConstructLeaf(node); 617 | } 618 | 619 | const int childSize = node->size / 2; 620 | bool hasChildren = false; 621 | 622 | for (int i = 0; i < 8; i++) 623 | { 624 | OctreeNode* child = new OctreeNode; 625 | child->size = childSize; 626 | child->min = node->min + (CHILD_MIN_OFFSETS[i] * childSize); 627 | child->type = Node_Internal; 628 | 629 | node->children[i] = ConstructOctreeNodes(child); 630 | hasChildren |= (node->children[i] != nullptr); 631 | } 632 | 633 | if (!hasChildren) 634 | { 635 | delete node; 636 | node = nullptr; 637 | return nullptr; 638 | } 639 | 640 | return node; 641 | } 642 | 643 | // ------------------------------------------------------------------------------- 644 | 645 | OctreeNode* BuildOctree(const FVector& min, const int32 size, const float threshold) 646 | { 647 | OctreeNode* root = new OctreeNode; 648 | root->min = min; 649 | root->size = size; 650 | root->type = Node_Internal; 651 | 652 | root = ConstructOctreeNodes(root); 653 | root = SimplifyOctree(root, threshold); 654 | 655 | return root; 656 | } 657 | 658 | // ---------------------------------------------------------------------------- 659 | 660 | void GenerateMeshFromOctree(OctreeNode* node, VertexBuffer& vertexBuffer, IndexBuffer& indexBuffer) 661 | { 662 | if (!node) 663 | { 664 | return; 665 | } 666 | 667 | vertexBuffer.Empty(); 668 | indexBuffer.Empty(); 669 | 670 | GenerateVertexIndices(node, vertexBuffer); 671 | ContourCellProc(node, indexBuffer); 672 | } 673 | 674 | // ------------------------------------------------------------------------------- 675 | 676 | void DestroyOctree(OctreeNode* node) 677 | { 678 | if (!node) 679 | { 680 | return; 681 | } 682 | 683 | for (int i = 0; i < 8; i++) 684 | { 685 | DestroyOctree(node->children[i]); 686 | } 687 | 688 | if (node->drawInfo) 689 | { 690 | delete node->drawInfo; 691 | node->drawInfo = nullptr; 692 | } 693 | 694 | delete node; 695 | node = nullptr; 696 | } 697 | 698 | // ------------------------------------------------------------------------------- 699 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Private/VoxelSVD.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | #include "VoxelGeometryPrivatePCH.h" 29 | #include "VoxelGeometry.h" 30 | #include "VoxelSVD.h" 31 | 32 | #include 33 | 34 | namespace svd 35 | { 36 | 37 | Mat3::Mat3() 38 | { 39 | this->clear(); 40 | } 41 | 42 | Mat3::Mat3(const float m00, const float m01, const float m02, 43 | const float m10, const float m11, const float m12, 44 | const float m20, const float m21, const float m22) 45 | { 46 | this->set(m00, m01, m02, m10, m11, m12, m20, m21, m22); 47 | } 48 | 49 | Mat3::Mat3(const Mat3 &rhs) 50 | { 51 | this->set(rhs); 52 | } 53 | 54 | void Mat3::clear() 55 | { 56 | this->set(0, 0, 0, 0, 0, 0, 0, 0, 0); 57 | } 58 | 59 | void Mat3::set(const float m00, const float m01, const float m02, 60 | const float m10, const float m11, const float m12, 61 | const float m20, const float m21, const float m22) 62 | { 63 | this->m00 = m00; 64 | this->m01 = m01; 65 | this->m02 = m02; 66 | this->m10 = m10; 67 | this->m11 = m11; 68 | this->m12 = m12; 69 | this->m20 = m20; 70 | this->m21 = m21; 71 | this->m22 = m22; 72 | } 73 | 74 | void Mat3::set(const Mat3 &rhs) 75 | { 76 | this->set(rhs.m00, rhs.m01, rhs.m02, rhs.m10, rhs.m11, rhs.m12, rhs.m20, 77 | rhs.m21, rhs.m22); 78 | } 79 | 80 | void Mat3::setSymmetric(const SMat3 &rhs) 81 | { 82 | this->setSymmetric(rhs.m00, rhs.m01, rhs.m02, rhs.m11, rhs.m12, rhs.m22); 83 | } 84 | 85 | void Mat3::setSymmetric(const float a00, const float a01, const float a02, 86 | const float a11, const float a12, const float a22) 87 | { 88 | this->set(a00, a01, a02, a01, a11, a12, a02, a12, a22); 89 | } 90 | 91 | SMat3::SMat3() 92 | { 93 | this->clear(); 94 | } 95 | 96 | SMat3::SMat3(const float m00, const float m01, const float m02, 97 | const float m11, const float m12, const float m22) 98 | { 99 | this->setSymmetric(m00, m01, m02, m11, m12, m22); 100 | } 101 | 102 | SMat3::SMat3(const SMat3 &rhs) 103 | { 104 | this->setSymmetric(rhs); 105 | } 106 | 107 | void SMat3::clear() 108 | { 109 | this->setSymmetric(0, 0, 0, 0, 0, 0); 110 | } 111 | 112 | void SMat3::setSymmetric(const SMat3 &rhs) 113 | { 114 | this->setSymmetric(rhs.m00, rhs.m01, rhs.m02, rhs.m11, rhs.m12, rhs.m22); 115 | } 116 | 117 | void SMat3::setSymmetric(const float a00, const float a01, const float a02, 118 | const float a11, const float a12, const float a22) 119 | { 120 | this->m00 = a00; 121 | this->m01 = a01; 122 | this->m02 = a02; 123 | this->m11 = a11; 124 | this->m12 = a12; 125 | this->m22 = a22; 126 | } 127 | 128 | Vec3::Vec3() : x(0), y(0), z(0) { } 129 | 130 | Vec3::Vec3(const Vec3 &rhs)// : Vec3() 131 | { 132 | this->set(rhs); 133 | } 134 | 135 | Vec3::Vec3(const float x, const float y, const float z)// : Vec3() 136 | { 137 | this->set(x, y, z); 138 | } 139 | 140 | void Vec3::clear() 141 | { 142 | this->set(0, 0, 0); 143 | } 144 | 145 | void Vec3::set(const float x, const float y, const float z) 146 | { 147 | this->x = x; 148 | this->y = y; 149 | this->z = z; 150 | } 151 | 152 | void Vec3::set(const Vec3 &rhs) 153 | { 154 | this->set(rhs.x, rhs.y, rhs.z); 155 | } 156 | 157 | float MatUtils::fnorm(const Mat3 &a) 158 | { 159 | return sqrt((a.m00 * a.m00) + (a.m01 * a.m01) + (a.m02 * a.m02) 160 | + (a.m10 * a.m10) + (a.m11 * a.m11) + (a.m12 * a.m12) 161 | + (a.m20 * a.m20) + (a.m21 * a.m21) + (a.m22 * a.m22)); 162 | } 163 | 164 | float MatUtils::fnorm(const SMat3 &a) 165 | { 166 | return sqrt((a.m00 * a.m00) + (a.m01 * a.m01) + (a.m02 * a.m02) 167 | + (a.m01 * a.m01) + (a.m11 * a.m11) + (a.m12 * a.m12) 168 | + (a.m02 * a.m02) + (a.m12 * a.m12) + (a.m22 * a.m22)); 169 | } 170 | 171 | float MatUtils::off(const Mat3 &a) 172 | { 173 | return sqrt((a.m01 * a.m01) + (a.m02 * a.m02) + (a.m10 * a.m10) 174 | + (a.m12 * a.m12) + (a.m20 * a.m20) + (a.m21 * a.m21)); 175 | } 176 | 177 | float MatUtils::off(const SMat3 &a) 178 | { 179 | return sqrt(2 * ((a.m01 * a.m01) + (a.m02 * a.m02) + (a.m12 * a.m12))); 180 | } 181 | 182 | void MatUtils::mmul(Mat3 &out, const Mat3 &a, const Mat3 &b) 183 | { 184 | out.set(a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20, 185 | a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21, 186 | a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22, 187 | a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20, 188 | a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21, 189 | a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22, 190 | a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20, 191 | a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21, 192 | a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22); 193 | } 194 | 195 | void MatUtils::mmul_ata(SMat3 &out, const Mat3 &a) 196 | { 197 | out.setSymmetric(a.m00 * a.m00 + a.m10 * a.m10 + a.m20 * a.m20, 198 | a.m00 * a.m01 + a.m10 * a.m11 + a.m20 * a.m21, 199 | a.m00 * a.m02 + a.m10 * a.m12 + a.m20 * a.m22, 200 | a.m01 * a.m01 + a.m11 * a.m11 + a.m21 * a.m21, 201 | a.m01 * a.m02 + a.m11 * a.m12 + a.m21 * a.m22, 202 | a.m02 * a.m02 + a.m12 * a.m12 + a.m22 * a.m22); 203 | } 204 | 205 | void MatUtils::transpose(Mat3 &out, const Mat3 &a) 206 | { 207 | out.set(a.m00, a.m10, a.m20, a.m01, a.m11, a.m21, a.m02, a.m12, a.m22); 208 | } 209 | 210 | void MatUtils::vmul(Vec3 &out, const Mat3 &a, const Vec3 &v) 211 | { 212 | out.x = (a.m00 * v.x) + (a.m01 * v.y) + (a.m02 * v.z); 213 | out.y = (a.m10 * v.x) + (a.m11 * v.y) + (a.m12 * v.z); 214 | out.z = (a.m20 * v.x) + (a.m21 * v.y) + (a.m22 * v.z); 215 | } 216 | 217 | void MatUtils::vmul_symmetric(Vec3 &out, const SMat3 &a, const Vec3 &v) 218 | { 219 | out.x = (a.m00 * v.x) + (a.m01 * v.y) + (a.m02 * v.z); 220 | out.y = (a.m01 * v.x) + (a.m11 * v.y) + (a.m12 * v.z); 221 | out.z = (a.m02 * v.x) + (a.m12 * v.y) + (a.m22 * v.z); 222 | } 223 | 224 | void VecUtils::addScaled(Vec3 &v, const float s, const Vec3 &rhs) 225 | { 226 | v.x += s * rhs.x; 227 | v.y += s * rhs.y; 228 | v.z += s * rhs.z; 229 | } 230 | 231 | float VecUtils::dot(const Vec3 &a, const Vec3 &b) 232 | { 233 | return a.x * b.x + a.y * b.y + a.z * b.z; 234 | } 235 | 236 | void VecUtils::normalize(Vec3 &v) 237 | { 238 | const float len2 = VecUtils::dot(v, v); 239 | 240 | if (fabs(len2) < 1e-12) { 241 | v.clear(); 242 | } 243 | else { 244 | VecUtils::scale(v, 1 / sqrt(len2)); 245 | } 246 | } 247 | 248 | void VecUtils::scale(Vec3 &v, const float s) 249 | { 250 | v.x *= s; 251 | v.y *= s; 252 | v.z *= s; 253 | } 254 | 255 | void VecUtils::sub(Vec3 &c, const Vec3 &a, const Vec3 &b) 256 | { 257 | const float v0 = a.x - b.x; 258 | const float v1 = a.y - b.y; 259 | const float v2 = a.z - b.z; 260 | c.x = v0; 261 | c.y = v1; 262 | c.z = v2; 263 | } 264 | 265 | void Givens::rot01_post(Mat3 &m, const float c, const float s) 266 | { 267 | const float m00 = m.m00, m01 = m.m01, m10 = m.m10, m11 = m.m11, m20 = m.m20, 268 | m21 = m.m21; 269 | m.set(c * m00 - s * m01, s * m00 + c * m01, m.m02, c * m10 - s * m11, 270 | s * m10 + c * m11, m.m12, c * m20 - s * m21, s * m20 + c * m21, m.m22); 271 | } 272 | 273 | void Givens::rot02_post(Mat3 &m, const float c, const float s) 274 | { 275 | const float m00 = m.m00, m02 = m.m02, m10 = m.m10, m12 = m.m12, 276 | m20 = m.m20, m22 = m.m22; 277 | m.set(c * m00 - s * m02, m.m01, s * m00 + c * m02, c * m10 - s * m12, m.m11, 278 | s * m10 + c * m12, c * m20 - s * m22, m.m21, s * m20 + c * m22); 279 | } 280 | 281 | void Givens::rot12_post(Mat3 &m, const float c, const float s) 282 | { 283 | const float m01 = m.m01, m02 = m.m02, m11 = m.m11, m12 = m.m12, 284 | m21 = m.m21, m22 = m.m22; 285 | m.set(m.m00, c * m01 - s * m02, s * m01 + c * m02, m.m10, c * m11 - s * m12, 286 | s * m11 + c * m12, m.m20, c * m21 - s * m22, s * m21 + c * m22); 287 | } 288 | 289 | static void calcSymmetricGivensCoefficients(const float a_pp, 290 | const float a_pq, const float a_qq, float &c, float &s) 291 | { 292 | if (a_pq == 0) { 293 | c = 1; 294 | s = 0; 295 | return; 296 | } 297 | 298 | const float tau = (a_qq - a_pp) / (2 * a_pq); 299 | const float stt = sqrt(1.0f + tau * tau); 300 | const float tan = 1.0f / ((tau >= 0) ? (tau + stt) : (tau - stt)); 301 | c = 1.0f / sqrt(1.f + tan * tan); 302 | s = tan * c; 303 | } 304 | 305 | void Schur2::rot01(SMat3 &m, float &c, float &s) 306 | { 307 | svd::calcSymmetricGivensCoefficients(m.m00, m.m01, m.m11, c, s); 308 | const float cc = c * c; 309 | const float ss = s * s; 310 | const float mix = 2 * c * s * m.m01; 311 | m.setSymmetric(cc * m.m00 - mix + ss * m.m11, 0, c * m.m02 - s * m.m12, 312 | ss * m.m00 + mix + cc * m.m11, s * m.m02 + c * m.m12, m.m22); 313 | } 314 | 315 | void Schur2::rot02(SMat3 &m, float &c, float &s) 316 | { 317 | svd::calcSymmetricGivensCoefficients(m.m00, m.m02, m.m22, c, s); 318 | const float cc = c * c; 319 | const float ss = s * s; 320 | const float mix = 2 * c * s * m.m02; 321 | m.setSymmetric(cc * m.m00 - mix + ss * m.m22, c * m.m01 - s * m.m12, 0, 322 | m.m11, s * m.m01 + c * m.m12, ss * m.m00 + mix + cc * m.m22); 323 | } 324 | 325 | void Schur2::rot12(SMat3 &m, float &c, float &s) 326 | { 327 | svd::calcSymmetricGivensCoefficients(m.m11, m.m12, m.m22, c, s); 328 | const float cc = c * c; 329 | const float ss = s * s; 330 | const float mix = 2 * c * s * m.m12; 331 | m.setSymmetric(m.m00, c * m.m01 - s * m.m02, s * m.m01 + c * m.m02, 332 | cc * m.m11 - mix + ss * m.m22, 0, ss * m.m11 + mix + cc * m.m22); 333 | } 334 | 335 | static void rotate01(SMat3 &vtav, Mat3 &v) 336 | { 337 | if (vtav.m01 == 0) { 338 | return; 339 | } 340 | 341 | float c, s; 342 | Schur2::rot01(vtav, c, s); 343 | Givens::rot01_post(v, c, s); 344 | } 345 | 346 | static void rotate02(SMat3 &vtav, Mat3 &v) 347 | { 348 | if (vtav.m02 == 0) { 349 | return; 350 | } 351 | 352 | float c, s; 353 | Schur2::rot02(vtav, c, s); 354 | Givens::rot02_post(v, c, s); 355 | } 356 | 357 | static void rotate12(SMat3 &vtav, Mat3 &v) 358 | { 359 | if (vtav.m12 == 0) { 360 | return; 361 | } 362 | 363 | float c, s; 364 | Schur2::rot12(vtav, c, s); 365 | Givens::rot12_post(v, c, s); 366 | } 367 | 368 | void Svd::getSymmetricSvd(const SMat3 &a, SMat3 &vtav, Mat3 &v, 369 | const float tol, 370 | const int max_sweeps) 371 | { 372 | vtav.setSymmetric(a); 373 | v.set(1, 0, 0, 0, 1, 0, 0, 0, 1); 374 | const float delta = tol * MatUtils::fnorm(vtav); 375 | 376 | for (int i = 0; i < max_sweeps 377 | && MatUtils::off(vtav) > delta; ++i) { 378 | rotate01(vtav, v); 379 | rotate02(vtav, v); 380 | rotate12(vtav, v); 381 | } 382 | } 383 | 384 | static float calcError(const Mat3 &A, const Vec3 &x, 385 | const Vec3 &b) 386 | { 387 | Vec3 vtmp; 388 | MatUtils::vmul(vtmp, A, x); 389 | VecUtils::sub(vtmp, b, vtmp); 390 | return VecUtils::dot(vtmp, vtmp); 391 | } 392 | 393 | static float calcError(const SMat3 &origA, const Vec3 &x, 394 | const Vec3 &b) 395 | { 396 | Mat3 A; 397 | Vec3 vtmp; 398 | A.setSymmetric(origA); 399 | MatUtils::vmul(vtmp, A, x); 400 | VecUtils::sub(vtmp, b, vtmp); 401 | return VecUtils::dot(vtmp, vtmp); 402 | } 403 | 404 | static float pinv(const float x, const float tol) 405 | { 406 | return (fabs(x) < tol || fabs(1 / x) < tol) ? 0 : (1 / x); 407 | } 408 | 409 | void Svd::pseudoinverse(Mat3 &out, const SMat3 &d, const Mat3 &v, 410 | const float tol) 411 | { 412 | const float d0 = pinv(d.m00, tol), d1 = pinv(d.m11, tol), d2 = pinv(d.m22, 413 | tol); 414 | out.set(v.m00 * d0 * v.m00 + v.m01 * d1 * v.m01 + v.m02 * d2 * v.m02, 415 | v.m00 * d0 * v.m10 + v.m01 * d1 * v.m11 + v.m02 * d2 * v.m12, 416 | v.m00 * d0 * v.m20 + v.m01 * d1 * v.m21 + v.m02 * d2 * v.m22, 417 | v.m10 * d0 * v.m00 + v.m11 * d1 * v.m01 + v.m12 * d2 * v.m02, 418 | v.m10 * d0 * v.m10 + v.m11 * d1 * v.m11 + v.m12 * d2 * v.m12, 419 | v.m10 * d0 * v.m20 + v.m11 * d1 * v.m21 + v.m12 * d2 * v.m22, 420 | v.m20 * d0 * v.m00 + v.m21 * d1 * v.m01 + v.m22 * d2 * v.m02, 421 | v.m20 * d0 * v.m10 + v.m21 * d1 * v.m11 + v.m22 * d2 * v.m12, 422 | v.m20 * d0 * v.m20 + v.m21 * d1 * v.m21 + v.m22 * d2 * v.m22); 423 | } 424 | 425 | float Svd::solveSymmetric(const SMat3 &A, const Vec3 &b, Vec3 &x, 426 | const float svd_tol, const int svd_sweeps, const float pinv_tol) 427 | { 428 | Mat3 mtmp, pinv, V; 429 | SMat3 VTAV; 430 | Svd::getSymmetricSvd(A, VTAV, V, svd_tol, svd_sweeps); 431 | Svd::pseudoinverse(pinv, VTAV, V, pinv_tol); 432 | MatUtils::vmul(x, pinv, b); 433 | return svd::calcError(A, x, b); 434 | } 435 | 436 | float 437 | LeastSquares::solveLeastSquares(const Mat3 &a, const Vec3 &b, Vec3 &x, 438 | const float svd_tol, const int svd_sweeps, const float pinv_tol) 439 | { 440 | Mat3 at; 441 | SMat3 ata; 442 | Vec3 atb; 443 | MatUtils::transpose(at, a); 444 | MatUtils::mmul_ata(ata, a); 445 | MatUtils::vmul(atb, at, b); 446 | return Svd::solveSymmetric(ata, atb, x, svd_tol, svd_sweeps, pinv_tol); 447 | } 448 | 449 | } 450 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/DensityFunctions.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | float Density_Func(const FVector& worldPosition); 6 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/PerlinNoise.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "AllowWindowsPlatformTypes.h" 6 | #include 7 | #include "HideWindowsPlatformTypes.h" 8 | #include "UnrealMath.h" 9 | 10 | /** 11 | * 12 | */ 13 | class VOXELGEOMETRY_API PerlinNoise { 14 | // The permutation vector 15 | std::vector p; 16 | public: 17 | // Initialize with the reference values for the permutation vector 18 | PerlinNoise(); 19 | // Generate a new permutation vector based on the value of seed 20 | PerlinNoise(unsigned int seed); 21 | // Get a noise value, for 2D images z can have any value 22 | double noise(double x, double y, double z); 23 | //FVector Version 24 | double noise(FVector V) { return noise(V.X, V.Y, V.Z); } 25 | //Octave Perlin Noise 26 | double Octave(FVector Pos, int octaves, float persistence); 27 | private: 28 | double fade(double t); 29 | double lerp(double t, double a, double b); 30 | double grad(int hash, double x, double y, double z); 31 | }; -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/QEF.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | #pragma once 29 | 30 | #include "VoxelSVD.h" 31 | namespace svd 32 | { 33 | class QefData 34 | { 35 | public: 36 | float ata_00, ata_01, ata_02, ata_11, ata_12, ata_22; 37 | float atb_x, atb_y, atb_z; 38 | float btb; 39 | float massPoint_x, massPoint_y, massPoint_z; 40 | int numPoints; 41 | 42 | QefData(); 43 | 44 | QefData(const float ata_00, const float ata_01, 45 | const float ata_02, const float ata_11, const float ata_12, 46 | const float ata_22, const float atb_x, const float atb_y, 47 | const float atb_z, const float btb, const float massPoint_x, 48 | const float massPoint_y, const float massPoint_z, 49 | const int numPoints); 50 | 51 | void add(const QefData &rhs); 52 | 53 | void clear(); 54 | 55 | void set(const float ata_00, const float ata_01, 56 | const float ata_02, const float ata_11, const float ata_12, 57 | const float ata_22, const float atb_x, const float atb_y, 58 | const float atb_z, const float btb, const float massPoint_x, 59 | const float massPoint_y, const float massPoint_z, 60 | const int numPoints); 61 | 62 | void set(const QefData &rhs); 63 | 64 | QefData(const QefData &rhs); 65 | QefData &operator= (const QefData &rhs); 66 | }; 67 | 68 | class QefSolver 69 | { 70 | private: 71 | QefData data; 72 | SMat3 ata; 73 | Vec3 atb, massPoint, x; 74 | bool hasSolution; 75 | public: 76 | QefSolver(); 77 | public: 78 | 79 | const Vec3& getMassPoint() const { return massPoint; } 80 | 81 | void add(const float px, const float py, const float pz, 82 | float nx, float ny, float nz); 83 | void add(const Vec3 &p, const Vec3 &n); 84 | void add(const QefData &rhs); 85 | QefData getData(); 86 | float getError(); 87 | float getError(const Vec3 &pos); 88 | void reset(); 89 | float solve(Vec3 &outx, const float svd_tol, 90 | const int svd_sweeps, const float pinv_tol); 91 | private: 92 | QefSolver(const QefSolver &rhs); 93 | QefSolver &operator=(const QefSolver &rhs); 94 | void setAta(); 95 | void setAtb(); 96 | }; 97 | }; -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/VoxelChunkActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "Engine.h" 6 | 7 | #include "VoxelGeometryFunctionLibrary.h" 8 | #include "Async.h" 9 | #include "GameFramework/Actor.h" 10 | 11 | #include "UFNNoiseGenerator.h" 12 | 13 | #include "VoxelChunkActor.generated.h" 14 | 15 | 16 | UCLASS() 17 | class VOXELGEOMETRY_API AVoxelChunkActor : public AActor 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | // Sets default values for this actor's properties 23 | AVoxelChunkActor(); 24 | 25 | // Called when the game starts or when spawned 26 | virtual void BeginPlay() override; 27 | 28 | // Called every frame 29 | virtual void Tick( float DeltaSeconds ) override; 30 | 31 | //Generates Chunk Geometry (Dual Contouring) 32 | UFUNCTION(BlueprintCallable, Category = ChunkActor) 33 | void BuildChunkAsync(UUFNNoiseGenerator* NoiseGenerator, float Threshold = 1.f); 34 | 35 | //Generates Chunk Geometry (Marching Cubes) 36 | UFUNCTION(BlueprintCallable, Category = ChunkActor) 37 | void BuildChunkAsyncMarchingCubes(UUFNNoiseGenerator* NoiseGenerator, float Threshold = 1.f, int32 VoxelsPerSide = 30, int32 ChunkSize = 1000); 38 | 39 | protected: 40 | 41 | /** Is chunk currently building*/ 42 | bool bChunkIsBuilding; 43 | 44 | /* Chunk Geometry */ 45 | FChunkGeometry ChunkGeometry; 46 | 47 | /*Stores and render voxel geometry */ 48 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ChunkActor, Transient) 49 | class UProceduralMeshComponent* ProceduralMeshComponent; 50 | 51 | /* Size of octree */ 52 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ChunkActor) 53 | int32 OctreeSize; 54 | }; 55 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/VoxelGeometry.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "ModuleManager.h" 6 | 7 | class FVoxelGeometryModule : public IModuleInterface 8 | { 9 | public: 10 | 11 | /** IModuleInterface implementation */ 12 | virtual void StartupModule() override; 13 | virtual void ShutdownModule() override; 14 | }; -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/VoxelGeometryFunctionLibrary.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "Kismet/BlueprintFunctionLibrary.h" 6 | #include "UFNNoiseGenerator.h" 7 | 8 | #include "VoxelGeometryFunctionLibrary.generated.h" 9 | 10 | USTRUCT(BlueprintType) 11 | struct FMarchingCubesCell 12 | { 13 | GENERATED_BODY() 14 | 15 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Grid) 16 | TArray Positions; 17 | 18 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Grid) 19 | TArray Values; 20 | 21 | FMarchingCubesCell() 22 | { 23 | Positions.Reserve(8); 24 | Values.Reserve(8); 25 | } 26 | }; 27 | 28 | USTRUCT(BlueprintType) 29 | struct FChunkGeometry 30 | { 31 | GENERATED_BODY() 32 | 33 | UPROPERTY(BlueprintReadOnly, Category = Chunk) 34 | TArray Vertices; 35 | 36 | UPROPERTY(BlueprintReadOnly, Category = Chunk) 37 | TArray Normals; 38 | 39 | UPROPERTY(BlueprintReadOnly, Category = Chunk) 40 | TArray Indices; 41 | }; 42 | 43 | UCLASS(BlueprintType) 44 | class VOXELGEOMETRY_API UNoiseGeneratorInterface : public UObject 45 | { 46 | GENERATED_BODY() 47 | 48 | public: 49 | 50 | virtual float GetNoise3D(FVector Position) { return 0.0f; } 51 | }; 52 | 53 | /** 54 | * 55 | */ 56 | UCLASS() 57 | class VOXELGEOMETRY_API UVoxelGeometryFunctionLibrary : public UBlueprintFunctionLibrary 58 | { 59 | GENERATED_BODY() 60 | 61 | 62 | public: 63 | 64 | UFUNCTION(BlueprintCallable, Category = "Voxel") 65 | static TArray PolygoniseCell(UPARAM(ref) FMarchingCubesCell& Cell, UPARAM(ref)float& IsoLevel); 66 | 67 | UFUNCTION(BlueprintCallable, Category = "Voxel") 68 | static FVector VertexInterp(float IsoLevel, UPARAM(ref)FVector& Pos1, UPARAM(ref)FVector& Pos2, UPARAM(ref)float& Val1, UPARAM(ref)float& Val2); 69 | 70 | //Generates chunk at StartPos of (Length,Width, and Height) == ChunkSize based on n VoxelsPerSide and outputs OutGeometry 71 | UFUNCTION(BlueprintCallable, Category = "Voxel") 72 | static FChunkGeometry GenerateChunkGeometry(UUFNNoiseGenerator* NoiseGenerator, UPARAM(ref) FVector& StartPos, UPARAM(ref)FVector& SamplePos, UPARAM(ref) float& ChunkSize, UPARAM(ref) float& VoxelsPerSide, float IsoLevel); 73 | 74 | }; 75 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/VoxelOctree.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Implementations of Octree member functions. 4 | 5 | Copyright (C) 2011 Tao Ju 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public License 9 | (LGPL) as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | */ 21 | #pragma once 22 | 23 | #include "UnrealMath.h" 24 | #include "QEF.h" 25 | 26 | struct MeshVertex 27 | { 28 | MeshVertex(const FVector& _xyz, const FVector& _normal) 29 | : xyz(_xyz) 30 | , normal(_normal) 31 | { 32 | } 33 | 34 | FVector xyz; 35 | FVector normal; 36 | }; 37 | 38 | typedef TArray VertexBuffer; 39 | typedef TArray IndexBuffer; 40 | 41 | // ---------------------------------------------------------------------------- 42 | 43 | enum OctreeNodeType 44 | { 45 | Node_None, 46 | Node_Internal, 47 | Node_Psuedo, 48 | Node_Leaf, 49 | }; 50 | 51 | // ---------------------------------------------------------------------------- 52 | 53 | struct OctreeDrawInfo 54 | { 55 | OctreeDrawInfo() 56 | : index(-1) 57 | , corners(0) 58 | { 59 | } 60 | 61 | int index; 62 | int corners; 63 | FVector position; 64 | FVector averageNormal; 65 | svd::QefData qef; 66 | }; 67 | 68 | // ---------------------------------------------------------------------------- 69 | 70 | class OctreeNode 71 | { 72 | public: 73 | 74 | OctreeNode() 75 | : type(Node_None) 76 | , min(0, 0, 0) 77 | , size(0) 78 | , drawInfo(nullptr) 79 | { 80 | for (int i = 0; i < 8; i++) 81 | { 82 | children[i] = nullptr; 83 | } 84 | } 85 | 86 | OctreeNode(const OctreeNodeType _type) 87 | : type(_type) 88 | , min(0, 0, 0) 89 | , size(0) 90 | , drawInfo(nullptr) 91 | { 92 | for (int i = 0; i < 8; i++) 93 | { 94 | children[i] = nullptr; 95 | } 96 | } 97 | 98 | OctreeNodeType type; 99 | FVector min; 100 | int32 size; 101 | OctreeNode* children[8]; 102 | OctreeDrawInfo* drawInfo; 103 | }; 104 | 105 | // ---------------------------------------------------------------------------- 106 | 107 | OctreeNode* BuildOctree(const FVector& Min, const int32 size, const float threshold); 108 | void DestroyOctree(OctreeNode* node); 109 | void GenerateMeshFromOctree(OctreeNode* node, VertexBuffer& vertexBuffer, IndexBuffer& indexBuffer); 110 | 111 | // ---------------------------------------------------------------------------- 112 | 113 | -------------------------------------------------------------------------------- /Source/VoxelGeometry/Public/VoxelSVD.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is free and unencumbered software released into the public domain. 3 | * 4 | * Anyone is free to copy, modify, publish, use, compile, sell, or 5 | * distribute this software, either in source code form or as a compiled 6 | * binary, for any purpose, commercial or non-commercial, and by any 7 | * means. 8 | * 9 | * In jurisdictions that recognize copyright laws, the author or authors 10 | * of this software dedicate any and all copyright interest in the 11 | * software to the public domain. We make this dedication for the benefit 12 | * of the public at large and to the detriment of our heirs and 13 | * successors. We intend this dedication to be an overt act of 14 | * relinquishment in perpetuity of all present and future rights to this 15 | * software under copyright law. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * For more information, please refer to 26 | */ 27 | 28 | #pragma once 29 | 30 | namespace svd 31 | { 32 | class SMat3 33 | { 34 | public: 35 | float m00, m01, m02, m11, m12, m22; 36 | public: 37 | SMat3(); 38 | SMat3(const float m00, const float m01, const float m02, 39 | const float m11, const float m12, const float m22); 40 | void clear(); 41 | void setSymmetric(const float m00, const float m01, const float m02, 42 | const float m11, 43 | const float m12, const float m22); 44 | void setSymmetric(const SMat3 &rhs); 45 | private: 46 | SMat3(const SMat3 &rhs); 47 | SMat3 &operator=(const SMat3 &rhs); 48 | }; 49 | class Mat3 50 | { 51 | public: 52 | float m00, m01, m02, m10, m11, m12, m20, m21, m22; 53 | public: 54 | Mat3(); 55 | Mat3(const float m00, const float m01, const float m02, 56 | const float m10, const float m11, const float m12, 57 | const float m20, const float m21, const float m22); 58 | void clear(); 59 | void set(const float m00, const float m01, const float m02, 60 | const float m10, const float m11, const float m12, 61 | const float m20, const float m21, const float m22); 62 | void set(const Mat3 &rhs); 63 | void setSymmetric(const float a00, const float a01, const float a02, 64 | const float a11, const float a12, const float a22); 65 | void setSymmetric(const SMat3 &rhs); 66 | private: 67 | Mat3(const Mat3 &rhs); 68 | Mat3 &operator=(const Mat3 &rhs); 69 | }; 70 | class Vec3 71 | { 72 | public: 73 | float x, y, z; 74 | public: 75 | Vec3(); 76 | Vec3(const float x, const float y, const float z); 77 | void clear(); 78 | void set(const float x, const float y, const float z); 79 | void set(const Vec3 &rhs); 80 | private: 81 | Vec3(const Vec3 &rhs); 82 | Vec3 &operator=(const Vec3 &rhs); 83 | }; 84 | 85 | class MatUtils 86 | { 87 | public: 88 | static float fnorm(const Mat3 &a); 89 | static float fnorm(const SMat3 &a); 90 | static float off(const Mat3 &a); 91 | static float off(const SMat3 &a); 92 | 93 | public: 94 | static void mmul(Mat3 &out, const Mat3 &a, const Mat3 &b); 95 | static void mmul_ata(SMat3 &out, const Mat3 &a); 96 | static void transpose(Mat3 &out, const Mat3 &a); 97 | static void vmul(Vec3 &out, const Mat3 &a, const Vec3 &v); 98 | static void vmul_symmetric(Vec3 &out, const SMat3 &a, const Vec3 &v); 99 | }; 100 | class VecUtils 101 | { 102 | public: 103 | static void addScaled(Vec3 &v, const float s, const Vec3 &rhs); 104 | static float dot(const Vec3 &a, const Vec3 &b); 105 | static void normalize(Vec3 &v); 106 | static void scale(Vec3 &v, const float s); 107 | static void sub(Vec3 &c, const Vec3 &a, const Vec3 &b); 108 | }; 109 | class Givens 110 | { 111 | public: 112 | static void rot01_post(Mat3 &m, const float c, const float s); 113 | static void rot02_post(Mat3 &m, const float c, const float s); 114 | static void rot12_post(Mat3 &m, const float c, const float s); 115 | }; 116 | class Schur2 117 | { 118 | public: 119 | static void rot01(SMat3 &out, float &c, float &s); 120 | static void rot02(SMat3 &out, float &c, float &s); 121 | static void rot12(SMat3 &out, float &c, float &s); 122 | }; 123 | class Svd 124 | { 125 | public: 126 | static void getSymmetricSvd(const SMat3 &a, SMat3 &vtav, Mat3 &v, 127 | const float tol, const int max_sweeps); 128 | static void pseudoinverse(Mat3 &out, const SMat3 &d, const Mat3 &v, 129 | const float tol); 130 | static float solveSymmetric(const SMat3 &A, const Vec3 &b, Vec3 &x, 131 | const float svd_tol, const int svd_sweeps, const float pinv_tol); 132 | }; 133 | class LeastSquares 134 | { 135 | public: 136 | static float solveLeastSquares(const Mat3 &a, const Vec3 &b, Vec3 &x, 137 | const float svd_tol, const int svd_sweeps, const float pinv_tol); 138 | }; 139 | }; -------------------------------------------------------------------------------- /Source/VoxelGeometry/VoxelGeometry.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class VoxelGeometry : ModuleRules 6 | { 7 | public VoxelGeometry(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | 10 | PublicIncludePaths.AddRange( 11 | new string[] { 12 | "VoxelGeometry/Public", 13 | "ProceduralMeshComponent", 14 | "UnrealFastNoisePlugin" 15 | // ... add public include paths required here ... 16 | } 17 | ); 18 | 19 | 20 | PrivateIncludePaths.AddRange( 21 | new string[] { 22 | "VoxelGeometry/Private", 23 | // ... add other private include paths required here ... 24 | } 25 | ); 26 | 27 | 28 | PublicDependencyModuleNames.AddRange( 29 | new string[] 30 | { 31 | "Core", 32 | "ProceduralMeshComponent", 33 | "UnrealFastNoisePlugin" 34 | // ... add other public dependencies that you statically link with here ... 35 | } 36 | ); 37 | 38 | 39 | PrivateDependencyModuleNames.AddRange( 40 | new string[] 41 | { 42 | "CoreUObject", 43 | "Engine", 44 | "Slate", 45 | "SlateCore", 46 | // ... add private dependencies that you statically link with here ... 47 | } 48 | ); 49 | 50 | 51 | DynamicallyLoadedModuleNames.AddRange( 52 | new string[] 53 | { 54 | // ... add any modules that your module loads dynamically here ... 55 | } 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /VoxelGeometry.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "VoxelGeometry", 6 | "Description": "", 7 | "Category": "Other", 8 | "CreatedBy": "", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": false, 14 | "IsBetaVersion": false, 15 | "Installed": false, 16 | "Modules": [ 17 | { 18 | "Name": "VoxelGeometry", 19 | "Type": "Developer", 20 | "LoadingPhase": "Default" 21 | } 22 | ], 23 | "Plugins": [ 24 | { 25 | "Name": "UnrealFastNoisePlugin", 26 | "Enabled": true 27 | }, 28 | { 29 | "Name": "ProceduralMeshComponent", 30 | "Enabled": true 31 | } 32 | ] 33 | } --------------------------------------------------------------------------------