namrog84
I am looking at the VoxelFarmDemoSimple, and see how they've added a custom voxellayer to a generator there, and are able to generate a solid amount similiar to what I'd like to accomplish in UE4?  Not sure how texture/materials work quite this way though. 

Specifically where the custom voxel layer is just a fairly straight forward perlinnoise getContourData overrided function. 
However, what Id like to do is be able to duplicate this type of functionality in UE4 itself? And not in this standalone approach


What I have in my mind I want to accomplish.
1. Use VoxelStudio to perhaps setup the materials/texture for the auto generation of the texture atlas (required?)
2. Go into UE4 and swap out the terrain voxel layers with something more custom written there? 
3. Then leverage the VoxelFarmWorldActor for simplicity of handling this custom generator/world hybrid?

Part 2 is the critical here because I want more finer grain control at world creation in-game then it appears I have accessing a more tightly controlled rule generated world. 

It currently seems like VoxelFarmWorldActor is tightly integrated with the concept of a project.  So either I need to likely abandon using this or majorly refactor this,  or perhaps generate the project itself before VoxelFarmWorldActor exists(Which I don't think is something I can do?)

The only way I've been able to think of so far, is to override everything with user modified voxel data. Essentially have a flatland world project, and then just at runtime modify all the voxel data from user level. However I believe this would destroy the performance and storage optimizations for the 'generator' of fire/use/forget of non-modified data. Since this doesn't override the generator and thus would assume a 100% 'user data created world'.  But this has a 2x perf impact because I'd still have to load up the initial flatland project before overriding/modifying all the voxel data points. And it'd likely instantly explode the storage/filesize. 

Any tips/tricks suggestions on how I can accomplish this?
1 0
voxelfarmtorres
This approach will work for UE4. You still need the concept of a project, since this is where your materials will be defined.

In Voxel Studio, make sure the terrain has no biomes, so this component will not take any resources.

You will need to add a line of code to insert your custom voxel layer into the generator used by the UE4 plugin.

To add a custom IVoxelLayer in the Unreal integration, locate the VoxelFarmWorldImpl.cpp file and find the constructor object for the VoxelFarmWorldImpl class. In this constructor, you will have access to the "worldGenerator" object. This is where the multiple voxel layers (like terrain) are already inserted. You can insert your own layer there by calling the "addVoxelLayer()" method:

worldGenerator->addVoxelLayer(myVoxelLayer, false);

Where "myVoxelLayer" is an instance of your custom IVoxelLayer class.
0 0
namrog84
I see no filenames with Impl in it, neither in my UE project nor VF installation SDK/paths.  So I couldn't find VoxelFarmWorldImpl.cpp.  

Ignoring not being able to find it via filenames, I started looking for any access to worldGenerator.  Thinking maybe you meant VoxelFarmWorldActor.cpp and saw no strong leads in there.  Aside from a call to VF_UEC::BundleWorld( which comes from VoxelFarmBundle.cpp

I did find 1 class that had a "worldGenerator" object, and that was the VoxelFarmBundle.cpp so I imagine maybe that's what you meant.


I am in the process of trying to add a custom layer to the worldGenerator in the the BundleWorld( call, because that is what is called via the constructor in VoxelFarmWorldActor. 



I have been able to modify the files and rebuild the plugin.  I am having unusual behavior/crashes though. 
I am able to make very minimal changes (added the code for class CSimpleVoxelLayer_Perlin3D and it's getContourData from VoxelFarmDemoSimple) and then try to add in the addVoxelLayer call for it.

e.g. 
CSimpleVoxelLayer_Perlin3D myLayer;
worldGenerator.addVoxelLayer(&myLayer, false);


Since I wanted to write as little as possible and re-use existing samples. I in, for the GetContourData function.
However, everytime I then try and launch/open back up my world after recompiling the plugin, I crash.


Access violation - code c0000005 (first/second chance not available)
UE4Editor_VoxelFarmPlugin!VoxelFarm::CGenerator::runJobs()
UE4Editor_VoxelFarmPlugin!VoxelFarm::CClipmapView::contourLoop()
UE4Editor_VoxelFarmPlugin!<lambda_3b937684a88fee091bf2dd4437bfc32f>::operator()() [e:\gamedev\ue\voxelfarmtesting\vfland2\plugins\voxelfarmplugin\source\voxelfarmplugin\private\voxelfarmworldactor.cpp:1142]
UE4Editor_VoxelFarmPlugin!TAsyncQueuedWork<void>::DoThreadedWork() [e:\program files\epic\ue_4.18\engine\source\runtime\core\public\async\async.h:228]



Am I adding in something at the wrong place? Or have any thoughts as to why this is?  I have tried several locations, and no luck. 

The only other code I've modified into the UE4 starter project was adding the VoxelFarmWorldActor (pointed project to a flatland VF project)
And add that actor to LevelBlueprints for updating my clipmap focus. (Looks identical to the help documentation example)
Lastly, the flatland VF Project I made, is an empty project with 3 materials added in it, pointed at grass/dirt/rock textures respectively. Nothing else changed, no biomes/terrain. 
0 0
voxelfarmtorres
I apologize, I now realize my reference for this was outdated. The location for this has changed. I will get the right info when the team comes back into the office tomorrow.
0 0
namrog84
I got it working!!

UE4Editor_2017-12-18_19-35-04.png

It was a C++ related issue.

My mistake was the lifetime of the object and the pointer.

I had been looking at the VoxelFarmDemoSimple.cpp sample
Where they used

CSimpleVoxelLayer_Perlin3D layer;
generator.addVoxelLayer(&layer);


Which I was duplicating that.  However, in that example  the layer is created in the main() function, thus it exists throughout the program. 
However in UE4 I had been adding it to BundleWorld() function, which is a relatively short lived function.  If you look at demoworld.cpp the voxel layers there are created at a global scope there so it also doesn't deallocate/destroy.

Because it gets deallocated and then VF tries to reference that same location later on. Thus the solution is quite obvious, create the custom layer in a place that it can be referenced and not destroyed after the function is finished.(Either at class or global level, or pass it in from the WorldActor of BundleWorld call.  

Once I made my custom voxel layer more permanently allocated in its lifetime and not allow it to be de-allocated after that function call ended, everything works perfectly fine now as seen in the picture.
1 0
namrog84
A few questions follow up regarding SetVoxelMaterial. 

So when I am using this, as shown in earlier picture, everything is the small cube. If I want something other than a perfect cube, I need to start introducing data->setVector( in my GetContourData function call right?

(0.5, 0.5, 0.5) would be the center of the voxel and indicates 0 direction or full cube? Thus I essentially need to set those values between 0 and 1, to indicate a direction that defines where the mesh will be. 

Just experimenting with values between 0 and 1, I can't quite get a handle on how this should behave.  Should only solids have this vector?  What about air? 

1. Is there any trick or good examples of how to conceptualize this and/or convert my leveraging this properly?
2. I'd imagine in perlin3d for example, I'd need to take additional subvoxel query points to figure out the vector (direction/density)? 
0 0
voxelfarmtorres
You are correct, you need to set vectors to the voxels in order for them to have the desired shape.

Please check out the guide on how to build an extension, even if the call to set the vector is different, the way you would compute the x,y,z values is the same:  http://www.voxelfarm.com/doc.html?creating-an-extension

By default, the vector is set at 0.5,0.5,0.5 which is the center of the voxel. Typically you would set vectors in the 0..1 range, but in reality, they can roam outside the voxel. The real range is from -1 to 2. This is used by voxelization to preserve even sharper features, you will not need the additional range for smooth terrain.

You need to set vectors to any voxel that is part of a material transition, does not matter if it is from air to solid, or vice-versa, from solid to air. A generic, robust approach that will work for any type of content is to check the 12 edges of a voxel. If there is a material transition crossing the voxel, you will know you need to set a vector. The vector could point to anywhere inside the voxel where you know the surface can be found. A generic way to find such point is to average the surface crossing points for all edges containing a crossing, but if you have an analytical form to produce your inner surface point, this would work too.

Once you progress with your custom layer, you may want to pay some attention to optimization. The sheer number of voxels to be evaluated can make your layer slow, creating issues like the viewer easily outrunning the procedural generation. We can certainly help you there, let us know when you hit this point.
0 0
namrog84
Excellent! I had overlooked the similarities/differences between writing a VF Studio custom extension and writing the Contour data directly for the custom voxel layer.


5hoQR5W.png 

Additional information for future people.

The main difference from the tutorial of extension that I had to change was the quite simple and that was the API. Its slightly different for writing an extension vs writing a custom voxel layer. 

Some things are slightly different like accessing some constants. In the extensions, some are passed into the function, whereas in a custom voxel layer its accessed via the global constants. 

data->setMaterial(index, material);  instead of materials[index] = material;
And of course data->setVector(index, vx, vy, vz); instead of modifying the vectors[index] array directly.

I'd imagine at compile time, they'd likely end up quite similar anyways. 

For those that wanted to duplicate my efforts. I was using the getContourData function sample provided by VoxelFarmDemoClipMat.cpp
and adding in the corners calculations was easy as shown in voxelfarmtorres's link above. 
double dx = (double)(x + CornerMap[corner][0] - VoxelFarm::BLOCK_MARGIN ) / (double)(VoxelFarm::BLOCK_DIMENSION);

Of course it needs to be moved to inside the corner's for loop instead of outside the y for loop as shown in that example.  Otherwise fairly straight forward small refactors and merging of samples. 



I will likely fall back to leveraging more of VF Studio in the future, I am still just experimenting and learning the ins and outs and any potential limitations before I fully dive into using VF for a game. I am sure when the time comes for optimizations in an actual game or something. I will definitely bring that up then.  I did notice that I had some slow perf generation with my current approach. I imagine I am likely doing a lot of unnecessary calculations. I think my current code doesn't appropriately consider a lot of things it should be. Even at a basic level I know I am recalculating corners field values of cubes for each point,  whereas those are not being cached/re-used by the neighbors for the same point in space let alone any more advanced changes.  

2 0
voxelfarmtorres
Great results!

Yes, probably the largest expense here is to evaluate each corner of a voxel.

For a heightmap-type of surface, like your object here, a typical optimization is to have a 2D cache where you compute the heights for each [x,z] point in the cell. XZ is the horizontal plane. You should align your 2D cache to iterate in this order z -> x, meaning you increment X coordinates faster than Z coordinates since the X loop is inside the Z loop.

Next, in a different loop, you would iterate over z -> x -> y. Before entering the last loop over Y, you can load 4 height points from the cache. These would be [x,z] [x+1,z] [x,z+1] [x+1,z+1]. Then you can proceed to iterate over Y and compute the eight voxel corners cheaply out of the 4 heights.

With this approach, you would be allocating a 2D buffer for your cache every time the getContourData is called. Memory allocation can be expensive, so it could be better to have one buffer you allocate once and then reuse for all cells. The problem with this is getContourData() for your layer will be called from different worker threads at the time. This is why getContourData() received an additional parameter called "threadContext". This is a pointer that can be anything you want and that is guaranteed by the engine to be thread-safe.

To use a Thread Context in your layer, you must override another two functions in the IVoxelLayer interface: createThreadContext() and disposeThreadContext().
0 0
namrog84
Thanks!

I was able to successfully setup the for loops before the main loop to precompute all the noise data for my given chunk. 
Then move onto the actual corner/vector calculations. 

Simply eliminating the redundant noise generation alone made an over 4x speedup in time to fully load my starting location's terrain.  I was also able to get the ThreadContext stuff to work as you suggested.  I'd imagine when I reincorporate non traditional heightmaps and using a 3D array, it'd make an even bigger difference. Also to have precomputed materials as well as not just field values. 
1 0