Пример #1
0
        static bool RunExample(ISimplygonSDK sdk)
        {
            // run twice per LOD
            for (int i = 0; i < num_lods; ++i)
            {
                Console.WriteLine("\n-------------------------------------------\n");
                Console.WriteLine("LOD {0} of {1}.", i + 1, num_lods);
                Console.WriteLine("\n-------------------------------------------\n");
                //Remesh with constant pixel size, but different merge distances

                Console.WriteLine("\nRunning with merge distance = {0}...\n", merge_distances[0]);
                Console.WriteLine("------------------------------------\n");
                if (!run_remeshing_for_lod(sdk, i, merge_distances[0]))
                {
                    return(false);
                }
                Console.WriteLine("\nRunning with merge distance = {0}...\n", merge_distances[1]);
                Console.WriteLine("--------------\n");
                if (!run_remeshing_for_lod(sdk, i, merge_distances[1]))
                {
                    return(false);
                }
                Console.WriteLine("\n");
            }
            return(true);
        }
Пример #2
0
        static void Main(string[] args)
        {
            String licensePath = @"C:\Users\David\AppData\Local\DonyaLabs\SimplygonSDK\Simplygon_5_license.dat";

            if (!File.Exists(licensePath))
            {
                Console.WriteLine("License file not found");
            }
            string licenseString = File.ReadAllText(licensePath);

            SimplygonSDK.AddSearchPath(@"c:\Program Files\SimplygonSDK");
            int res = SimplygonSDK.Initialize(null, licenseString);

            if (res != 0)
            {
                Console.WriteLine("Simplygon inititialization failed with error: " + res);
                return;
            }
            ISimplygonSDK sdk = SimplygonSDK.GetSDK();

            if (sdk == null)
            {
                return;
            }
            Console.WriteLine("Simplygon version: " + sdk.GetVersion());
            RunHighQualityReduction(sdk, @"c:\temp\out");
        }
Пример #3
0
        static void Main(string[] args)
        {
            ISimplygonSDK sdk = Example.Example.InitExample();

            if (sdk == null)
            {
                return;
            }
            sdk.SetGlobalSetting("DefaultTBNType", (int)TangentSpaceMethod.SG_TANGENTSPACEMETHOD_ORTHONORMAL);
            RunExample(sdk);
        }
Пример #4
0
        // This function stores the data into an .obj file
        static bool save_geometry_to_file(ISimplygonSDK sdk, spScene scene, string filepath)
        {
            // create the wavefront exporter
            spWavefrontExporter exp = sdk.CreateWavefrontExporter();

            spGeometryData geom = Utils.SimplygonCast <spSceneMesh>(scene.GetRootNode().GetChild(0), false).GetGeometry();

            // set the geometry
            exp.SetSingleGeometry(geom);


            // set file path
            exp.SetExportFilePath(filepath);

            // export to file
            return(exp.RunExport());
        }
Пример #5
0
        static void Main(string[] args)
        {
            ISimplygonSDK sdk = Example.Example.InitExample();

            if (sdk == null)
            {
                return;
            }
            string userProfileDirectory = System.Environment.GetEnvironmentVariable("USERPROFILE");
            string assetRoot            = userProfileDirectory + @"/Documents/SimplygonSDK/SourceCode/Assets/";
            string tempRoot             = @"../../../../../temp/";

            Console.WriteLine("Running HQ Reduction");
            RunHighQualityReduction(sdk, assetRoot + "SimplygonMan.obj", tempRoot + "SimplygonMan_HQ_LOD");
            Console.WriteLine("Running Material Reduction");
            RunReductionWithTextureCasting(sdk, assetRoot + "SimplygonMan.obj", "SimplygonMan_Rebaked_Materials_LOD");
            Console.WriteLine("Running Cascaded LOD Reduction");
            RunCascadedLodChainReduction(sdk, assetRoot + "SimplygonMan.obj", "SimplygonMan_Cascade_LOD1", "SimplygonMan_Cascade_LOD2", "SimplygonMan_Cascade_LOD3");
        }
Пример #6
0
        static void RunExampleRemoveBonesFromSimplygonScene(ISimplygonSDK sdk)
        {
            string  userProfileDirectory = System.Environment.GetEnvironmentVariable("USERPROFILE");
            string  assetRoot            = userProfileDirectory + @"/Documents/SimplygonSDK/SourceCode/Assets/RiggedSimplygonMan/";
            string  tempRoot             = @"../../../../../temp/";
            spScene scene = sdk.CreateScene();

            if (!scene.LoadFromFile(assetRoot + "RiggedSimplygonMan.sgscene"))
            {
                Console.WriteLine("Failed to load scene");
                return;
            }

            Console.WriteLine("Reducing bones in RiggedSimplygonMan...\n");
            scene.CalculateExtents();

            //Run reduction processor with zero max_deviation - meaning that the
            //mesh won't be reduced.

            Console.WriteLine("By 20%%\n");
            RunReductionProcessing(sdk, scene, 0.0f, 0.8f);
            if (!scene.SaveToFile(tempRoot + "RiggedSimplygonMan_BoneLOD_80.sgscene"))
            {
                Console.WriteLine("Failed to save scene");
                return;
            }

            Console.WriteLine("By 50%%\n");
            RunReductionProcessing(sdk, scene, 0.0f, 0.5f);
            if (!scene.SaveToFile(tempRoot + "RiggedSimplygonMan_BoneLOD_50.sgscene"))
            {
                Console.WriteLine("Failed to save scene");
                return;
            }

            Console.WriteLine("By 80%%\n");
            RunReductionProcessing(sdk, scene, 0.0f, 0.2f);
            if (!scene.SaveToFile(tempRoot + "RiggedSimplygonMan_BoneLOD_20.sgscene"))
            {
                Console.WriteLine("Failed to save scene");
                return;
            }
        }
Пример #7
0
        static void Main(string[] args)
        {
            // init SDK
            ISimplygonSDK sdk = Example.Example.InitExample();

            if (sdk == null)
            {
                return;
            }
            //MoveDirToExecutablePath(_T("RiggedSimplygonMan"), _T(""));

            // Run the example code
            RunExampleGenerateAndBendHelix(sdk);
            RunExampleRemoveBonesFromSimplygonScene(sdk);

            // deinit SDK
            //DeinitExample();

            return;
        }
Пример #8
0
        static void RunExampleGenerateAndBendHelix(ISimplygonSDK sdk)
        {
            // generate the initial tube geometry
            Console.WriteLine("Generating helix-shaped tube...\n");
            spScene scene    = generate_simple_tube(sdk);
            spScene sceneCpy = generate_simple_tube(sdk);

            string tempRoot = @"../../../../../temp/";

            string outputPath = tempRoot + "original_tube.obj";

            //store the original data to a wavefront file
            save_geometry_to_file(sdk, scene, outputPath);


            //take a copy of the original geometry and do a bend and save to file
            bend_geometry(sdk, sceneCpy);
            outputPath = tempRoot + "original_bended_tube.obj";
            save_geometry_to_file(sdk, sceneCpy, outputPath);

            // now, reduce the geometry, using max deviation
            Console.WriteLine("Reducing using max deviation (Bone reduced by half)...\n");
            RunReductionProcessing(sdk, scene, 1.0f, 0.5f);

            outputPath = tempRoot + "reduced_tube.obj";
            // store the reduced geometry to a wavefront file
            save_geometry_to_file(sdk, scene, outputPath);

            // now, bend the reduced tube using the blend weights
            Console.WriteLine("Bending the reduced tube using blend...\n");
            bend_geometry(sdk, scene);

            outputPath = tempRoot + "bended_reduced_tube.obj";
            // store the reduced geometry to a wavefront file
            save_geometry_to_file(sdk, scene, outputPath);
        }
Пример #9
0
        static void RunHighQualityReduction(ISimplygonSDK sdk, string readFrom, string writeTo)
        {
            spWavefrontImporter objReader = sdk.CreateWavefrontImporter();

            objReader.SetExtractGroups(false); //This makes the .obj reader import into a single geometry object instead of multiple
            objReader.SetImportFilePath(readFrom);
            if (!objReader.RunImport())
            {
                Console.WriteLine("Failed to read: " + readFrom);
                return;
            }
            spGeometryData  originalGeom      = objReader.GetFirstGeometry(); //Only contains a single geom, so "first" is fine
            spMaterialTable originalMaterials = objReader.GetMaterials();

            //Create a copy of the original geometry on which we will run the reduction
            spGeometryData lodGeom = originalGeom.NewCopy(true);

            // Create the reduction-processor, and set the geometry to reduce
            spReductionProcessor reductionProcessor = sdk.CreateReductionProcessor();

            reductionProcessor.SetGeometry(lodGeom);

            ///////////////////////////////////////////////////////////////////////////////////////////////
            // SETTINGS - Most of these are set to the same value by default, but are set anyway for clarity

            // The reduction settings object contains settings pertaining to the actual decimation
            spReductionSettings reductionSettings = reductionProcessor.GetReductionSettings();

            reductionSettings.SetEnablePreprocessing(true);                                                        //This enables the pre-processing block, which contains welding and t-junction removal
            reductionSettings.SetEnablePostprocessing(true);                                                       //This enables the post-processing block, which contains normal recalculation and mapping image generation
            reductionSettings.SetKeepSymmetry(true);                                                               //Try, when possible to reduce symmetrically
            reductionSettings.SetUseAutomaticSymmetryDetection(true);                                              //Auto-detect the symmetry plane, if one exists. Can, if required, be set manually instead.
            reductionSettings.SetUseHighQualityNormalCalculation(true);                                            //Drastically increases the quality of the LODs normals, at the cost of extra processing time.
            reductionSettings.SetReductionHeuristics((uint)ReductionHeuristics.SG_REDUCTIONHEURISTICS_CONSISTENT); //Choose between "fast" and "consistent" processing. Fast will look as good, but may cause inconsistent
            //triangle counts when comparing MaxDeviation targets to the corresponding percentage targets.

            // The reducer uses a feature flags mask to tell it what kind of borders to respect during reduction.
            FeatureFlags featureFlagsMask = 0;

            featureFlagsMask |= FeatureFlags.SG_FEATUREFLAGS_GROUP;    //Respect borders between group ids
            featureFlagsMask |= FeatureFlags.SG_FEATUREFLAGS_MATERIAL; //Respect borders between material ids
            featureFlagsMask |= FeatureFlags.SG_FEATUREFLAGS_TEXTURE0; //Respect discontinuities in the first texcoord field
            featureFlagsMask |= FeatureFlags.SG_FEATUREFLAGS_SHADING;  //Respect hard shading borders
            reductionSettings.SetFeatureFlags((uint)featureFlagsMask);

            // The reducer uses importance weights for all features to decide where and how to reduce.
            // These are advanced settings and should only be changed if you have some specific reduction requirement
            /*reductionSettings.SetShadingImportance(2.f); //This would make the shading twice as important to the reducer as the other features.*/

            // The actual reduction triangle target are controlled by these three settings
            reductionSettings.SetStopCondition((uint)StopCondition.SG_STOPCONDITION_EITHER_IS_REACHED); //The reduction stops when either of the targets is reached
            reductionSettings.SetReductionRatio(0.5f);                                                  //Stops at 50% of the original triangle count
            reductionSettings.SetMaxDeviation(SimplygonSDK.REAL_MAX);                                   //Stops when an error of the specified size has been reached. As set here it never happens.
            //This condition corresponds to the on-screen size target presented in the Simplygon GUI, with a simple formula to convert between the two.

            // The repair settings object contains settings for the pre-processing block
            spRepairSettings repairSettings = reductionProcessor.GetRepairSettings();

            repairSettings.SetTjuncDist(0.0f); //Removes t-junctions with distance 0.0f
            repairSettings.SetWeldDist(0.0f);  //Welds overlapping vertices

            // The normal calculation settings deal with the normal-specific reduction settings
            spNormalCalculationSettings normalSettings = reductionProcessor.GetNormalCalculationSettings();

            normalSettings.SetReplaceNormals(false); //If true, this will turn off normal handling in the reducer and recalculate them all afterwards instead.
            //If false, the reducer will try to preserve the original normals as well as possible
            /*normalSettings.SetHardEdgeAngle( 60.f ); //If the normals are recalculated, this sets the hard-edge angle.*/

            //END SETTINGS
            ///////////////////////////////////////////////////////////////////////////////////////////////


            // Run the actual processing. After this, the set geometry will have been reduced according to the settings
            reductionProcessor.RunProcessing();

            //Create an .obj exporter to save our result
            spWavefrontExporter objExporter = sdk.CreateWavefrontExporter();

            // Do the actual exporting
            objExporter.SetExportFilePath(writeTo + ".obj");
            objExporter.SetSingleGeometry(lodGeom);      //This is the geometry we set as the processing geom of the reducer
            objExporter.SetMaterials(originalMaterials); //Same material set as input
            if (!objExporter.RunExport())
            {
                Console.WriteLine("Failed to write target file");
            }

            //Done! LOD created.
        }
Пример #10
0
        static void RunCascadedLodChainReduction(ISimplygonSDK sdk, string readFrom, string writeToLod1, string writeToLod2, string writeToLod3)
        {
            // Load input geometry from file
            spWavefrontImporter objReader = sdk.CreateWavefrontImporter();

            objReader.SetExtractGroups(false); //This makes the .obj reader import into a single geometry object instead of multiple
            objReader.SetImportFilePath(readFrom);
            if (!objReader.RunImport())
            {
                return;
            }

            // Get geometry and materials from importer
            spGeometryData  originalGeometry      = objReader.GetFirstGeometry(); //Only contains a single geom, so "first" is fine
            spMaterialTable originalMaterialTable = objReader.GetMaterials();

            //Create a copy of the original geometry on which we will run the reduction
            spGeometryData lodGeometry = originalGeometry.NewCopy(true);

            // Create the reduction-processor, and set the geometry to reduce
            spReductionProcessor reductionProcessor = sdk.CreateReductionProcessor();

            reductionProcessor.SetGeometry(lodGeometry);


            ///////////////////////////////////////////////////////////////////////////////////////////////
            // SETTINGS

            // The reduction settings object contains settings pertaining to the actual decimation
            spReductionSettings reductionSettings = reductionProcessor.GetReductionSettings();

            reductionSettings.SetReductionHeuristics((uint)ReductionHeuristics.SG_REDUCTIONHEURISTICS_FAST); //Choose between "fast" and "consistent" processing.

            // The normal calculation settings deal with the normal-specific reduction settings
            spNormalCalculationSettings normalSettings = reductionProcessor.GetNormalCalculationSettings();

            normalSettings.SetReplaceNormals(true); //If true, this will turn off normal handling in the reducer and recalculate them all afterwards instead.
            normalSettings.SetHardEdgeAngle(70.0f); //If the normals are recalculated, this sets the hard-edge angle.

            // The actual reduction triangle target are controlled by these three settings
            reductionSettings.SetStopCondition((uint)StopCondition.SG_STOPCONDITION_EITHER_IS_REACHED); //The reduction stops when either of the targets is reached
            reductionSettings.SetReductionRatio(0.0f);                                                  //Never stop at a triangle percentage, will hit MaxDeviation instead.


            //END SETTINGS
            ///////////////////////////////////////////////////////////////////////////////////////////////

            //Create an .obj exporter to save our result
            spWavefrontExporter objExporter = sdk.CreateWavefrontExporter();

            objExporter.SetSingleGeometry(lodGeometry);      //This is the geometry we set as the processing geom of the reducer
            objExporter.SetMaterials(originalMaterialTable); //Same materials as original

            //Set reduction targets using on-screen size to set the maximum deviation
            originalGeometry.CalculateExtents(true); //This calculates the bounds of the geometry
            float[] geometryInf = new float[3];
            float[] geometrySup = new float[3];
            originalGeometry.GetInf(geometryInf);
            originalGeometry.GetSup(geometrySup);
            float geometryDiagonalLength = (float)Math.Sqrt((double)((geometrySup[0] - geometryInf[0]) * (geometrySup[0] - geometryInf[0]) +
                                                                     (geometrySup[1] - geometryInf[1]) * (geometrySup[1] - geometryInf[1]) +
                                                                     (geometrySup[2] - geometryInf[2]) * (geometrySup[2] - geometryInf[2])));

            float[] maxDeviationTargets = new float[3];               //To find an approximate MaxDeviation for a pixelsize on screen, we just divide the diagonal by our wanted pixelsize
            maxDeviationTargets[0] = geometryDiagonalLength / 500.0f; //Gives a deviation of max 1 pixel at ~500 pixels on-screen
            maxDeviationTargets[1] = geometryDiagonalLength / 100.0f; //Gives a deviation of max 1 pixel at ~100 pixels on-screen
            maxDeviationTargets[2] = geometryDiagonalLength / 50.0f;  //Gives a deviation of max 1 pixel at ~50 pixels on-screen

            //Generate the output filenames
            string[] outputGeomFilename = new string[3];
            outputGeomFilename[0] = writeToLod1 + ".obj";
            outputGeomFilename[1] = writeToLod2 + ".obj";
            outputGeomFilename[2] = writeToLod3 + ".obj";

            // Run the iterative processing, saving the output geometry after every process
            for (int reductionIteration = 0; reductionIteration < 3; ++reductionIteration)
            {
                // The geometry still uses the same pointer, so it does not need to be re-set for the exporter or reducer after each pass.
                reductionSettings.SetMaxDeviation(maxDeviationTargets[reductionIteration]); //Stops when an error of the specified size has been reached.
                reductionProcessor.RunProcessing();

                // Do the exporting
                objExporter.SetMaterialFilePath(null); //Reset the material file path so it's set by ExportFilePath
                objExporter.SetExportFilePath(outputGeomFilename[reductionIteration]);
                if (!objExporter.RunExport())
                {
                    Console.WriteLine("Failed to write target file");
                }
            }

            //Done! 3 cascaded LODs created.
        }
