///////////////////////////////////////////////////////////////////////////
//
//  System:    Simplygon
//  File:      FoliageExample.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# 
//
//  RunExampleBillboards:
//
//  Vegetation type objects are replaced by a set of static billboards that 
//  maintains the visual perception of depth and volume regardless of orientation.
//
//  The foliage processor divides the vegetation asset into a trunk and a foliage 
//  part. The trunk geometry retains the original materials and have its triangle 
//  count reduced. The foliage part is replaced by a set of billboards that 
//  receives a newly generated material.
//
//  There are a number of settings that determine the complexity and quality of 
//  the optimized vegetation such as the permitted distance (maximum deviation) 
//  between the original leaves and their projection on a billboard. If the 
//  maximum deviation is set to a low value (same as having a high on screen pixel 
//  size), more billboards would need to be created in order to satisfy the low 
//  error tolerance. However, the user can also set a maximum number of billboards 
//  allowed to make sure there's a certain geometry complexity limit. If the maximum 
//  billboard count is reached but there are still leaves further away from any 
//  billboard than than the permitted projection distance the leaves will be 
//  projected to the most suitable billboard.
//
//
//  RunExampleFlipbook:
//  
//  The flip book generator replaces the scene with a single quad that when rendered 
//  should always be oriented towards the viewer around the up-vector. The quad 
//  should be textured with the original scene rendered from a similar view.
//  
//  The flip book generator renders the input scene from a number of views and 
//  concatenates the images into an atlas (per channel). Every view is rendered at 
//  the same size making indexing a view from the finished atlas predictable. The 
//  generator also  creates a single square quad positioned at the center of the 
//  scene scaled to fit an orthographic projection of the scene from any angle.
//  
//  The user decides the amount of views to render. The output texture is always a
//  square therefore the number of views is also a square number. When rendering 
//  the quad, select the view from the texture atlas that closest corresponds to the
//  current direction.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
void RunExampleBillboards(const std::string& readFrom, const std::string& writeTo);
void RunExampleFlipbook(const std::string& readFrom, const std::string& writeTo);
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();
        RunExampleBillboards(assetPath + "Tree/Tree.obj", "TreeBillboards");
        RunExampleFlipbook(assetPath + "Tree/Tree.obj", "TreeFlipbook");
        DeinitExample();
    }
    catch (const std::exception& ex)
    {
        std::cerr << ex.what() << std::endl;
        return -1;
    }
    return 0;
    }
void RunExampleBillboards(const std::string& readFrom, const std::string& writeTo)
    {
    std::string exePath = GetExecutablePath();
    std::string outputGeometryFilename = exePath + writeTo + ".obj";
    //Load object from file
    spWavefrontImporter objReader = sg->CreateWavefrontImporter();
    objReader->SetUseAlphaAsOpacity(true);
    objReader->SetImportFilePath(readFrom.c_str());
    if (!objReader->RunImport())
        throw std::exception("Failed to load input file!");
    //Get the scene
    spScene originalScene = objReader->GetScene();
    spScene lodScene = sg->CreateScene();
    lodScene->DeepCopy(originalScene);
    spFoliageProcessor foliageProcessor = sg->CreateFoliageProcessor();
    foliageProcessor->SetScene(lodScene);
    foliageProcessor->GetMappingImageSettings()->SetWidth(1024);
    foliageProcessor->GetMappingImageSettings()->SetHeight(1024);
    foliageProcessor->GetMappingImageSettings()->SetMaximumLayers(10);
    foliageProcessor->GetMappingImageSettings()->SetMultisamplingLevel(2);
    spFoliageSettings foliageSettings = foliageProcessor->GetFoliageSettings();
    foliageSettings->SetBillboardDensity(0.2f);
    foliageSettings->SetBillboardTrunkReductionRatio(0.5f);
    foliageSettings->SetFoliageType(SG_FOLIAGETYPE_BILLBOARDS);
    foliageSettings->SetBillboardFavorVerticalPlanes(false);
    foliageSettings->SetBillboardMaxPlaneCount(20);
    foliageSettings->SetBillboardSubdividePlanes(true);
    foliageSettings->SetBillboardAllowConvexPolygons(true);
    foliageSettings->SetBillboardTwoSided(true);
    foliageSettings->SetBillboardUseVisibilityWeights(true);
    foliageSettings->SetSeparateFoliageTriangleRatio(0.5f);
    foliageProcessor->RunProcessing();
    spMappingImage foliageMappingImage = foliageProcessor->GetMappingImage();
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // cast materials
    spMaterialTable originalMaterialTable = originalScene->GetMaterialTable();
    spTextureTable originalTextures = originalScene->GetTextureTable();
    // Now, for each channel, we want to cast the input materials into a single output material, with one texture per channel. 
    // First, clear the lod materials (as they are copies from the original initially)
    spMaterialTable lodMaterialTable = lodScene->GetMaterialTable();
    spTextureTable lodTextureTable = lodScene->GetTextureTable();
    // Get the newly created foliage material 
    spMaterial foliageMaterial = lodMaterialTable->GetMaterial(foliageProcessor->GetFoliageMaterialID());
    // Cast diffuse and specular texture data with a color caster
    {
    // Cast the data using a color caster
    spColorCaster colorCaster = sg->CreateColorCaster();
    colorCaster->SetSourceMaterials(originalMaterialTable);
    colorCaster->SetSourceTextures(originalTextures);
    colorCaster->SetMappingImage(foliageMappingImage); //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->SetBakeOpacityInAlpha(false);
    colorCaster->SetUseMultisampling(true);
    colorCaster->SetFillMode(SG_ATLASFILLMODE_INTERPOLATE);
    colorCaster->SetOutputSRGB(true);
    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("BillboardsDiffuseMap.png"); //Where the texture map will be saved to file.
    colorCaster->RunProcessing(); //Do the actual casting and write to texture.
    AddSimplygonTexture(foliageMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_DIFFUSE, "BillboardsDiffuseMap.png");
    }
    // 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(originalMaterialTable);
    normalCaster->SetSourceTextures(originalTextures);
    normalCaster->SetMappingImage(foliageMappingImage);
    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("BillboardsNormalMap.png");
    normalCaster->SetFlipBackfacingNormals(false);
    normalCaster->SetGenerateTangentSpaceNormals(true);
    normalCaster->RunProcessing();
    // Set normal map of the created material to point to the combined normal map
    AddSimplygonTexture(foliageMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_NORMALS, "BillboardsNormalMap.png");
    }
    // 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
    spOpacityCaster opacityCaster = sg->CreateOpacityCaster();
    opacityCaster->SetSourceMaterials(originalMaterialTable);
    opacityCaster->SetSourceTextures(originalTextures);
    opacityCaster->SetMappingImage(foliageMappingImage);
    opacityCaster->SetOutputChannels(1); // RGB, 3 channels! (But really the x, y and z values for the normal)
    opacityCaster->SetOutputChannelBitDepth(8);
    opacityCaster->SetOutputFilePath("BillboardsOpacityMap.png");
    opacityCaster->SetDilation(0);
    opacityCaster->SetFillMode(SG_ATLASFILLMODE_NONE); //Important to set to NONE so that the opacity just covers the geometry projected on the billboard
    opacityCaster->RunProcessing();
    // Set normal map of the created material to point to the combined normal map
    AddSimplygonTexture(foliageMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_OPACITY, "BillboardsOpacityMap.png");
    }
    // END CASTING
    ///////////////////////////////////////////////////////////////////////////////////////////////
    //Create an .obj exporter to save our result
    spWavefrontExporter objExporter = sg->CreateWavefrontExporter();
    // Generate the output filenames
    std::string outputGeomFilename = GetExecutablePath() + writeTo + ".obj";
    // Do the actual exporting
    objExporter->SetExportFilePath(outputGeomFilename.c_str());
    objExporter->SetScene(lodScene); //This is the geometry we set as the processing geom of the reducer, retaining the materials in the original scene
    objExporter->RunExport();
    }
