RemeshingExample.cpp

<< Click to Display Table of Contents >>

Navigation:  Simplygon 7.1 examples >

RemeshingExample.cpp

///////////////////////////////////////////////////////////////////////////
//
//  System:    Simplygon
//  File:      RemeshingExample.cpp
//  Language:  C++
//
//  Copyright (c) 2015 Donya Labs AB. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////
//
//  #Description# A remeshing example
//
//  This example shows how to use the remeshing API in Simplygon.
//
//  First, an asset with transparencies and internal geometry is remeshed
//  to demonstrate the remesher's ability to remove internal geometry and
//  project the detail of said geometry onto transparent surfaces in the output
//  textures.
//
//  Secondly, a brick wall asset is processed with a high merge distance,
//  demoing the remesher's ability to fill cavities in meshes and generate
//  nice, two-manifold outputs.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
//Function containing the actual Simplygon remeshing calls
void RunRemeshingProcessing(const std::string& readFrom, const std::string& writeTo, int onScreenSize, int mergeDistance, int textureSize);
//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[] )
    {
    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 console mesh processing, merge distance 0
    printf("Running console asset, low merge distance...\n");
    PrintProgressBar(0); //Initial progress bar
    RunRemeshingProcessing( assetPath + "Console/console.obj", "console_remeshed", 300, 0, 1024 );
    printf("\nDone.\n\n");
    // Run brick wall mesh processing, merge distance 16
    printf("Running brick wall asset, high merge distance to fuse the individual bricks...\n ");
    PrintProgressBar(0); //Initial progress bar
    RunRemeshingProcessing( assetPath + "wall.obj", "wall_remeshed", 300, 16, 1024 );
    printf("\nDone.\n\n");
    // Done!
    printf("All LODs complete, shutting down...");
    DeinitExample();
    return 0;
    }
//////////////////////////////////////////////////////////////////////
void RunRemeshingProcessing(const std::string& readFrom, const std::string& writeTo, int onScreenSize, int mergeDistance, int textureSize)
    {
    //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() )
        return;
    //Get the scene from the importer
    spScene scene = objReader->GetScene();
    spMaterialTable materials = scene->GetMaterialTable();
    //Create a remeshing processor
    spRemeshingProcessor remeshingProcessor = sg->CreateRemeshingProcessor();
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // SETTINGS
    remeshingProcessor->SetScene(scene); //Defines the scene on which to run the remesher
    //Geometry related settings:
    spRemeshingSettings remeshingSettings = remeshingProcessor->GetRemeshingSettings();
    //remeshingSettings->SetProcessSelectionSetID(-1); //Can be used to remesh only a specific selection set defined in the scene
    remeshingSettings->SetOnScreenSize( onScreenSize ); //The most important setting, defines the "resolution" of the remeshing, i.e. tri-count
    remeshingSettings->SetMergeDistance( mergeDistance ); //Defines how large gaps to fill in, in pixels. Relative to the setting above.
    remeshingSettings->SetSurfaceTransferMode( SG_SURFACETRANSFER_ACCURATE ); //This toggles between the two available surface mapping modes
    //remeshingSettings->SetTransferColors(false); //Sets if the remesher should transfer the old vertex colors to the new geometry, if availible
    //remeshingSettings->SetTransferNormals(false) //Sets if the remesher should transfer the old normals to the new geometry (generally a bad idea since the geometries don't match exactly)
    remeshingSettings->SetHardEdgeAngleInRadians(3.141f / 2.1f); //Sets the normal hard edge angle, used for normal recalc if TransferNormals is off. Here, slightly lower than 90 degrees.
    //remeshingSettings->SetUseCuttingPlanes( false ); //Defines whether to use cutting planes or not. Planes are defined in the scene object.
    //remeshingSettings->SetCuttingPlaneSelectionSetID( -1 ); //Sets a selection set for cutting planes, if you don't want to use all of the planes in the scene.
    remeshingSettings->SetUseEmptySpaceOverride( false ); //Overrides what the remesher considers to be "outside", so you can do interiors. Set coord with SetEmptySpaceOverride.
    //remeshingSettings->SetMaxTriangleSize( 10 ); //Can be used to limit the triangle size of the output mesh, producing more triangles. Can be useful for exotic use cases.
    //Parameterization and material casting related settings
    spMappingImageSettings mappingSettings = remeshingProcessor->GetMappingImageSettings();
    //mappingSettings->SetUseFullRetexturing( true ); //Kind of irrelevant for the remesher, since it always is a "full retexturing"
    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->SetGenerateTangents( true );//Set to generate new tangents and bitangents.
    mappingSettings->SetParameterizerMaxStretch( 0.8f ); //The higher the number, the fewer texture-borders. Also introduces more stretch, obviously.
    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.
    mappingSettings->SetWidth( textureSize );
    mappingSettings->SetHeight( textureSize );
    mappingSettings->SetMultisamplingLevel( 2 );
    mappingSettings->SetMaximumLayers(3); //IMPORTANT! This setting defines how many transparant layers the remesher will project onto the outermost surface of the remeshed geom,
    //and hence, how many layers will be in the generated mapping image
    //END SETTINGS
    ///////////////////////////////////////////////////////////////////////////////////////////////
    remeshingProcessor->AddObserver( &progressObserver , SG_EVENT_PROGRESS );
    //Run the remeshing
    remeshingProcessor->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 = remeshingProcessor->GetMappingImage();
    // Now, for each channel, we want to cast the input materials into a single output material, with one texture per channel.
    // Create new material for the table.
    spMaterial lodMaterial = sg->CreateMaterial();
    lodMaterial->SetName( "SimplygonBakedMaterial" );
    // Cast diffuse and specular texture data with a color caster
        {
        // Set the material properties
        lodMaterial->SetColor( SG_MATERIAL_CHANNEL_AMBIENT , 0 , 0 , 0 , 0 );
        lodMaterial->SetColor( SG_MATERIAL_CHANNEL_DIFFUSE , 1 , 1 , 1 , 1 );
        lodMaterial->SetColor( 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 = sg->CreateColorCaster();
        colorCaster->SetSourceMaterials( materials );
        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( SG_MATERIAL_CHANNEL_DIFFUSE );
        colorCaster->SetOutputChannels( 3 ); //RGB, 3 channels! (1 would be for grey scale, and 4 would be for RGBA.)
        colorCaster->SetOutputFilePath( outputDiffPath.c_str() ); //Where the texture map will be saved to file.
        colorCaster->CastMaterials(); //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( outputSpecPath.c_str() ); //Where the texture map will be saved to file.
        colorCaster->CastMaterials(); //Do the actual casting and write to texture.
        lodMaterial->SetTexture( SG_MATERIAL_CHANNEL_DIFFUSE , outputDiffPath.c_str() ); //Set material to point to the texture we cast to above
        lodMaterial->SetTexture( SG_MATERIAL_CHANNEL_SPECULAR , outputSpecPath.c_str() ); //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 = sg->CreateNormalCaster();
        normalCaster->SetSourceMaterials( materials );
        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->CastMaterials();
        // Set normal map of the created material to point to the combined normal map
        lodMaterial->SetTexture( 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
        // END CASTING
        ///////////////////////////////////////////////////////////////////////////////////////////////
        //Create an .obj exporter to save our result
        spWavefrontExporter objExporter = sg->CreateWavefrontExporter();
        // Generate the output filenames
        // Do the actual 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.
    }