Пример #11
0
        static void RunReductionWithTextureCasting(ISimplygonSDK sdk, string readFrom, string writeTo)
        {
            // Load input geometry from file
            spWavefrontImporter objReader = sdk.CreateWavefrontImporter();

            objReader.SetExtractGroups(false); //This makes the .obj reader import into a single geometry object instead of multiple
            objReader.SetImportFilePath(readFrom);
            if (!objReader.RunImport())
            {
                Console.WriteLine("Failed to read: " + readFrom);
                return;
            }

            // Get geometry and materials from importer
            spGeometryData  originalGeom          = objReader.GetFirstGeometry(); //Only contains a single geom, so "first" is fine
            spMaterialTable originalMaterialTable = objReader.GetMaterials();

            // Create a copy of the original geometry on which we will run the reduction
            spGeometryData lodGeometry = originalGeom.NewCopy(true);

            // Create the reduction-processor, and set the geometry to reduce
            spReductionProcessor reductionProcessor = sdk.CreateReductionProcessor();

            reductionProcessor.SetGeometry(lodGeometry);


            ///////////////////////////////////////////////////////////////////////////////////////////////
            // SETTINGS - Most of these are set to the same value by default, but are set anyway for clarity

            // The reduction settings object contains settings pertaining to the actual decimation
            spReductionSettings reductionSettings = reductionProcessor.GetReductionSettings();

            reductionSettings.SetReductionHeuristics((uint)ReductionHeuristics.SG_REDUCTIONHEURISTICS_FAST); //Choose between "fast" and "consistent" processing.

            // The actual reduction triangle target are controlled by these three settings
            reductionSettings.SetStopCondition((uint)StopCondition.SG_STOPCONDITION_EITHER_IS_REACHED); //The reduction stops when either of the targets is reached
            reductionSettings.SetReductionRatio(0.5f);                                                  //Stops at 50% of the original triangle count
            reductionSettings.SetMaxDeviation(SimplygonSDK.REAL_MAX);                                   //Stops when an error of the specified size has been reached. As set here it never happens.

            // The normal calculation settings deal with the normal-specific reduction settings
            spNormalCalculationSettings normalSettings = reductionProcessor.GetNormalCalculationSettings();

            normalSettings.SetReplaceNormals(true); //If true, this will turn off normal handling in the reducer and recalculate them all afterwards instead.
            normalSettings.SetHardEdgeAngle(70.0f); //If the normals are recalculated, this sets the hard-edge angle.

            // The Image Mapping Settings, specifically needed for the texture baking we are doing later
            spMappingImageSettings mappingSettings = reductionProcessor.GetMappingImageSettings();

            mappingSettings.SetGenerateMappingImage(true); //Without this we cannot fetch data from the original geometry, and thus not generate diffuse and normal-maps later on.
            mappingSettings.SetGenerateTexCoords(true);    //Set to generate new texture coordinates.
            mappingSettings.SetMaxStretch(0.4f);           //The higher the number, the fewer texture-borders.
            mappingSettings.SetGutterSpace(2);             //Buffer space for when texture is mip-mapped, so color values don't blend over. Greatly influences packing efficiency
            mappingSettings.SetTexCoordLevel(0);           //Sets the output texcoord level. For this asset, this will overwrite the original coords
            mappingSettings.SetWidth(1024);
            mappingSettings.SetHeight(1024);
            mappingSettings.SetMultisamplingLevel(2);

            //END SETTINGS
            ///////////////////////////////////////////////////////////////////////////////////////////////


            // Run the actual processing. After this, the set geometry will have been reduced according to the settings
            reductionProcessor.RunProcessing();


            ///////////////////////////////////////////////////////////////////////////////////////////////
            // CASTING

            // Now, we need to retrieve the generated mapping image and use it to cast the old materials into a new one, for each channel.
            spMappingImage mappingImage = reductionProcessor.GetMappingImage();

            // Now, for each channel, we want to cast the 9 input materials into a single output material, with one texture per channel.
            // First, create a new material table.
            spMaterialTable lodMaterialTable = sdk.CreateMaterialTable();
            // Create new material for the table.
            spMaterial lodMaterial = sdk.CreateMaterial();

            lodMaterial.SetName("SimplygonBakedMaterial");
            lodMaterialTable.AddMaterial(lodMaterial);
            // Cast diffuse and specular texture data with a color caster
            {
                // Set the material properties
                lodMaterial.SetColor(SimplygonSDK.SG_MATERIAL_CHANNEL_AMBIENT, 0, 0, 0, 0);
                lodMaterial.SetColor(SimplygonSDK.SG_MATERIAL_CHANNEL_DIFFUSE, 1, 1, 1, 1);
                lodMaterial.SetColor(SimplygonSDK.SG_MATERIAL_CHANNEL_SPECULAR, 1, 1, 1, 128);
                //Note the 128 on the specular channels alpha. Simplygon bakes shininess
                //to the alpha channel of the specular map if the caster is set to 4 channel
                //output, and it is scaled between 0 and 1 internally. To get the correct
                //scale on the output, it should be multiplied by 128.

                // Cast the data using a color caster
                spColorCaster colorCaster = sdk.CreateColorCaster();
                colorCaster.SetSourceMaterials(originalMaterialTable);
                colorCaster.SetDestMaterial(lodMaterial);  //This modulates the cast color with the base colors set for the dest material above.
                //It means the internal shininess is multiplied by 128 before baking to texture.
                colorCaster.SetMappingImage(mappingImage); //The mapping image we got from the reduction process.
                colorCaster.SetOutputChannelBitDepth(8);   //8 bits per channel. So in this case we will have 24bit colors RGB.
                colorCaster.SetDilation(10);               //To avoid mip-map artifacts, the empty pixels on the map needs to be filled to a degree as well.

                colorCaster.SetColorType(SimplygonSDK.SG_MATERIAL_CHANNEL_DIFFUSE);
                colorCaster.SetOutputChannels(3);                        //RGB, 3 channels! (1 would be for grey scale, and 4 would be for RGBA.)
                colorCaster.SetOutputFilePath("combinedDiffuseMap.png"); //Where the texture map will be saved to file.
                colorCaster.CastMaterials();                             //Do the actual casting and write to texture.

                colorCaster.SetColorType(SimplygonSDK.SG_MATERIAL_CHANNEL_SPECULAR);
                colorCaster.SetOutputChannels(4);                         //RGBA, 4 channels! Stores spec power in A
                colorCaster.SetOutputFilePath("combinedSpecularMap.png"); //Where the texture map will be saved to file.
                colorCaster.CastMaterials();                              //Do the actual casting and write to texture.


                lodMaterial.SetTexture(SimplygonSDK.SG_MATERIAL_CHANNEL_DIFFUSE, "combinedDiffuseMap.png");   //Set material to point to the texture we cast to above
                lodMaterial.SetTexture(SimplygonSDK.SG_MATERIAL_CHANNEL_SPECULAR, "combinedSpecularMap.png"); //Set material to point to the texture we cast to above
            }

            // Cast normal map texture data with the normal caster. This also compensates for any geometric errors that have appeared in the reduction process.
            {
                // cast the data using a normal caster
                spNormalCaster normalCaster = sdk.CreateNormalCaster();
                normalCaster.SetSourceMaterials(originalMaterialTable);
                normalCaster.SetMappingImage(mappingImage);
                normalCaster.SetOutputChannels(3); // RGB, 3 channels! (But really the x, y and z values for the normal)
                normalCaster.SetOutputChannelBitDepth(8);
                normalCaster.SetDilation(10);
                normalCaster.SetOutputFilePath("combinedNormalMap.png");
                normalCaster.SetFlipBackfacingNormals(false);
                normalCaster.SetGenerateTangentSpaceNormals(true);
                normalCaster.CastMaterials();

                // Set normal map of the created material to point to the combined normal map
                lodMaterial.SetTexture(SimplygonSDK.SG_MATERIAL_CHANNEL_NORMALS, "combinedNormalMap.png");
            }

            // END CASTING
            ///////////////////////////////////////////////////////////////////////////////////////////////

            //Create an .obj exporter to save our result
            spWavefrontExporter objExporter = sdk.CreateWavefrontExporter();

            // Generate the output filenames
            // Do the actual exporting
            objExporter.SetExportFilePath(writeTo + ".obj");
            objExporter.SetSingleGeometry(lodGeometry); //This is the geometry we set as the processing geom of the reducer
            objExporter.SetMaterials(lodMaterialTable); //Our new cast material
            if (!objExporter.RunExport())
            {
                Console.WriteLine("Failed to write target file");
            }

            //Done! LOD and material created.
        }
Пример #12
0
        private static void RunHighQualityReduction(ISimplygonSDK SDK, string writeTo)
        {
            const int vertex_count   = 12;
            const int triangle_count = 4;

            // 4 triangles x 3 indices ( or 3 corners )
            int[] corner_ids = { 0,  1, 2,
                                 3,  4, 5,
                                 6,  7, 8,
                                 9, 10, 11 };

            // 12 vertices with values for the x, y and z coordinates.
            float[] vertex_coordinates = { 0.0f, 0.0f, 0.0f,
                                           1.0f, 0.0f, 0.0f,
                                           1.0f, 1.0f, 0.0f,

                                           1.0f, 1.0f, 0.0f,
                                           0.0f, 1.0f, 0.0f,
                                           0.0f, 0.0f, 0.0f,

                                           1.0f, 0.0f, 0.0f,
                                           2.0f, 0.0f, 0.0f,
                                           2.0f, 1.0f, 0.0f,

                                           2.0f, 1.0f, 0.0f,
                                           1.0f, 1.0f, 0.0f,
                                           1.0f, 0.0f, 0.0f };

            spGeometryData g      = SDK.CreateGeometryData();
            spRealArray    coords = g.GetCoords();
            spRidArray     ids    = g.GetVertexIds();

            g.SetVertexCount(vertex_count);
            g.SetTriangleCount(triangle_count);
            for (int i = 0; i < vertex_count; ++i)
            {
                //coords.SetTuple(i, vertex_coordinates[i * 3]);
                //coords[i] = vertex_coordinates[i * 3];
                float[] v = { 0.0f, 0.0f, 0.0f };
                for (int j = 0; j < 3; ++j)
                {
                    v[j] = vertex_coordinates[i * 3 + j];
                }

                coords.SetTuple(i, v);
            }
            for (int i = 0; i < corner_ids.Length; ++i)
            {
                ids.SetItem(i, corner_ids[i]);
            }

            // Create the reduction-processor, and set which scene to reduce
            using (var reductionProcessor = SDK.CreateReductionProcessor())
            {
                spScene     scene    = SDK.CreateScene();
                spSceneNode root     = scene.GetRootNode();
                spSceneMesh meshNode = SDK.CreateSceneMesh();
                meshNode.SetGeometry(g);
                //auto meshTransform = meshNode->GetRelativeTransform();
                //meshTransform->SetToTranslationTransform(100, 0, 0);
                root.AddChild(meshNode);
                reductionProcessor.SetSceneRoot(root);


                ///////////////////////////////////////////////////////////////////////////////////////////////
                // SETTINGS - Most of these are set to the same value by default, but are set anyway for clarity

                // The reduction settings object contains settings pertaining to the actual decimation
                var reductionSettings = reductionProcessor.GetReductionSettings();
                reductionSettings.SetKeepSymmetry(true);                                                               //Try, when possible to reduce symmetrically
                reductionSettings.SetUseAutomaticSymmetryDetection(true);                                              //Auto-detect the symmetry plane, if one exists. Can, if required, be set manually instead.
                reductionSettings.SetUseHighQualityNormalCalculation(true);                                            //Drastically increases the quality of the LODs normals, at the cost of extra processing time.
                reductionSettings.SetReductionHeuristics((uint)ReductionHeuristics.SG_REDUCTIONHEURISTICS_CONSISTENT); //Choose between "fast" and "consistent" processing. Fast will look as good, but may cause inconsistent
                //triangle counts when comparing MaxDeviation targets to the corresponding percentage targets.

                // The reducer uses importance weights for all features to decide where and how to reduce.
                // These are advanced settings and should only be changed if you have some specific reduction requirement
                //reductionSettings.SetShadingImportance(2.f); //This would make the shading twice as important to the reducer as the other features./

                // The actual reduction triangle target are controlled by these settings
                reductionSettings.SetStopCondition((uint)StopCondition.SG_STOPCONDITION_EITHER_IS_REACHED); //The reduction stops when any of the targets below is reached
                reductionSettings.SetReductionRatio(0.5f);                                                  //Targets at 50% of the original triangle count
                reductionSettings.SetMaxDeviation(float.MaxValue);                                          //Targets when an error of the specified size has been reached. As set here it never happens.

                // The repair settings object contains settings to fix the geometries
                var repairSettings = reductionProcessor.GetRepairSettings();
                repairSettings.SetTjuncDist(0.0f); //Removes t-junctions with distance 0.0f
                repairSettings.SetWeldDist(0.0f);  //Welds overlapping vertices

                // The normal calculation settings deal with the normal-specific reduction settings
                var normalSettings = reductionProcessor.GetNormalCalculationSettings();
                normalSettings.SetReplaceNormals(false); //If true, this will turn off normal handling in the reducer and recalculate them all afterwards instead.
                //If false, the reducer will try to preserve the original normals as well as possible
                //normalSettings.SetHardEdgeAngle( 60.f ); //If the normals are recalculated, this sets the hard-edge angle./

                //END SETTINGS
                ///////////////////////////////////////////////////////////////////////////////////////////////

                // Run the actual processing. After this, the set geometry will have been reduced according to the settings
                reductionProcessor.RunProcessing();

                // For this reduction, the LOD will use the same material set as the original, and hence no further processing is required

                //Create an .obj exporter to save our result
                using (var objExporter = SDK.CreateWavefrontExporter())
                {
                    // Generate the output filenames
                    string      outputGeomFilename = writeTo + ".obj";
                    spSceneMesh topmesh            = Utils.SimplygonCast <spSceneMesh>(scene.GetRootNode().GetChild(0), false);


                    // Do the actual exporting
                    objExporter.SetExportFilePath(outputGeomFilename);
                    objExporter.SetSingleGeometry(topmesh.GetGeometry()); //This is the geometry we set as the processing geom of the reducer, retaining the materials in the original scene
                    objExporter.RunExport();

                    //Done! LOD created.
                }
            }
        }
Пример #13
0
        static void RunReductionProcessing(ISimplygonSDK sdk, spScene scene, float max_dev, float keep_bone_ratio)
        {
            // Create the reduction processor. Set the scene that is to be processed
            spReductionProcessor red = sdk.CreateReductionProcessor();

            red.SetSceneRoot(scene.GetRootNode());

            ///////////////////////////////////////////////////
            //	Set the bone settings
            spBoneSettings boneSettings = red.GetBoneSettings();

            // Reduce bones based on percentage of bones in the scene.
            // Bone lod process tells the reduction processor the method
            // to use for bone reduction.
            boneSettings.SetBoneLodProcess((uint)BoneRemovalProcessing.SG_BONEPROCESSING_RATIO_PROCESSING);

            // Set the ratio of bones to keep in the scene
            boneSettings.SetBoneLodRatio(keep_bone_ratio);

            ///////////////////////////////////////////////////
            //
            // Set the Repair Settings. Current settings will mean that all visual gaps will remain in the geometry and thus
            // hinder the reduction on geometries that contains gaps, holes and t-junctions.
            spRepairSettings repair_settings = red.GetRepairSettings();

            // Only vertices that actually share the same position will be welded together
            repair_settings.SetWeldDist(0.0f);

            // Only t-junctions with no actual visual distance will be fixed.
            repair_settings.SetTjuncDist(0.0f);

            ///////////////////////////////////////////////////
            //
            // Set the Reduction Settings.
            spReductionSettings reduction_settings = red.GetReductionSettings();

            // These flags will make the reduction process respect group and material setups,
            // as well as preserve UV coordinates.
            FeatureFlags BorderFlagsMask = 0;

            BorderFlagsMask |= FeatureFlags.SG_FEATUREFLAGS_GROUP;
            BorderFlagsMask |= FeatureFlags.SG_FEATUREFLAGS_MATERIAL;
            BorderFlagsMask |= FeatureFlags.SG_FEATUREFLAGS_TEXTURE0;

            reduction_settings.SetFeatureFlags((uint)BorderFlagsMask);

            // Reduce until we reach max deviation.
            reduction_settings.SetMaxDeviation(max_dev);

            ///////////////////////////////////////////////////
            //
            // Set the Normal Calculation Settings.
            spNormalCalculationSettings normal_settings = red.GetNormalCalculationSettings();

            // Will completely recalculate the normals.
            normal_settings.SetReplaceNormals(true);
            normal_settings.SetHardEdgeAngle(90.0f);

            // Run the process
            red.RunProcessing();
        }
Пример #14
0
        static void bend_geometry(ISimplygonSDK sdk, spScene scene)
        {
            // The two bones that influence the vertices
            //spMatrix4x4 sp_bone1;

            spGeometryData geom = Utils.SimplygonCast <spSceneMesh>(scene.GetRootNode().GetChild(0)).GetGeometry();

            // get the bone weights field and ids
            spRealArray boneWeights = geom.GetBoneWeights();
            spRidArray  boneIds     = geom.GetBoneIds();

            // get the Coordinates field
            spRealArray Coords = geom.GetCoords();

            // now, transform all vertices' coordinates using the bones
            for (int v = 0; v < Coords.GetTupleCount(); ++v)
            {
                Vector3D vtx = new Vector3D(Coords.GetItem(v * 3 + 0), Coords.GetItem(v * 3 + 1), Coords.GetItem(v * 3 + 2));

                uint no = boneIds.GetItemCount();

                int bone_id_1 = boneIds.GetItem(v * 2 + 0);
                int bone_id_2 = boneIds.GetItem(v * 2 + 1);

                spSceneBone bone_1;
                spSceneBone bone_2;

                Matrix4x4 b1gtMat;
                Matrix4x4 b2gtMat;

                Vector3D vtx1 = new Vector3D(vtx);
                Vector3D vtx2 = new Vector3D(vtx);

                if (bone_id_1 != -1)
                {
                    bone_1 = scene.GetBoneTable().GetBone(bone_id_1);

                    //retrieve the global transform of the bone in bind space
                    spMatrix4x4 rootGlobalTransform = sdk.CreateMatrix4x4();
                    bone_1.EvaluateDefaultGlobalTransformation(rootGlobalTransform);

                    //apply transfrom to animate bone
                    spTransform3 bone1_transform = sdk.CreateTransform3();
                    bone1_transform.AddTransformation(rootGlobalTransform);
                    bone1_transform.AddRotation(GetRadFromDegrees(-30), 1, 0, 0);
                    rootGlobalTransform = bone1_transform.GetMatrix();

                    //apply transform
                    b1gtMat = GetMatrix4x4FromIMatrix(rootGlobalTransform);
                    vtx1    = b1gtMat.MultiplyPointVector(vtx);
                }

                if (bone_id_2 != -1)
                {
                    bone_2 = scene.GetBoneTable().GetBone(bone_id_2);

                    spMatrix4x4 boneGlobalTransform = sdk.CreateMatrix4x4();
                    bone_2.EvaluateDefaultGlobalTransformation(boneGlobalTransform);

                    spTransform3 bone2_transform = sdk.CreateTransform3();

                    //transform into bone2's local space and apply transform
                    bone2_transform.PreMultiply();
                    boneGlobalTransform.Invert();
                    bone2_transform.AddTransformation(boneGlobalTransform);
                    bone2_transform.AddRotation(GetRadFromDegrees(-30), 1, 0, 0);
                    bone2_transform.AddTranslation(0.0f, 17.5f, 0.0f);

                    //apply transform of the first bone
                    bone2_transform.AddRotation(GetRadFromDegrees(-30), 1, 0, 0);

                    //get the global transform matrix for the animation pose
                    boneGlobalTransform = bone2_transform.GetMatrix();
                    b2gtMat             = GetMatrix4x4FromIMatrix(boneGlobalTransform);

                    //apply transform to the vertex
                    vtx2 = b2gtMat.MultiplyPointVector(vtx);
                }

                //get the bone weights from the geometry data
                float blend1 = boneWeights.GetItem(v * 2 + 0);
                float blend2 = boneWeights.GetItem(v * 2 + 1);

                // normalize the blend
                float blend_scale = 1.0f / (blend1 + blend2);
                blend1 *= blend_scale;
                blend2 *= blend_scale;


                // do a linear blend
                vtx = vtx1 * blend1 + vtx2 * blend2;

                // store in coordinates
                Coords.SetItem(v * 3 + 0, vtx.x);
                Coords.SetItem(v * 3 + 1, vtx.y);
                Coords.SetItem(v * 3 + 2, vtx.z);
            }
        }
