Show / Hide Table of Contents
    ///////////////////////////////////////////////////////////////////////////
    //
    //  System:    Simplygon
    //  File:      RemeshingV2Example.cpp
    //  Language:  C++
    //
    //  Copyright (c) 2019 Microsoft. All rights reserved.
    //
    ///////////////////////////////////////////////////////////////////////////
    //
    //  #Description# A remeshing example
    //
    //  This example shows how to use the updated remeshing processor and its features
    //
    //  The naming of the processor is meant to indicate that this processor will 
    //  eventually replace the current RemeshingProcessor when it is feature complete, 
    //  and is now supplied as a preview to show the new features.
    //
    //  What differentiates the new remeshing processor from the old one is 
    //  currently massively increased maximum OnScreenSize for generating very
    //  high quality meshes and intelligent hole filling and cavity removal.
    //
    //  This example will demonstrate the functionality of the hole filling.
    //
    ///////////////////////////////////////////////////////////////////////////
    
    #include "../Common/Example.h"
    
    //Functions containing the Simplygon remeshing calls
    void RunRemeshingProcessing(const std::string& readFrom, const std::string& writeTo, int onScreenSize, int mergeDistance, float mergeBoldness, int textureSize, bool bakeMaterials = true);
    
    //Progress observer
    class ProgressObserver : public robserver
        {
        public:
            virtual void Execute(
                IObject* subject,
                rid EventId,
                void* EventParameterBlock,
                unsigned int EventParameterBlockSize)
                {
                // only care for progress events
                if (EventId == SG_EVENT_PROGRESS)
                    {
                    // get the progress in percent
                    int val = *((int*)EventParameterBlock);
                    // tell the process to continue
                    // this is required by the progress event
                    *((int*)EventParameterBlock) = 1;
                    // output the progress update
                    PrintProgressBar(val);
                    }
                }
        } progressObserver;
    
    /////////////////////////////////////////////////////////////////////
    // Main function with startup and shutdown code
    
    int main(int argc, char* argv[])
        {
        try
            {
            InitExample();
    
            // Before any specific processing starts, set global variables. 
            // Using Orthonormal method for calculating tangentspace.
            sg->SetGlobalSetting("DefaultTBNType", SG_TANGENTSPACEMETHOD_ORTHONORMAL);
    
            std::string assetPath = GetAssetPath();
    
            // Run remeshing processing on a character model with removed components and 
            // large gaps in its armor. The merge settings used will fill the holes
            // in a smooth way and create a solid two-manifold geometry while 
            // using the old remesher or using 0 merge distance will generate
            // a twosided mesh, wasting geometry and texture space.
            printf("Running broken SimplygonMan...\n");
            PrintProgressBar(0); //Initial progress bar
            RunRemeshingProcessing(assetPath + "SimplygonMan/BrokenSimplygonManTorsoHalf.obj", "TorsoHalfHoleFilled", 500, 100, 0.7f, 1024);
            printf("\nDone.\n\n");
    
            // Run multiple remeshings on a test mesh with increasing mergeboldness
            // without casting materials to demonstrate the effect on the output geometry
            printf("Running sphere hole asset with increasing mergeboldness...\n");
            PrintProgressBar(0); 
            RunRemeshingProcessing(assetPath + "spherehole.obj", "sphereholefilled_nomerge", 200, 0, 0, 1024, false);
            PrintProgressBar(0); 
            RunRemeshingProcessing(assetPath + "spherehole.obj", "sphereholefilled_b" + std::to_string(0.0), 200, 100, 0.0, 1024, false);
            PrintProgressBar(0);
            RunRemeshingProcessing(assetPath + "spherehole.obj", "sphereholefilled_b" + std::to_string(0.5), 200, 100, 0.5, 1024, false);
            printf("\nDone.\n\n");
            // Done!
            printf("All LODs complete, shutting down...");
            DeinitExample();
            }
        catch (const std::exception & ex)
            {
            std::cerr << ex.what() << std::endl;
            return -1;
            }
    
        return 0;
        }
    
    //////////////////////////////////////////////////////////////////////
    
    
    //Takes a .obj path and remeshes the asset with the parameters below
    void RunRemeshingProcessing(const std::string& readFrom, const std::string& writeTo, int onScreenSize, int mergeDistance, float mergeBoldness, int textureSize, bool bakeMaterials)
        {
        //Setup output paths
        std::string outputGeomPath = GetExecutablePath() + writeTo + ".obj";
        std::string outputDiffPath = GetExecutablePath() + writeTo + "_d.png";
        std::string outputSpecPath = GetExecutablePath() + writeTo + "_s.png";
        std::string outputNormPath = GetExecutablePath() + writeTo + "_n.png";
    
        //Load input geometry from file
        spWavefrontImporter objReader = sg->CreateWavefrontImporter();
        objReader->SetExtractGroups(false); //This makes the .obj reader import into a single geometry object instead of multiple
        objReader->SetImportFilePath(readFrom.c_str());
        if (!objReader->RunImport())
            throw std::exception("Failed to load input file!");
    
        //Get the scene from the importer
        spScene scene = objReader->GetScene();
        spMaterialTable materials = scene->GetMaterialTable();
        spTextureTable textures = scene->GetTextureTable();
    
        //Create a remeshing processor
        spRemeshingProcessorV2 remeshingProcessor = sg->CreateRemeshingProcessorV2();
        remeshingProcessor->SetScene(scene);
    
        ///////////////////////////////////////////////////////////////////////////////////////////////
        // SETTINGS
        spRemeshingSettingsV2 remeshingSettings = remeshingProcessor->GetRemeshingSettingsV2();
    
        //The most important setting, defines the "resolution" of the remeshing, i.e. tri-count. 
        //Works the same way as the setting in the old remesher
        remeshingSettings->SetOnScreenSize(onScreenSize);
    
        //Merge distance and merge boldness are used to control the holefilling and cavity removal.
        //These settings exist in the old remesher, but what they actually do is quite different.
        //In the previous remesher, these settings controlled what was essentially a morphologic close operation,
        //often leaving the results "blobby", undefined, and lacking detail.
        //In V2, they control an intelligent localized cavity removal algorithm that selectively fills
        //holes and cavities with setting-dependent opening sizes and depths, and will not merge things that 
        //should not be merged.
    
        //Defines how large the openings of the holes and cavities the processor will attempt to fill will be, in pixels. 
        remeshingSettings->SetMergeDistance(mergeDistance);
        //Defines how deep cavities that should be filled are allowed to be. 
        //0 means almost nothing will be filled, 1 will fill more or less everything obviously concave.
        remeshingSettings->SetMergeBoldness(mergeBoldness);
    
        //These settings are optional algorithm switches which can be used to cut down processing time
        //slightly at the cost of output consistency. Controlled with SG_MODE_ACCURATE and SG_MODE_FAST flags.
        remeshingSettings->SetMeshGenerationMode(SG_MODE_ACCURATE);
        remeshingSettings->SetSurfaceTransferMode(SG_MODE_ACCURATE); //SG_MODE_ACCURATE == SG_SURFACETRANSFER_ACCURATE from old processor
    
        remeshingSettings->SetHardEdgeAngleInRadians(3.141f / 4.1f);
        spMappingImageSettings mappingSettings = remeshingProcessor->GetMappingImageSettings();
        mappingSettings->SetGenerateMappingImage(bakeMaterials);
        mappingSettings->SetGenerateTexCoords(bakeMaterials);
        mappingSettings->SetGenerateTangents(bakeMaterials);
        mappingSettings->SetParameterizerMaxStretch(0.5f);
        mappingSettings->SetGutterSpace(2);
        mappingSettings->SetTexCoordLevel(0);
        mappingSettings->SetWidth(textureSize);
        mappingSettings->SetHeight(textureSize);
        mappingSettings->SetMultisamplingLevel(2);
        mappingSettings->SetMaximumLayers(3);
        //END SETTINGS 
        ///////////////////////////////////////////////////////////////////////////////////////////////
    
        remeshingProcessor->AddObserver(&progressObserver, SG_EVENT_PROGRESS);
    
        //Run the remeshing
        remeshingProcessor->RunProcessing();
    
        ///////////////////////////////////////////////////////////////////////////////////////////////
        // CASTING
        spMappingImage mappingImage = remeshingProcessor->GetMappingImage();
    
        spMaterial lodMaterial = sg->CreateMaterial();
        lodMaterial->SetName("SimplygonBakedMaterial");
    
        spTextureTable lodTextures = sg->CreateTextureTable();
    
        // Cast diffuse and specular texture data with a color caster
        if (bakeMaterials)
            {
            spColorCaster colorCaster = sg->CreateColorCaster();
            colorCaster->SetSourceMaterials(materials);
            colorCaster->SetSourceTextures(textures);
            colorCaster->SetMappingImage(mappingImage);
            colorCaster->SetOutputChannelBitDepth(8);
            colorCaster->SetDilation(10);
    
            colorCaster->SetColorType(SG_MATERIAL_CHANNEL_DIFFUSE);
            colorCaster->SetOutputChannels(3);
            colorCaster->SetOutputFilePath(outputDiffPath.c_str());
            colorCaster->RunProcessing();
    
            colorCaster->SetColorType(SG_MATERIAL_CHANNEL_SPECULAR);
            colorCaster->SetOutputChannels(4); //RGBA, 4 channels, stores spec power in A
            colorCaster->SetOutputFilePath(outputSpecPath.c_str());
            colorCaster->RunProcessing();
    
            AddSimplygonTexture(lodMaterial, lodTextures, SG_MATERIAL_CHANNEL_DIFFUSE, outputDiffPath.c_str());
            AddSimplygonTexture(lodMaterial, lodTextures, SG_MATERIAL_CHANNEL_SPECULAR, outputSpecPath.c_str());
            }
    
        // Cast normal map texture data with the normal caster. This also compensates for any geometric errors that have appeared in the reduction process.
        if (bakeMaterials)
            {
            spNormalCaster normalCaster = sg->CreateNormalCaster();
            normalCaster->SetSourceMaterials(materials);
            normalCaster->SetSourceTextures(textures);
            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(outputNormPath.c_str());
            normalCaster->SetFlipBackfacingNormals(false);
            normalCaster->SetGenerateTangentSpaceNormals(true);
            normalCaster->RunProcessing();
    
            AddSimplygonTexture(lodMaterial, lodTextures, SG_MATERIAL_CHANNEL_NORMALS, outputNormPath.c_str());
            }
    
        //Now, we can clear the original material table in the scene, and replace its contents with our new lodMaterial
        materials->Clear();
        materials->AddMaterial(lodMaterial); //This will be added at matId 0, which will match the remeshed geometry
    
        //Also, replace the texture list from the original with the new one
        textures->Copy(lodTextures);
        // END CASTING
        ///////////////////////////////////////////////////////////////////////////////////////////////
    
        //Create an .obj exporter to save our result
        spWavefrontExporter objExporter = sg->CreateWavefrontExporter();
    
        // Do the exporting
        objExporter->SetExportFilePath(outputGeomPath.c_str());
        objExporter->SetScene(scene); //scene now contains the remeshed geometry and the material table we modified above
        objExporter->RunExport();
    
        //Done! LOD and material created.
        }
    
    Back to top Terms of Use | Privacy and cookies | Trademarks | Copyright © 2019 Microsoft