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: