///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: OcclusionMeshExample.cpp
// Language: C++
//
// Copyright (c) 2019 Microsoft. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////
//
// #Description# An occlusion mesh example
//
// This example shows how to use the occlusion mesh processor.
//
// The processor is used to generate three meshes, one occluder (smaller than the original asset),
// one occludee (larger than the original asset), and one which is the same size as the original.
// The processor constructs the output meshes based purely on the silhouette of the input, meaning
// that the resulting meshes are efficient proxies constructed to occlude, and be occluded by,
// other meshes in occlusion culling rendering and shadow passes. Not suitable for self-shadowing,
// since they are not depth-conservative, only silhouette-conservative.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
void RunOcclusionProcessing( const std::string& readFrom, const std::string& writeTo, int onScreenSize, int error, int mode );
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[] )
{
try
{
InitExample();
std::string assetPath = GetAssetPath();
// Create occluder
printf("Running wall asset, generating occluder...\n");
PrintProgressBar(0); //Initial progress bar
RunOcclusionProcessing(assetPath + "wall.obj", "wall_occluder", 100, 10, SG_OCCLUSIONMODE_OCCLUDER);
printf("\nDone.\n\n");
// Create occludee
printf("Running wall asset, generating occludee...\n");
PrintProgressBar(0); //Initial progress bar
RunOcclusionProcessing(assetPath + "wall.obj", "wall_occludee", 100, 10, SG_OCCLUSIONMODE_OCCLUDEE);
printf("\nDone.\n\n");
// Create offscreen shadow mesh, neither larger nor smaller than the original.
printf("Running wall asset, generating shadow mesh...\n");
PrintProgressBar(0); //Initial progress bar
RunOcclusionProcessing(assetPath + "wall.obj", "wall_standard", 100, 10, SG_OCCLUSIONMODE_STANDARD);
printf("\nDone.\n\n");
// Done!
printf("All LODs complete, shutting down...");
DeinitExample();
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return -1;
}
return 0;
}
//////////////////////////////////////////////////////////////////////
void RunOcclusionProcessing( const std::string& readFrom, const std::string& writeTo, int onScreenSize, int error, int mode )
{
//Setup output paths
std::string outputGeomPath = GetExecutablePath() + writeTo + ".obj";
//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() )
throw std::exception("Failed to load input file!");
//Get the scene from the importer
spScene scene = objReader->GetScene();
scene->GetMaterialTable()->Clear(); //No materials required for occlusion meshing
//Create a processor
spOcclusionMeshProcessor omProcessor = sg->CreateOcclusionMeshProcessor();
omProcessor->SetScene( scene );
//Set settings
spOcclusionMeshSettings omSettings = omProcessor->GetOcclusionMeshSettings();
omSettings->SetOnScreenSize( onScreenSize ); //Determines triangle count / mesh resolution. Generally, we recommend sizes around 100 pixels, currently the algorithm scales badly at larger sizes.
omSettings->SetOcclusionMode( mode ); //This determines if the output will be smaller or larger than the input.
omSettings->SetOnScreenErrorTolerance( error ); //This determines the accuracy of the silhouette reconstruction. 1 is the most accurate, larger is faster. Measured in pixels in worst-case setting, so you can easily get away with much larger values.
//omSettings->SetTransferSkinning( true );
//omSettings->SetInvertOutputMesh(true); //This inverts the winding, making frontfaces backfaces. Good for guaranteed conservative occluders.
omProcessor->AddObserver( &progressObserver, SG_EVENT_PROGRESS ); //Add progress observer
//Run the processing
omProcessor->RunProcessing();
//Create an .obj exporter to save our result
spWavefrontExporter objExporter = sg->CreateWavefrontExporter();
// 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!
}