///////////////////////////////////////////////////////////////////////////
//
//  System:    Simplygon
//  File:      DataCreationPreferencesExample.cpp
//  Language:  C++
//
//  Copyright (c) 2019 Microsoft. All rights reserved.
//
//  This is private property, and it is illegal to copy or distribute in
//  any form, without written authorization by the copyright owner(s).
//
///////////////////////////////////////////////////////////////////////////
//
//  #Description#
//
//  A wavefront file is opened, the scene is then processed in 3 different ways, 
//  one for each data creation preference setting.
//
//  The generated LOD and the original geometry are combined into a single geometry
//  with a shared vertex set.
//
//  All reductions are run with identical settings except for the data creation
//  preferences flag:
//
//  1) SG_DATACREATIONPREFERENCES_ONLY_USE_ORIGINAL_DATA
//  When the reducer is set to only use original data, the new geometry is 
//  constructed entirely from vertices present in the original geometry.
//
//  The combined geometry will have the same vertex count as the original.
//
//  2) SG_DATACREATIONPREFERENCES_PREFER_ORIGINAL_DATA
//  When the flag is set to prefer original vertices, mesh repair will be allowed
//  to run on the asset and new combinations of vertex data will be allowed to
//  be created, but most of the vertices will correspond to ones found in the original.
//  
//  The combined geometry will probably have a larger vertex count than the original.
//
//  3) SG_DATACREATIONPREFERENCES_PREFER_OPTIMIZED_RESULT
//  The third setting allows optimization of all vertex data, and produces an 
//  entirely new set of vertex coords. The vertices are re-positioned to preserve the 
//  original silhouette and volume of the mesh.
//
//  The combined geometry will likely not share vertices between the LOD and original so
//  the combined vertex count is increased.
//
//
//  It should be noted that the most restrictive option original vertices should only 
//  be used when the original model is reasonably clean, as all the mesh repair features 
//  of Simplygon will be switched off. New normals will not be calculated, vertices
//  will not be welded and T-junctions will not be fixed.
//
//  The results of the three processes are saved as individual .obj files
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
void RunExampleReduction( const std::string& readFrom, const std::string& writeTo, uint DataCreationPref );
int main( int argc, char* argv[] )
    {
    try
    {
        InitExample();
        // Set global variable. Using Orthonormal method for calculating tangentspace.
        sg->SetGlobalSetting("DefaultTBNType", SG_TANGENTSPACEMETHOD_ORTHONORMAL);
        std::string assetPath = GetAssetPath();
        // Run the example reduction, use only original vertices
        printf("Running reduction, only using original vertices\n");
        RunExampleReduction(assetPath + "wall.obj", "original_and_lod_original_verts", SG_DATACREATIONPREFERENCES_ONLY_USE_ORIGINAL_DATA);
        printf("Done, output geometry saved to file.\n\n");
        // Run the example reduction, prefer original vertices but still allow new data when required
        printf("Running reduction, allowing creation of new vertices when needed\n");
        RunExampleReduction(assetPath + "wall.obj", "original_and_lod_new_verts", SG_DATACREATIONPREFERENCES_PREFER_ORIGINAL_DATA);
        printf("Done, output geometry saved to file.\n\n");
        // Run the example reduction, optimize all geometry data as required
        printf("Running reduction, allowing modification of all vertex data\n");
        RunExampleReduction(assetPath + "wall.obj", "original_and_lod_optimized_verts", SG_DATACREATIONPREFERENCES_PREFER_OPTIMIZED_RESULT);
        printf("Done, output geometry saved to file.\n");
        DeinitExample();
    }
    catch (const std::exception& ex)
    {
        std::cerr << ex.what() << std::endl;
        return -1;
    }
    return 0;
    }
