///////////////////////////////////////////////////////////////////////////
//
// 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();
}