Пример #15
0
        //this function generates a fixed tube
        static spScene generate_simple_tube(ISimplygonSDK sdk)
        {
            const int vertex_count   = 16;
            const int triangle_count = 24;
            const int corner_count   = triangle_count * 3;

            //create a scene object
            spScene scene = sdk.CreateScene();

            // triangles x 3 indices ( or 3 corners )
            int[] corner_ids = { 0,   1,  4,
                                 4,   1,  5,
                                 5,   1,  6,
                                 1,   2,  6,
                                 6,   2,  3,
                                 6,   3,  7,
                                 7,   3,  0,
                                 7,   0,  4,


                                 4,   5,  8,
                                 8,   5,  9,
                                 9,   5, 10,
                                 5,   6, 10,
                                 10,  6,  7,
                                 10,  7, 11,
                                 11,  7,  4,
                                 11,  4,  8,


                                 8,   9, 12,
                                 12,  9, 13,
                                 13,  9, 14,
                                 9,  10, 14,
                                 14, 10, 11,
                                 14, 11, 15,
                                 15, 11,  8,
                                 15,  8, 12 };

            // vertices with values for the x, y and z coordinates.
            float[] vertex_coordinates = { 1.0f,   0.0f,  1.0f,
                                           1.0f,   0.0f, -1.0f,
                                           -1.0f,  0.0f, -1.0f,
                                           -1.0f,  0.0f,  1.0f,

                                           1.0f,  15.0f,  1.0f,
                                           1.0f,  15.0f, -1.0f,
                                           -1.0f, 15.0f, -1.0f,
                                           -1.0f, 15.0f,  1.0f,

                                           1.0f,  20.0f,  1.0f,
                                           1.0f,  20.0f, -1.0f,
                                           -1.0f, 20.0f, -1.0f,
                                           -1.0f, 20.0f,  1.0f,

                                           1.0f,  35.0f,  1.0f,
                                           1.0f,  35.0f, -1.0f,
                                           -1.0f, 35.0f, -1.0f,
                                           -1.0f, 35.0f, 1.0f };

            spGeometryData geom = sdk.CreateGeometryData();

            //add bone weights and ids to geometry data (per vertex)
            geom.AddBoneWeights(2);

            spRealArray coords     = geom.GetCoords();
            spRidArray  vertex_ids = geom.GetVertexIds();


            spRealArray BoneWeights = geom.GetBoneWeights();
            spRidArray  BoneIds     = geom.GetBoneIds();

            geom.SetVertexCount(vertex_count);
            geom.SetTriangleCount(triangle_count);

            //create the bone table
            spSceneBoneTable scn_bone_table = scene.GetBoneTable();

            //create an array to store bone ids
            spRidArray bone_ids = sdk.CreateRidArray();


            //create root bone for the scene
            spSceneBone root_bone = sdk.CreateSceneBone();

            root_bone.SetName("root_bone");

            //add the bone to the scene bone table and get a bone id
            int root_bone_id = scn_bone_table.AddBone(root_bone);


            //a
            bone_ids.AddItem(root_bone_id);

            spSceneBone parent_bone = root_bone;

            //create bones and populate the scene bone table
            for (uint bone_index = 1; bone_index < total_bone; bone_index++)
            {
                spSceneBone bone = sdk.CreateSceneBone();
                bone.SetName("ChildBone");
                spTransform3 boneTransform = sdk.CreateTransform3();

                //SET UP BONE IN BIND POSE
                //translate the child bone to its corrent position relative to the parent bone
                boneTransform.AddTransformation(bone.GetRelativeTransform());
                boneTransform.PreMultiply();
                boneTransform.AddTranslation(0.0f, 17.5f, 0.0f);

                //store the relatvice transform
                bone.GetRelativeTransform().DeepCopy(boneTransform.GetMatrix());

                //add bone to the scene bone table
                int bone_id = scn_bone_table.AddBone(bone);

                //link bone to parent bone
                parent_bone.AddChild(bone);
                bone_ids.AddItem(bone_id);

                parent_bone = bone;
            }


            float[] v = new float[3];
            for (int i = 0; i < vertex_count; ++i)
            {
                v[0] = vertex_coordinates[i * 3];
                v[1] = vertex_coordinates[i * 3 + 1];
                v[2] = vertex_coordinates[i * 3 + 2];
                coords.SetTuple(i, v);
                //real blend_val = real((rid(i)/rid(4)))/real(3);
                float blend_val = 0.5f;
                float blend1    = 1.0f - blend_val;
                float blend2    = blend_val;

                int bone_id_1 = 0;
                int bone_id_2 = 1;


                if (i < 4)
                {
                    bone_id_2 = -1;
                    blend2    = 0;
                    blend1    = 1;
                }
                else if (i > 11)
                {
                    bone_id_1 = -1;
                    blend2    = 1;
                    blend1    = 0;
                }

                blend1 *= blend1;
                blend2 *= blend2;

                //set the bone weights to perform skining
                BoneWeights.SetItem((i * 2) + 0, blend1);
                BoneWeights.SetItem((i * 2) + 1, blend2);

                //set the bone ids influencing the vertex.
                BoneIds.SetItem((i * 2) + 0, bone_id_1);
                BoneIds.SetItem((i * 2) + 1, bone_id_2);
            }

            for (int i = 0; i < corner_count; ++i)
            {
                vertex_ids.SetItem(i, corner_ids[i]);
            }

            //create a scene mesh
            spSceneMesh mesh = sdk.CreateSceneMesh();

            mesh.SetGeometry(geom);
            mesh.SetName("mesh");

            //get the root node of the scene and add the root_bone and mesh to the scene
            scene.GetRootNode().AddChild(mesh);
            scene.GetRootNode().AddChild(root_bone);
            return(scene);
        }