void RunExampleReduction( const std::string& readFrom, const std::string& writeTo, uint DataCreationPref )
    {
    // Run reduction with default settings to 10% of the original triangle count. 
    // The data restrictions flag is set from the main function and determines
    // if the reducer will be allowed to create new vertices or construct the LOD
    // using only the vertices available in the original mesh. The decimated 
    // geometry is appended to the packed original, so that the geometry now contains
    // all unique vertices from both the original geometry and the generated LOD. 
    // If the data creation flag is set to only use original data no new
    // vertices should be created when the LOD is appended to the original mesh.
    // The LOD triangles described with the combined vertex set is then added to
    // the packed geometry, creating a geometry that contains both the original
    // model and the generated LOD using the same vertices.
    std::string output_geometry_filename = GetExecutablePath() + writeTo + ".obj";
    // Load from file
    spWavefrontImporter objReader = sg->CreateWavefrontImporter();
    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();
    spSelectionSet selectMesh = scene->GetSelectionSetTable()->GetSelectionSet( scene->SelectNodes( "ISceneMesh" ) );
    spGeometryData currentGeom = Cast<ISceneMesh>( scene->GetNodeByGUID( selectMesh->GetItem( 0 ) ) )->GetGeometry();
    // Create a packed copy of the original geometry for appending to later
    spPackedGeometryData packedGeometry = currentGeom->NewPackedCopy();
    // To compare the combined vertex counts vs the original, we output the original unpacked -> packed geom
    if( true )
        {
        spScene outputOriginalPackedScene = sg->CreateScene();
        outputOriginalPackedScene->GetRootNode()->CreateChildMesh( packedGeometry->NewUnpackedCopy() );
        outputOriginalPackedScene->GetMaterialTable()->Copy( scene->GetMaterialTable() );
        spWavefrontExporter objexp = sg->CreateWavefrontExporter();
        std::string output_original_unpacked_packed_filename = GetExecutablePath() + "original_unpacked_packed.obj";
        objexp->SetExportFilePath( output_original_unpacked_packed_filename.c_str() );
        objexp->SetScene( outputOriginalPackedScene );
        objexp->RunExport();
        }
    // Create the reduction-processor.
    spReductionProcessor red = sg->CreateReductionProcessor();
    red->SetScene( scene );
    // Set the Reduction Settings.
    spReductionSettings reduction_settings = red->GetReductionSettings();
    // Will reduce to 1/10 of the original trianglecount.
    reduction_settings->SetTriangleRatio( 0.1f );
    // Set the data creation preferences flag
    reduction_settings->SetDataCreationPreferences( DataCreationPref );
    // Run the reduction
    red->RunProcessing();
    // Append processed geometry to original geometry and check number of new vertices
    // Unique vertices from currentGeom will be added to the packed geometry
    // newVertexList will contain a description of the triangles of currentGeom using the 
    // original vertex IDs where available. 
    // newUniqueVertexCount should increase with the level of freedom allowed for the reducer
    // Note that the geometries need to contain the same set of fields, otherwise the append will fail
    spRidArray newVertexIdsList = sg->CreateRidArray();
    int newUniqueVertexCount = packedGeometry->AppendPackedGeometry( currentGeom, newVertexIdsList, false );
    printf( "%i new vertices generated.\n", newUniqueVertexCount );
    // Save original triangle count
    rid originalTriangleCount = packedGeometry->GetTriangleCount();
    // Add the amount of triangles from currentgeom
    packedGeometry->AddTriangles( currentGeom->GetTriangleCount() );
    // Append LOD vertex list to the end of the original geometries vertex list 
    spRidData newVertexIdsListData = sg->CreateRidData();
    newVertexIdsList->GetData( newVertexIdsListData );
    packedGeometry->GetVertexIds()->SetDataRange( originalTriangleCount * 3, newVertexIdsListData.Data(), newVertexIdsList->GetItemCount() );
    // Append LOD triangle data to the end of the original geometries triangle data
    packedGeometry->GetTriangles()->CopyRange( currentGeom->GetTriangles(), originalTriangleCount, 0, currentGeom->GetTriangleCount() );
    // Now, the triangles in packedGeometry describes the original model from 0 <= id < originalTriangleCount, 
    // and the generated LOD following that.
    spScene outputCombinedGeometriesScene = sg->CreateScene();
    outputCombinedGeometriesScene->GetRootNode()->CreateChildMesh( packedGeometry->NewUnpackedCopy() );
    outputCombinedGeometriesScene->GetMaterialTable()->Copy( scene->GetMaterialTable() );
    // Now store combined original and LOD geometry to file
    spWavefrontExporter objexp = sg->CreateWavefrontExporter();
    objexp->SetExportFilePath( output_geometry_filename.c_str() );
    bool outputCombinedScene = true;
    if( outputCombinedScene )
        objexp->SetScene( outputCombinedGeometriesScene ); //Output original and LOD sharing vertex list
    else
        objexp->SetScene( scene ); //Only output the LOD
    objexp->RunExport();
    }