Guest User

Maxwell's terrain generation implementation

a guest
Oct 5th, 2016
304
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.89 KB | None | 0 0
  1.  
  2. // Fill out your copyright notice in the Description page of Project Settings.
  3.  
  4. #include "tw_GARVINIZED.h"
  5. #include "FNoiseWorker.h"
  6.  
  7. // PolyVox
  8. #include "PolyVox/CubicSurfaceExtractor.h"
  9. #include "PolyVox/Mesh.h"
  10. using namespace PolyVox;
  11.  
  12. // ANL
  13. #include "VM/kernel.h"
  14.  
  15. //using namespace anl;
  16. typedef anl::CInstructionIndex Cii;
  17.  
  18. class FNoiseWorker::PrivateData {
  19. public:
  20.     anl::CKernel NoiseKernel;
  21.     Cii* PerturbGradient;
  22.     Cii* GrassZ;
  23.     Cii* OreFractal;
  24.     anl::CNoiseExecutor* TerrainExecutor;
  25.  
  26.     PrivateData(const VTPagerDetails& vtPagerDetails) :
  27.         TerrainExecutor(nullptr)
  28.     {
  29.  
  30.         // Commonly used constants
  31.         auto Zero = NoiseKernel.constant(0);
  32.  
  33.  
  34.         Cii One = NoiseKernel.constant(1);
  35.         Cii zScale = NoiseKernel.constant(1.1);
  36.         Cii twoHundred55 = NoiseKernel.constant(255);
  37.         Cii VerticalHeight = NoiseKernel.constant(vtPagerDetails.Height);
  38.         Cii HalfVerticalHeight = NoiseKernel.constant(vtPagerDetails.Height / 2.f);
  39.         Cii falloff = NoiseKernel.constant(2.0 / vtPagerDetails.Height);
  40.  
  41.  
  42.         // Create a gradient on the vertical axis to form our ground plane.
  43.         Cii VerticalGradient = NoiseKernel.divide(NoiseKernel.clamp(NoiseKernel.subtract(VerticalHeight, NoiseKernel.z()), Zero, VerticalHeight), VerticalHeight);
  44.  
  45.         // Turn our gradient into two solids that represent the ground and air. This prevents floating terrain from forming later.
  46.         // falloff determines the width of the boundary
  47.         Cii VerticalSelect = NoiseKernel.select(Zero, twoHundred55, VerticalGradient, NoiseKernel.constant(0.5), falloff);
  48.  
  49.         // This is the actual noise generator we'll be using.
  50.         // In this case I've gone with a simple fBm generator, which will create terrain that looks like smooth, rolling hills.
  51.         Cii TerrainFractal = NoiseKernel.simplefBm(anl::BasisTypes::BASIS_GRADIENT, anl::InterpolationTypes::INTERP_QUINTIC, vtPagerDetails.Octaves, vtPagerDetails.Frequency, vtPagerDetails.Seed);
  52.  
  53.         // Scale and offset the generated noise value.
  54.         // Scaling the noise makes the features bigger or smaller, and offsetting it will move the terrain up and down.
  55.         Cii TerrainScale = NoiseKernel.scaleOffset(TerrainFractal, vtPagerDetails.Scale, vtPagerDetails.Offset);
  56.  
  57.         // Setting the Z scale of the fractal to 0 will effectively turn the fractal into a heightmap. Larger values allow for the terrain to overlap.
  58.         Cii TerrainZScale = NoiseKernel.scaleZ(TerrainScale, zScale);
  59.  
  60.         // Finally, apply the Z offset we just calculated from the fractal to our ground plane.
  61.         Cii PerturbGradient = NoiseKernel.translateZ(VerticalSelect, TerrainZScale);
  62.         this->PerturbGradient = new anl::CInstructionIndex(PerturbGradient);
  63.  
  64.         // Now we want to determine different materials based on a variety of factors.
  65.         // This is made easier by the fact that we're basically generating a heightmap.
  66.  
  67.         // For now our grass is always going to appear at the top level, so we don't need to do anything fancy.
  68.         Cii GrassZ = NoiseKernel.subtract(HalfVerticalHeight, TerrainZScale);
  69.         this->GrassZ = new anl::CInstructionIndex(GrassZ);
  70.  
  71.         // To generate pockets of ore we're going to need another noise generator.
  72.         Cii OreFractal = NoiseKernel.simpleRidgedMultifractal(anl::BasisTypes::BASIS_SIMPLEX, anl::InterpolationTypes::INTERP_LINEAR, 2, 5 * vtPagerDetails.Frequency, vtPagerDetails.Seed);
  73.         this->OreFractal = new anl::CInstructionIndex(OreFractal);
  74.  
  75.         TerrainExecutor = new anl::CNoiseExecutor(this->NoiseKernel);
  76.     }
  77.     ~PrivateData() {
  78.         delete PerturbGradient;
  79.         delete GrassZ;
  80.         delete OreFractal;
  81.         delete TerrainExecutor;
  82.     }
  83.  
  84. };
  85.  
  86. VoxelData_sp createSphereInVolume(const PolyVox::Region& region)
  87. {
  88.     const int32_t xSize = region.getUpperX() - region.getLowerX() + 1;
  89.     const int32_t ySize = region.getUpperY() - region.getLowerY() + 1;
  90.     const int32_t zSize = region.getUpperZ() - region.getLowerZ() + 1;
  91.  
  92.     float fRadius = xSize / 2.2;
  93.  
  94.     VoxelData_sp voxelData = MakeShareable(new VoxelData(xSize, ySize, zSize));
  95.  
  96.     //This vector hold the position of the center of the volume
  97.     Vector3DFloat v3dVolCenter(xSize / 2, ySize / 2, zSize / 2);
  98.  
  99.     //This three-level for loop iterates over every voxel in the volume
  100.     //const float innerBoundary = 0.1f * fRadius;
  101.     for (int z = 0; z < zSize; z++)
  102.     {
  103.         for (int y = 0; y < ySize; y++)
  104.         {
  105.             for (int x = 0; x < xSize; x++)
  106.             {
  107.                 //Store our current position as a vector...
  108.                 Vector3DFloat v3dCurrentPos(x, y, z);
  109.                 //And compute how far the current position is from the center of the volume
  110.                 float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length();
  111.  
  112.                 uint8_t uVoxelValue = 0;
  113.  
  114.                 float val = fRadius - fDistToCenter; // val is positive when inside sphere
  115.                 val = PolyVox::clamp(val, -1.0f, 1.0f); // val is between -1.0 and 1.0
  116.                 val += 1.0f; // val is between 0.0 and 2.0
  117.                 val *= 127.5f; // val is between 0.0 and 255
  118.  
  119.                                // Cast to int
  120.                 uVoxelValue = static_cast<uint8_t>(val);
  121.  
  122.                 MDP88 Voxel;
  123.                 Voxel.setDensity(uVoxelValue);
  124.                 Voxel.setMaterial(3);
  125.                 voxelData->at(x, y, z) = Voxel;
  126.             }
  127.         }
  128.     }
  129.     return voxelData;
  130. }
  131.  
  132. VoxelData_sp buildRegionDetails(FNoiseWorker::PrivateData* m_data, const PolyVox::Region& region) {
  133.     assert(m_data->TerrainExecutor != nullptr);
  134.     const int32_t xSize = region.getUpperX() - region.getLowerX() + 1;
  135.     const int32_t ySize = region.getUpperY() - region.getLowerY() + 1;
  136.     const int32_t zSize = region.getUpperZ() - region.getLowerZ() + 1;
  137.  
  138.     VoxelData_sp voxelData = MakeShareable(new VoxelData(xSize, ySize, zSize));
  139.  
  140.     // Now that we have our noise setup, let's loop over our chunk and apply it.
  141.     const auto lowerX = region.getLowerX();
  142.     const auto upperX = region.getUpperX();
  143.  
  144.     const auto lowerY = region.getLowerY();
  145.     const auto upperY = region.getUpperY();
  146.  
  147.     const auto lowerZ = region.getLowerZ();
  148.     const auto upperZ = region.getUpperZ();
  149.  
  150.     for (int x = lowerX; x <= upperX; x++)
  151.     {
  152.         for (int y = lowerY; y <= upperY; y++)
  153.         {
  154.             for (int z = lowerZ; z <= upperZ; z++)
  155.             {
  156.                 const int32_t x_index = x - lowerX;
  157.                 const int32_t y_index = y - lowerY;
  158.                 const int32_t z_index = z - lowerZ;
  159.  
  160.                 // Evaluate the noise
  161.                 auto EvaluatedNoise = m_data->TerrainExecutor->evaluateScalar(x, y, z, *m_data->PerturbGradient);
  162.                 MDP88 Voxel;
  163.  
  164.                 bool bSolid = EvaluatedNoise >= 127;
  165.                 Voxel.setDensity(EvaluatedNoise);
  166.  
  167.                 // Determine what material should be set on the voxel
  168.                 // Air = 0
  169.                 // Stone = 1
  170.                 // Dirt = 2
  171.                 // Grass = 3
  172.                 // Ore = 4
  173.  
  174.                 int ActualGrassZ = FMath::FloorToInt(m_data->TerrainExecutor->evaluateScalar(x, y, z, *m_data->GrassZ)) - 1;
  175.                 int DirtZ = ActualGrassZ - 1;
  176.                 int DirtThickness = 3;
  177.  
  178.                 if (bSolid) {
  179.                     if (z >= ActualGrassZ)
  180.                     {
  181.                         Voxel.setMaterial(3);
  182.                     }
  183.                     else if (z <= DirtZ && z > (DirtZ - DirtThickness))
  184.                     {
  185.                         Voxel.setMaterial(2);
  186.                     }
  187.                     else
  188.                     {
  189.                         auto EvaluatedOreFractal = m_data->TerrainExecutor->evaluateScalar(x, y, z, *m_data->OreFractal);
  190.  
  191.                         if (EvaluatedOreFractal > 1.95)
  192.                             Voxel.setMaterial(4);
  193.                         else
  194.                             Voxel.setMaterial(1);
  195.                     }
  196.                 }
  197.                 else
  198.                 {
  199.                     Voxel.setMaterial(0);
  200.                 }
  201.                 voxelData->at(x_index, y_index, z_index) = Voxel;
  202.             }
  203.         }
  204.     }
  205.     return voxelData;
  206.  
  207. }
  208.  
  209.  
  210. FNoiseWorker::FNoiseWorker()
  211. {
  212.     this->inputQueue = MakeShareable(new NoiseThreadRequest_sp_SafeTArray());
  213.     this->completedThreadRequests = MakeShareable(new NoiseThreadRequest_sp_SafeTArray());
  214.  
  215.  
  216.     Thread = FRunnableThread::Create(this, TEXT("FNoiseWorker"), 0, TPri_BelowNormal); //windows default = 8mb for thread, could specify more
  217.  
  218. }
  219.  
  220. void FNoiseWorker::setPagerDetails(VTPagerDetails& pagerDetails) {
  221.     this->m_data = new FNoiseWorker::PrivateData(pagerDetails);
  222. }
  223.  
  224. FNoiseWorker::~FNoiseWorker()
  225. {
  226.     if (StopTaskCounter.GetValue() == 0) {
  227.         this->Stop();
  228.     }
  229.     onScreen("end thread!");
  230.     delete Thread;
  231.     Thread = NULL;
  232.     delete this->m_data;
  233. }
  234.  
  235. uint32 FNoiseWorker::Run()
  236. {
  237.     //Initial wait before starting
  238.     while (StopTaskCounter.GetValue() == 0) {
  239.         FPlatformProcess::Sleep(0.1);
  240.         //auto ctr = this->completedThreadRequests->checkout();
  241.         if (this->inputQueue->isEmpty()) {
  242.             continue;
  243.         }
  244.  
  245.         auto inQ = this->inputQueue->checkout();
  246.         auto ctr = this->completedThreadRequests->checkout();
  247.  
  248.         NoiseThreadRequest_sp threadReq = *inQ->GetData();
  249.         inQ->RemoveAt(0);
  250.  
  251.         ctr->Push(threadReq);
  252.  
  253.         this->inputQueue->checkin();
  254.         this->completedThreadRequests->checkin();
  255.  
  256.         {
  257.             //onScreen("FNoiseWorker received request for owner# " + FString::FromInt(threadReq->ownerID));
  258.             threadReq->noiseData = buildRegionDetails(this->m_data, threadReq->region);
  259.             //threadReq->noiseData = createSphereInVolume(threadReq->region);
  260.         }
  261.  
  262.         if ((StopTaskCounter.GetValue() == 0)) {
  263.             threadReq->processedNoiseRequests->push_back(threadReq); // this goes to ATerrainManager::Tick
  264.         }
  265.     }
  266.     this->Exit();
  267.     return 0;
  268. }
  269.  
  270. void FNoiseWorker::Stop() {
  271.     StopTaskCounter.Increment();
  272. }
  273.  
  274. void FNoiseWorker::terminateQueue() {
  275.     this->inputQueue->clear();
  276. }
Advertisement
Add Comment
Please, Sign In to add comment