Example - Mesh Export
The following C# program connects to a Voxel Farm server and exports the terrain surface within a 3D region as a mesh:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VoxelFarm;
namespace ExportMesh
{
class Program
{
private static void Terrain2MeshOBJ(
string webServer,
string contentServerAddr,
int contentServerPort,
string projectId,
string entityId,
Region25D region,
int lod,
string outputFile)
{
// Create output file
System.IO.StreamWriter fileOutput = new StreamWriter(outputFile);
// Local variables to track OBJ output
bool done = false;
int totalFaces = 0;
int vertexIndexOffset = 1;
// Start work pool
VoxelFarmWorkPool.Start(2 * Environment.ProcessorCount);
// Create view
VoxelFarmView view = new VoxelFarmView();
// Print view debug log
view.OnDebugLog = (message) =>
{
//Console.WriteLine(message);
};
// Also print any uncaught error
VoxelFarmWorkPool.OnError = (Exception error) =>
{
Console.WriteLine(error.Message + " " + error.StackTrace);
done = true;
};
// Set OnViewStarted event
view.OnViewStarted = () =>
{
try
{
// Use the supplied region to create a set of ranges of 10x10x10 cells
var ranges = region.breakdownRegion(lod, 10, view.AffineTransform);
// Iterate over each range of cells
int totalRanges = ranges.Count;
int completedRanges = 0;
foreach (var range in ranges)
{
// Output progress message
int progress = (100 * completedRanges++) / totalRanges;
string message = (totalFaces > 0) ? String.Format("{0:0,0} triangles exported...", totalFaces) : "Scanning for triangles...";
Console.WriteLine(progress + "% " + message);
// Ask the view to load the cell range
view.SetCellRange(range);
// Wait until the view completes processing the range
view.Join();
// Check that we still have a connection to the server
if (view.ConnectionLost() || !view.IsConnected())
{
Console.WriteLine("Lost connection to content server.");
done = true;
}
}
done = true;
}
catch (Exception error)
{
Console.WriteLine(error.Message + " " + error.StackTrace);
done = true;
}
};
// Define a callaback to handle mesh data
view.OnReceiveMeshData = (data) =>
{
ManualResetEvent outputDone = new ManualResetEvent(false);
VoxelFarmWorkPool.RunInMainThread(() => {
int level, xc, yc, zc;
VoxelFarmCell.Unpack(data.cellId, out level, out xc, out yc, out zc);
double scale = VoxelFarmConstant.CELL_SIZE * (1 << level);
double cellOffsetX = scale * xc;
double cellOffsetY = scale * yc;
double cellOffsetZ = scale * zc;
int vertCount = data.vertices.Length / 3;
int faceCount = data.faces.Length / 3;
for (int i = 0; i < vertCount; i++)
{
var wc = view.AffineTransform.VF_TO_WC(
new VoxelFarmAffineTransform.sAffineVector
{
X = data.vertices[3 * i + 0] * scale + cellOffsetX,
Y = data.vertices[3 * i + 1] * scale + cellOffsetY,
Z = data.vertices[3 * i + 2] * scale + cellOffsetZ
});
fileOutput.WriteLine("v " + (-wc.X) + " " + wc.Z + " " + wc.Y);
}
for (int i = 0; i < faceCount; i++)
{
fileOutput.WriteLine("f " +
(data.faces[3 * i + 0] + vertexIndexOffset) + " " +
(data.faces[3 * i + 1] + vertexIndexOffset) + " " +
(data.faces[3 * i + 2] + vertexIndexOffset));
}
vertexIndexOffset += vertCount;
totalFaces += faceCount;
outputDone.Set();
});
outputDone.WaitOne();
};
// Set server and project
view.SetServer(webServer, contentServerAddr, contentServerPort, false);
view.SetProjectId(projectId);
// Load the project
view.LoadProject((HttpStatusCode httpcode) =>
{
if (httpcode == HttpStatusCode.OK)
{
// Find entity
var terrainEntity = view.GetEntity(entityId);
if (terrainEntity == null || terrainEntity["file_type"] != VoxelFarmEntityType.VoxelTerrain)
{
done = true;
return;
}
// Create view metadata
var components = new List<VoxelFarmMetaComponent>();
var componentMasks = new List<ushort>();
List<VoxelFarmMetaLayer> layerStack = new List<VoxelFarmMetaLayer>();
layerStack.Add(new VoxelFarmMetaLayerHM { Id = entityId, Config = terrainEntity["runtime"] });
var layers = new VoxelFarmMetaLayerStack(layerStack);
var surface = new VoxelFarmMetaSurfaceOperation { OpId = VoxelFarmMetaID.META_OP_NONE, OpA = layers };
components.Add(surface);
componentMasks.Add(0x02);
var componentMaterials = new List<ushort>();
componentMaterials.Add(0xFFFF);
var metadata = new VoxelFarmViewMeta(components, componentMasks, componentMaterials, null);
// Set view metadata, this will start the connection to the streaming server
view.SetViewMetaData(metadata);
}
else
{
done = true;
}
});
while (!done)
{
VoxelFarmWorkPool.RunNextMainThreadJob();
}
fileOutput.Close();
VoxelFarmWorkPool.Stop();
Console.WriteLine("Export complete.");
}
static void Main(string[] args)
{
if (args.Length != 8)
{
Console.WriteLine("Usage ExportMesh.exe <REST URL> <Streaming Server> <Streaming Port> <Project Id> <Entity Id> <Region> <LOD> <Output File>");
return;
}
try
{
Region25D region = new Region25D();
region.loadFromString(args[5]);
Terrain2MeshOBJ(args[0], args[1], Convert.ToInt32(args[2]), args[3], args[4], region, Convert.ToInt32(args[6]), args[7]);
}
catch (Exception error)
{
Console.WriteLine(error.Message + " " + error.StackTrace);
}
}
}
}
The following command line tests this program over the Sta. Barbara Island dataset, requesting a mesh export for the full island at LOD 5 resolution:
ExportMesh.exe http://18.236.158.139 18.236.158.139 3333 blank 390CC9D2B934446DAEE5EFE9F7D64C4F 1,1.0000000000002274,300,-13253468.999999998,3937303,-13248860.999999998,3937303,-13248860.999999998,3932695,-13253468.999999998,3932695 5 f:\scrap\m1.obj
Note that this program requires the “vfcompression.dll” to be in the same folder as the .EXE, or be in one of the folders specified in the system PATH variable.
The program produces the following output to the console window:
0% Scanning for triangles...
4% 20,378 triangles exported...
8% 59,296 triangles exported...
12% 120,883 triangles exported...
16% 174,325 triangles exported...
20% 198,326 triangles exported...
24% 231,524 triangles exported...
28% 304,836 triangles exported...
32% 367,609 triangles exported...
36% 410,992 triangles exported...
40% 423,511 triangles exported...
44% 448,613 triangles exported...
48% 522,385 triangles exported...
52% 548,733 triangles exported...
56% 592,684 triangles exported...
60% 597,296 triangles exported...
64% 631,860 triangles exported...
68% 694,986 triangles exported...
72% 759,434 triangles exported...
76% 780,406 triangles exported...
80% 781,337 triangles exported...
84% 806,390 triangles exported...
88% 848,185 triangles exported...
92% 869,169 triangles exported...
96% 875,037 triangles exported...
Export complete.
The exported OBJ mesh looks like this: