Show / Hide Table of Contents
    ///////////////////////////////////////////////////////////////////////////
    //
    //  System:    Simplygon
    //  File:      ClippingGeometryExample.cpp
    //  Language:  C++
    //
    //  Copyright (c) 2019 Microsoft. All rights reserved.
    //
    ///////////////////////////////////////////////////////////////////////////
    //
    //  #Description#
    //
    //  This example demonstrates how to use the clipping geometry functionality
    //  of the remeshing processor to use a terrain to clip remeshed objects,
    //  saving texture space and avoiding two-sided geometry for assets like houses 
    //  (or, in this case, some nice teapot programmer art) that are placed so they are
    //  partially sticking out of the terrain.
    //  
    //  Note that the clipping geometry functionality can also be used to do boolean type operations
    //  with objects other than terrain.
    //
    //  This only works if the seal between the process geometry and the clipping geometry is
    //  completely watertight, with no leaks within the bounding volume of the process geometry.
    //  So, if your potential "buildings" do not in fact clip tightly into the ground
    //  plane, the clipping geometry will not work as intended.
    //  
    ///////////////////////////////////////////////////////////////////////////
    
    #include <ios> 
    #include "../Common/Example.h"
    
    //Basic vector define
    typedef real real3[3];
    
    //This function runs the remeshing processing for the given clipping mode
    void RunRemeshingWithClipping( bool UseTerrainClipping, const std::string& readFrom, const std::string& writeTo );
    
    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 without terrain clipping
            printf("Running remeshing without terrain clipping... ");
            RunRemeshingWithClipping(false, assetPath + "IslandOfTeapots/IslandOfTeapots.obj", "TeapotIslandUnclipped");
            printf("Done.\n");
    
            //Run remeshing with terrain clipping
            printf("Running remeshing with terrain clipping... ");
            RunRemeshingWithClipping(true, assetPath + "IslandOfTeapots/IslandOfTeapots.obj", "TeapotIslandClipped");
            printf("Done.\n");
    
            //Done!
            printf("\nAll LODs complete, shutting down...");
            DeinitExample();
        }
        catch (const std::exception& ex)
        {
            std::cerr << ex.what() << std::endl;
            return -1;
        }
    
        return 0;
        }
    
    
    void RunRemeshingWithClipping( bool UseClipping, const std::string& readFrom, const std::string& writeTo )
        {
        std::string exePath = GetExecutablePath();
    
        //Load input geometry from file
        spWavefrontImporter objReader = sg->CreateWavefrontImporter();
        objReader->SetExtractGroups( true );
        objReader->SetImportFilePath( readFrom.c_str() );
        if( !objReader->RunImport() )
            throw std::exception("Failed to load input file!");
    
        spScene scene = objReader->GetScene();
        spScene processScene = scene->NewCopy();
    
        spTextureTable lodTextures = sg->CreateTextureTable();
        lodTextures->Copy( scene->GetTextureTable() );
    
        //Selection set setup
        //We know the layout of the .obj file, it has the terrain in group 0 and teapots in the rest, which are put in sequential root children by the .obj reader
        spSelectionSet terrainSet = sg->CreateSelectionSet();
        spSelectionSet processSet = sg->CreateSelectionSet();
        int terrainSetId = processScene->GetSelectionSetTable()->AddSelectionSet( terrainSet );
        int processSetId = processScene->GetSelectionSetTable()->AddSelectionSet( processSet );
    
        terrainSet->AddItem( processScene->GetRootNode()->GetChild( 0 )->GetNodeGUID() );
        for( uint i = 1; i < processScene->GetRootNode()->GetChildCount(); ++i )
            {
            processSet->AddItem( processScene->GetRootNode()->GetChild( i )->GetNodeGUID() );
            }
    
        //Create processor and set scene
        spRemeshingProcessor remeshingProcessor = sg->CreateRemeshingProcessor();
        remeshingProcessor->SetScene( processScene );
    
        ///////////////////////////////////////////////////////////////////////////////////////////////
        // SETTINGS
    
        spRemeshingSettings remeshingSettings = remeshingProcessor->GetRemeshingSettings();
        remeshingSettings->SetOnScreenSize( 800 );
        remeshingSettings->SetMergeDistance( 6 ); //The asset has some gaps, try to merge
        remeshingSettings->SetSurfaceTransferMode( SG_SURFACETRANSFER_ACCURATE );
    
    
        //Now we have two selection sets with potential cutting planes, use mode switch to determine which set to use.
        if( UseClipping )
            {
            remeshingSettings->SetUseClippingGeometry( true );
            remeshingSettings->SetClippingGeometrySelectionSetID( terrainSetId );
    
            //The remesher will try to automatically find what side of the clipping geometry should be considered "outside" by using the triangle winding.
            //If this behaviour is not wanted, you can use the override coord to set what is considered empty space for the clipping geometry
            //real3 overrideCoord = { 0.f, 0.f, 0.f };
            //remeshingSettings->SetClippingGeometryEmptySpaceOverride( overrideCoord );
            }
    
    
        remeshingSettings->SetProcessSelectionSetID( processSetId ); //the -1 selection set contains all scene nodes, ie. all planes in the scene will be used.
    
        // The Image Mapping Settings, specifically needed for the texture baking we are doing later
        spMappingImageSettings mappingSettings = remeshingProcessor->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->SetParameterizerMaxStretch( 0.8f ); //The higher the number, the fewer texture-borders.
        mappingSettings->SetGutterSpace( 1 ); //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.
        mappingSettings->SetWidth( 512 );
        mappingSettings->SetHeight( 512 );
        mappingSettings->SetMultisamplingLevel( 2 );
    
        //END SETTINGS 
        ///////////////////////////////////////////////////////////////////////////////////////////////
    
    
        // Run the actual processing. After this, the set geometry will have been reduced according to the settings
        remeshingProcessor->RunProcessing();
    
    
        ///////////////////////////////////////////////////////////////////////////////////////////////
        // CASTING
    
        // Generate the output filenames
        std::string outputDiffFilename = exePath + writeTo + "_d.png";
        std::string outputSpecFilename = exePath + writeTo + "_s.png";
        std::string outputNormFilename = exePath + writeTo + "_n.png";
    
        // 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 = remeshingProcessor->GetMappingImage();
    
        // First, create a new material table.
        spMaterialTable lodMaterialTable = sg->CreateMaterialTable();
        // Create new material for the table.
        spMaterial lodMaterial = sg->CreateMaterial();
        lodMaterial->SetName( "SimplygonBakedMaterial" );
    
        // Cast diffuse and specular texture data with a color caster
        {
        // Cast the data using a color caster
        spColorCaster colorCaster = sg->CreateColorCaster();
        colorCaster->SetSourceMaterials( scene->GetMaterialTable() );
        colorCaster->SetSourceTextures( scene->GetTextureTable() );
        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( SG_MATERIAL_CHANNEL_DIFFUSE );
        colorCaster->SetOutputChannels( 3 ); //RGB, 3 channels! (1 would be for grey scale, and 4 would be for RGBA.)
        colorCaster->SetOutputFilePath( outputDiffFilename.c_str() ); //Where the texture map will be saved to file.
        colorCaster->RunProcessing(); //Do the actual casting and write to texture.
    
        colorCaster->SetColorType( SG_MATERIAL_CHANNEL_SPECULAR );
        colorCaster->SetOutputChannels( 4 ); //RGBA, 4 channels! Stores spec power in A
        colorCaster->SetOutputFilePath( outputSpecFilename.c_str() ); //Where the texture map will be saved to file.
        colorCaster->RunProcessing(); //Do the actual casting and write to texture.
    
        AddSimplygonTexture( lodMaterial, lodTextures, SG_MATERIAL_CHANNEL_DIFFUSE, outputDiffFilename.c_str() );
        AddSimplygonTexture( lodMaterial, lodTextures, SG_MATERIAL_CHANNEL_SPECULAR, outputSpecFilename.c_str() );
    
        //Set the spec power based on what it was in the original material, the process geom uses mat 1
        lodMaterial->SetShadingNetwork( SG_MATERIAL_CHANNEL_SHININESS, scene->GetMaterialTable()->GetMaterial( 1 )->GetShadingNetwork( SG_MATERIAL_CHANNEL_SHININESS ) );
        }
    
        // Cast normal map texture data with the normal caster. This also compensates for any geometric errors that have appeared in the reduction process.
        if( true )
            {
            // cast the data using a normal caster
            spNormalCaster normalCaster = sg->CreateNormalCaster();
            normalCaster->SetSourceMaterials( processScene->GetMaterialTable() );
            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( outputNormFilename.c_str() );
            normalCaster->SetFlipBackfacingNormals( true );
            normalCaster->SetGenerateTangentSpaceNormals( true );
            normalCaster->RunProcessing();
    
            // Set normal map of the created material to point to the combined normal map
            AddSimplygonTexture( lodMaterial, lodTextures, SG_MATERIAL_CHANNEL_NORMALS, outputNormFilename.c_str() );
            }
    
        // END CASTING
        ///////////////////////////////////////////////////////////////////////////////////////////////
    
    
    
        //Create an .obj exporter to save our result
        spWavefrontExporter objExporter = sg->CreateWavefrontExporter();
    
        // Generate the output filenames
        std::string outputGeomFilename = exePath + writeTo + ".obj";
        std::string outputGeomWithTerrainFilename = exePath + writeTo + "WithTerrain" + ".obj";
    
        //This add lod material, this scene now has both original and lod materials
        processScene->GetMaterialTable()->AddMaterial( lodMaterial );
        processScene->GetTextureTable()->Copy( lodTextures );
    
        //Set the material id of the new geometry to 2 to correspond to the material we just added to the table
        spSceneNode processGeomNode = processScene->GetNodeByGUID( processScene->GetSelectionSetTable()->GetSelectionSet( remeshingProcessor->GetResultSelectionSetId() )->GetItem( 0 ) );
        spGeometryData processGeometry = SafeCast<ISceneMesh>( processGeomNode )->GetGeometry();
        if(!processGeometry->GetMaterialIds())
            processGeometry->AddMaterialIds();
        for( uint t = 0; t < processGeometry->GetTriangleCount(); ++t )
            {
            processGeometry->GetMaterialIds()->SetItem( t, 2 );
            }
    
        //Add the terrain to the processed scene which now only contains the processed geometry
        processScene->GetRootNode()->AddChild( scene->GetRootNode()->GetChild( 0 ) );
    
        //Do the actual exporting, first with all geometry
        objExporter->SetExportFilePath( outputGeomWithTerrainFilename.c_str() );
        objExporter->SetScene( processScene ); //This is the geometry we set as the processing geom of the reducer
        objExporter->RunExport();
    
        //Export a version only containing the processed geometry
        objExporter->SetMaterialFilePath( NULL ); //This just resets the auto-set material path, so that it'll be set by the ExportFilePath
        objExporter->SetExportFilePath( outputGeomFilename.c_str() );
        objExporter->SetScene( processScene ); //This is the geometry we set as the processing geom of the reducer
        objExporter->SetSelectionSet( remeshingProcessor->GetResultSelectionSetId() );
        objExporter->RunExport();
    
        //Done! LOD and material created.
        }
    
    Back to top Terms of Use | Privacy and cookies | Trademarks | Copyright © 2019 Microsoft