void RunExampleFlipbook(const std::string& readFrom, const std::string& writeTo)
    {
    std::string exePath = GetExecutablePath();
    std::string outputGeometryFilename = exePath + writeTo + ".obj";
    //Load object from file
    spWavefrontImporter objReader = sg->CreateWavefrontImporter();
    objReader->SetUseAlphaAsOpacity(true);
    objReader->SetImportFilePath(readFrom.c_str());
    if (!objReader->RunImport())
        throw std::exception("Failed to load input file!");
    //Get the scene
    spScene originalScene = objReader->GetScene();
    spScene lodScene = sg->CreateScene();
    lodScene->DeepCopy(originalScene);
    spFoliageProcessor foliageProcessor = sg->CreateFoliageProcessor();
    foliageProcessor->SetScene(lodScene);
    foliageProcessor->GetMappingImageSettings()->SetWidth(256); //The dimension is set per view (so with 9 = 3x3 views we get 3*256 = 768 pixels
    foliageProcessor->GetMappingImageSettings()->SetHeight(256);
    foliageProcessor->GetMappingImageSettings()->SetMaximumLayers(10);
    foliageProcessor->GetMappingImageSettings()->SetMultisamplingLevel(2);
    // Set the Repair Settings.
    spFoliageSettings foliageSettings = foliageProcessor->GetFoliageSettings();
    foliageSettings->SetFoliageType(SG_FOLIAGETYPE_FLIPBOOK);
    foliageSettings->SetFlipbookNumberOfViews(9);
    foliageSettings->SetFlipbookVerticalAngleInRadians(0.0f);
    foliageProcessor->RunProcessing();
    spMappingImage foliageMappingImage = foliageProcessor->GetMappingImage();
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // cast materials
    spMaterialTable originalMaterialTable = originalScene->GetMaterialTable();
    spTextureTable originalTextures = originalScene->GetTextureTable();
    // Now, for each channel, we want to cast the input materials into a single output material, with one texture per channel. 
    // First, clear the lod materials (as they are copies from the original initially)
    spMaterialTable lodMaterialTable = lodScene->GetMaterialTable();
    spTextureTable lodTextureTable = lodScene->GetTextureTable();
    lodMaterialTable->Clear();
    lodTextureTable->Clear();
    // Get the newly created foliage material 
    spMaterial foliageMaterial = sg->CreateMaterial();
    foliageMaterial->SetName("FlipbookMaterial");
    lodMaterialTable->AddMaterial(foliageMaterial);
    // Cast diffuse and specular texture data with a color caster
    {
    // Cast the data using a color caster
    spColorCaster colorCaster = sg->CreateColorCaster();
    colorCaster->SetSourceMaterials(originalMaterialTable);
    colorCaster->SetSourceTextures(originalTextures);
    colorCaster->SetMappingImage(foliageMappingImage); //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->SetBakeOpacityInAlpha(false);
    colorCaster->SetUseMultisampling(true);
    colorCaster->SetFillMode(SG_ATLASFILLMODE_INTERPOLATE);
    colorCaster->SetOutputSRGB(true);
    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("FlipbookDiffuseMap.png"); //Where the texture map will be saved to file.
    colorCaster->RunProcessing(); //Do the actual casting and write to texture.
    AddSimplygonTexture(foliageMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_DIFFUSE, "FlipbookDiffuseMap.png");
    }
    // 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(originalMaterialTable);
    normalCaster->SetSourceTextures(originalTextures);
    normalCaster->SetMappingImage(foliageMappingImage);
    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("FlipbookNormalMap.png");
    normalCaster->SetFlipBackfacingNormals(false);
    normalCaster->SetGenerateTangentSpaceNormals(false);
    normalCaster->RunProcessing();
    // Set normal map of the created material to point to the combined normal map
    AddSimplygonTexture(foliageMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_NORMALS, "FlipbookNormalMap.png");
    }
    // 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
    spOpacityCaster opacityCaster = sg->CreateOpacityCaster();
    opacityCaster->SetSourceMaterials(originalMaterialTable);
    opacityCaster->SetSourceTextures(originalTextures);
    opacityCaster->SetMappingImage(foliageMappingImage);
    opacityCaster->SetOutputChannels(1); // RGB, 3 channels! (But really the x, y and z values for the normal)
    opacityCaster->SetOutputChannelBitDepth(8);
    opacityCaster->SetOutputFilePath("FlipbookOpacityMap.png");
    opacityCaster->SetDilation(0);
    opacityCaster->SetFillMode(SG_ATLASFILLMODE_NONE); //Important to set to NONE so that the opacity just covers the geometry projected on the billboard
    opacityCaster->RunProcessing();
    // Set normal map of the created material to point to the combined normal map
    AddSimplygonTexture(foliageMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_OPACITY, "FlipbookOpacityMap.png");
    }
    // END CASTING
    ///////////////////////////////////////////////////////////////////////////////////////////////
    //Store the finished processed scene
    spWavefrontExporter objexp = sg->CreateWavefrontExporter();
    objexp->SetExportFilePath(outputGeometryFilename.c_str());
    objexp->SetScene(lodScene);
    objexp->RunExport();
    }