ImpostorExample.cpp

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