<< 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.
}