Пример #16
0
        static bool run_remeshing_for_lod(ISimplygonSDK sdk, int lod_index, uint merge_distance)
        {
            string userProfileDirectory     = System.Environment.GetEnvironmentVariable("USERPROFILE");
            string assetRoot                = userProfileDirectory + @"/Documents/SimplygonSDK/SourceCode/Assets/";
            string tempRoot                 = @"../../../../../temp/";
            string output_filename          = string.Format(tempRoot + "wall_lod{0}_merge{1}.obj", lod_index + 1, merge_distance);
            string output_material_filename = string.Format(tempRoot + "wall_lod{0}_merge{1}.mtl", lod_index + 1, merge_distance);
            string output_diffuse_filename  = string.Format(tempRoot + "wall_lod{0}_merge{1}_diffuse.png", lod_index + 1, merge_distance);
            string output_normals_filename  = string.Format(tempRoot + "wall_lod{0}_merge{1}_normals.png", lod_index + 1, merge_distance);


            // Import source geometry
            spGeometryData  geom;
            spMaterialTable materials;

            Console.WriteLine("Importing wavefront .obj file...\n");
            {
                // Run import
                spWavefrontImporter importer = sdk.CreateWavefrontImporter();
                importer.SetExtractGroups(false); // We only want one large geometry, no need to extract groups

                importer.SetImportFilePath(assetRoot + "wall.obj");
                if (!importer.RunImport())
                {
                    Console.WriteLine("Could not open input test file");
                    return(false);
                }

                // Get the only geometry and the materials
                geom      = importer.GetFirstGeometry();
                materials = importer.GetMaterials();
            }

            // Make a copy of the geometry, we need the original for texture casting later.
            // The remesher will replace the data of the geometry after it has processed it.
            spGeometryData red_geom = geom.NewCopy(true);

            // Create a Scene-object and a SceneMesh-object.
            // Place the red_geom into the SceneMesh, and then the SceneMesh as a child to the RootNode.
            spScene     scene = sdk.CreateScene();
            spSceneMesh mesh  = sdk.CreateSceneMesh();

            mesh.SetGeometry(red_geom);
            mesh.SetName(red_geom.GetName().GetText());
            scene.GetRootNode().AddChild(mesh);

            spMappingImage mapping_image;

            // Remesh it
            Console.WriteLine("Running remesher...\n");
            {
                spRemeshingProcessor remesher = sdk.CreateRemeshingProcessor();

                //  Set target on-screen size in pixels
                remesher.GetRemeshingSettings().SetOnScreenSize(lod_sizes[lod_index]);
                //  Set the on-screen merge distance in pixels. Holes smaller than this will be sealed
                //  and geometries closer to each other than this will be merged.
                remesher.GetRemeshingSettings().SetMergeDistance(merge_distance);
                //  Disable the cutting plane
                remesher.GetRemeshingSettings().SetUseGroundPlane(false);
                //	Set to generate mapping image for texture casting.
                remesher.GetMappingImageSettings().SetGenerateMappingImage(true);

                remesher.SetSceneRoot(scene.GetRootNode());
                remesher.RemeshGeometry();

                // Mapping image is needed later on for texture casting.
                mapping_image = remesher.GetMappingImage();
            }

            spSceneMesh topmesh = Utils.SimplygonCast <spSceneMesh>(scene.GetRootNode().GetChild(0), false);

            // Cast diffuse texture and normal map data into a new material
            //	Create new material table.
            spMaterialTable output_materials = sdk.CreateMaterialTable();
            //	Create new material for the table.
            spMaterial output_material = sdk.CreateMaterial();

            output_materials.AddMaterial(output_material);

            // Cast diffuse texture data
            {
                // Cast the data using a color caster
                spColorCaster cast = sdk.CreateColorCaster();
                cast.SetColorType(SimplygonSDK.SG_MATERIAL_CHANNEL_DIFFUSE);
                cast.SetSourceMaterials(materials);
                cast.SetMappingImage(mapping_image);             // The mapping image we got from the remeshing process.
                cast.SetOutputChannels(3);                       // RGB, 3 channels! (1 would be for grey scale, and 4 would be for RGBA.)
                cast.SetOutputChannelBitDepth(8);                // 8 bits per channel. So in this case we will have 24bit colors RGB.
                cast.SetDilation(10);                            // To avoid mip-map artifacts, the empty pixels on the map needs to be filled to a degree aswell.
                cast.SetOutputFilePath(output_diffuse_filename); // Where the texture map will be saved to file.
                cast.CastMaterials();                            // Fetch!

                // set the material properties
                // Set the diffuse multiplier for the texture. 1 means it will not differ from original texture,
                // For example: 0 would ignore a specified color and 2 would make a color twice as pronounced as the others.
                output_material.SetDiffuseRed(1);
                output_material.SetDiffuseGreen(1);
                output_material.SetDiffuseBlue(1);
                // Set material to point to created texture filename.
                output_material.SetTexture(SimplygonSDK.SG_MATERIAL_CHANNEL_DIFFUSE, output_diffuse_filename);
            }

            // cast normal map texture data
            {
                // cast the data using a color caster
                spNormalCaster cast = sdk.CreateNormalCaster();
                cast.SetSourceMaterials(materials);
                cast.SetMappingImage(mapping_image);
                cast.SetOutputChannels(3); // RGB, 3 channels! (But really the x, y and z values for the normal)
                cast.SetOutputChannelBitDepth(8);
                cast.SetDilation(10);
                cast.SetOutputFilePath(output_normals_filename);
                cast.CastMaterials();

                // Set material to point to created texture filename.
                output_material.SetTexture(SimplygonSDK.SG_MATERIAL_CHANNEL_NORMALS, output_normals_filename);
            }

            // export the remeshed geometry to an OBJ file
            Console.WriteLine("Exporting wavefront .obj file...\n");
            {
                spWavefrontExporter exporter = sdk.CreateWavefrontExporter();
                exporter.SetExportFilePath(output_filename);
                exporter.SetSingleGeometry(topmesh.GetGeometry());
                exporter.SetMaterials(output_materials);
                if (!exporter.RunExport())
                {
                    Console.WriteLine("Failed to write result");
                    return(false);
                }
            }
            return(true);
        }