<< Click to Display Table of Contents >> Navigation: Simplygon 7.1 examples > ImpostorExample.cpp |
///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: ImpostorExample.cpp
// Language: C++
//
// Copyright (c) 2015 Donya Labs AB. 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#
//
// In this example, we use the ImpostorProcessor to generate a two-triangle
// billboard impostor geometry of an input geometry from a specific viewing
// angle, and cast textures and normals from the original geometry to the
// impostor.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
//Observer for monitoring progress
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;
void RunImpostorGenerationWithTextureCasting(const std::string& readFrom, const std::string& writeTo);
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 HQ reduction example, reducing a single geometry to a single LOD
printf("Running impostor generation and texture casting... \n");
PrintProgressBar(0); //Initial progress bar
RunImpostorGenerationWithTextureCasting( assetPath + "wall.obj", "wall_impostor.obj" );
printf("\nDone.\n\n");
// Done!
printf("LOD complete, shutting down...");
DeinitExample();
return 0;
}
void RunImpostorGenerationWithTextureCasting(const std::string& readFrom, const std::string& writeTo)
{
// 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
// Will only contain a single geom
spScene scene = objReader->GetScene();
spMaterialTable originalMaterialTable = scene->GetMaterialTable();
spSelectionSet selectSceneMeshes = scene->GetSelectionSetTable()->GetSelectionSet(scene->SelectNodes("ISceneMesh"));
spSceneMesh sceneMesh = Cast<ISceneMesh>(scene->GetNodeByGUID(selectSceneMeshes->GetItem(0)));
spGeometryData originalGeom = sceneMesh->GetGeometry();
// Make sure geometry has normals and tangents, since it will cast tangent space normals wrong if either are not present and an input normal map exists.
spNormalRepairer normRep = sg->CreateNormalRepairer();
normRep->SetGeometry(originalGeom);
normRep->RunProcessing();
spTangentCalculator tanCalc = sg->CreateTangentCalculator();
tanCalc->CalculateTangents(originalGeom);
// Create the impostor processor, and set the geometry
spImpostorProcessor imp = sg->CreateImpostorProcessor();
imp->SetGeometry(originalGeom);
///////////////////////////////////////////////////////////////////////////////////////////////
// SETTINGS
spImpostorSettings impostorSettings = imp->GetImpostorSettings();
//The viewdir determines from what direction the billboard will be visible.
//If making a billboard replacement for something like a wall segment, this vector should point
//directly towards the wall, perfectly orthogonal to the wall plane.
//The impostor processor always uses positive Y as up, meaning that the quads are aligned with the Y axis.
real viewDir[3] = {0.f,0.f,-1.0f};
impostorSettings->SetViewDirection(viewDir);
//This setting controls the extents of the generated billboard geometry. If true, the impostor geometry will EXACTLY
//encapsulate the original geometry from the set viewing angle. If false, the billboards generated are always square,
//with sides as wide as the diameter of the object.
//It is worth noting that if this is true, the pixel density in X and Y will in most cases become non-uniform,
//since we do not yet know the aspect ratio of the impostor when setting texture size.
//You can fix this by calculating the aspect ratio beforehand with the function below.
impostorSettings->SetUseTightFitting(true);
//The depth offset is the offset in the impostor-space z direction of the resulting plane.
//0 puts the impostor in the center of the input geometry.
//1 puts the impostor at the "front" of the input geometry
//-1 puts the impostor at the "back" of the input geometry.
impostorSettings->SetTightFittingDepthOffset(1.f);
//Once geometry and settings are set, you can calculate the aspect ratio for your textures.
real aspect = imp->CalculateImpostorAspectRatio();
int xDim = int(aspect * 512.f);
int yDim = int(1.f * 512.f);
//These settings determine the dimensions of the output mapping image, and hence baked textures.
//MappingImageSettings contains a lot of further settings, but none of those are relevant to the impostor processor.
spMappingImageSettings mappingSettings = imp->GetMappingImageSettings();
mappingSettings->SetWidth(xDim);
mappingSettings->SetHeight(yDim);
mappingSettings->SetMultisamplingLevel(2);
//END SETTINGS
///////////////////////////////////////////////////////////////////////////////////////////////
// Add observer
imp->AddObserver(&progressObserver, SG_EVENT_PROGRESS);
// Run the actual processing.
// In contrast to the reduction and remeshing processors, the input geometry is never replaced by the lod.
// Instead, the lod can be specifically fetched using imp->GetImpostorGeometry();
imp->RunProcessing();
// The processing is done. Fetch the generated impostor, and the output mapping image
spGeometryData lodGeometry = imp->GetImpostorGeometry();
spMappingImage mappingImage = imp->GetMappingImage();
///////////////////////////////////////////////////////////////////////////////////////////////
// CASTING
// Now, we want to cast the materials and normals from our original geometry onto the billboard geometry.
// Note the OPACITY casting, as it might be important depending on the type of impostor you want
// First, create a new material table.
spMaterialTable lodMaterialTable = sg->CreateMaterialTable();
// Create new material for the table.
spMaterial lodMaterial = sg->CreateMaterial();
lodMaterial->SetName( "SimplygonBakedMaterial" );
lodMaterialTable->AddMaterial( lodMaterial );
// Cast diffuse and specular texture data with a color caster
if(true)
{
// 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_OPACITY , 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( originalMaterialTable );
colorCaster->SetDestMaterial( lodMaterial ); //This modulates the cast color with the base colors set for the dest material above.
colorCaster->SetMappingImage( mappingImage ); //The mapping image we got from the impostor processor.
colorCaster->SetOutputChannelBitDepth( 8 ); //8 bits per channel. So in this case we will have 24bit colors RGB.
colorCaster->SetBakeOpacityInAlpha(false);
colorCaster->SetDilation( 10 ); //To avoid mip-map artifacts, the empty pixels on the map needs to be filled to a degree as well.
colorCaster->SetFillMode(SG_ATLASFILLMODE_INTERPOLATE); //This determines what to do with the empty space left on the atlas after dilation
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( "combinedDiffuseMap.png" ); //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( "combinedSpecularMap.png" ); //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 , "combinedDiffuseMap.png" ); //Set material to point to the texture we cast to above
lodMaterial->SetTexture( SG_MATERIAL_CHANNEL_SPECULAR , "combinedSpecularMap.png" ); //Set material to point to the texture we cast to above
lodMaterial->SetDiffuseTextureHasAlpha(false);
}
// Cast normal map texture data with the normal caster.
if(true)
{
spNormalCaster normalCaster = sg->CreateNormalCaster();
normalCaster->SetSourceMaterials( originalMaterialTable );
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( "combinedNormalMap.png" );
normalCaster->SetFlipBackfacingNormals( true );
normalCaster->SetGenerateTangentSpaceNormals( true );
normalCaster->SetFillMode(SG_ATLASFILLMODE_INTERPOLATE);
normalCaster->CastMaterials();
// Set normal map of the created material to point to the combined normal map
lodMaterial->SetTexture( SG_MATERIAL_CHANNEL_NORMALS , "combinedNormalMap.png" );
lodMaterial->SetUseTangentSpaceNormals(true);
}
// Cast an opacity map
// This step is important if you want an impostor with alpha transparency that works correctly.
// There are some specific settings that needs to be used, otherwise the empty space will be filled
// by the dilation and fill steps, rather than contain transparency.
// The same settings will also apply if you bake the alpha into the diffuse map, rather than into a specific map.
if(true)
{
// cast the data using an opacity caster
spOpacityCaster opacityCaster = sg->CreateOpacityCaster();
opacityCaster->SetSourceMaterials( originalMaterialTable );
opacityCaster->SetMappingImage( mappingImage );
opacityCaster->SetOutputChannels( 1 ); // L, 1 channel
opacityCaster->SetOutputChannelBitDepth( 8 );
opacityCaster->SetDilation( 0 ); // If this is not 0, the opaque pixels will dilate into the "empty area"
opacityCaster->SetOutputFilePath( "combinedOpacityMap.png" );
opacityCaster->SetFillMode(SG_ATLASFILLMODE_NONE);// IMPORTANT! If this is not set to NONE, it will fill the areas that should be transparant with potentially opaque values.
opacityCaster->CastMaterials();
// Set opacity map of the created material to point to the combined normal map
lodMaterial->SetTexture( SG_MATERIAL_CHANNEL_OPACITY , "combinedOpacityMap.png" );
}
// END CASTING
///////////////////////////////////////////////////////////////////////////////////////////////
spScene ImpostorScene = sg->CreateScene();
ImpostorScene->GetRootNode()->CreateChildMesh( lodGeometry );
ImpostorScene->GetMaterialTable()->Copy(lodMaterialTable);
//Create an .obj exporter to save our result
spWavefrontExporter objExporter = sg->CreateWavefrontExporter();
std::string outputGeomFilename = GetExecutablePath() + writeTo;
// Do the actual exporting
objExporter->SetExportFilePath( outputGeomFilename.c_str() );
objExporter->SetScene( ImpostorScene );
objExporter->RunExport();
//Done! LOD and material created.
}