Search Results for

    Show / Hide Table of Contents
    ///////////////////////////////////////////////////////////////////////////
    //
    //  System:    Simplygon
    //  File:      Impostor.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# 
    //
    //  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[] )
        {
        try
        {
            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();
        }
        catch (const std::exception& ex)
        {
            std::cerr << ex.what() << std::endl;
            return -1;
        }
    
        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() )
            throw std::exception("Failed to load input file!");
    
        // Get the scene from the importer
        // Will only contain a single geom
        spScene scene = objReader->GetScene();
        spMaterialTable originalMaterialTable = scene->GetMaterialTable();
        spTextureTable originalTextureTable = scene->GetTextureTable();
    
        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 );
    
        //This makes the texcoords generated in the range 0.01 to 0.99 instead of 0 to 1, fixing some potential sub-pixel 
        //rendering errors near the edge of the impostor if you're using tiled textures in your engine.
        impostorSettings->SetTexCoordPadding( 0.01f );
    
        //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();
        spTextureTable lodTextureTable = sg->CreateTextureTable();
    
        // 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 )
            {
            // Cast the data using a color caster
            spColorCaster colorCaster = sg->CreateColorCaster();
            colorCaster->SetSourceMaterials( originalMaterialTable );
            colorCaster->SetSourceTextures( originalTextureTable );
            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->RunProcessing(); //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->RunProcessing(); //Do the actual casting and write to texture.
    
            AddSimplygonTexture( lodMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_DIFFUSE, "combinedDiffuseMap.png" );
            AddSimplygonTexture( lodMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_SPECULAR, "combinedSpecularMap.png" );
            }
    
        // Cast normal map texture data with the normal caster.
        if( true )
            {
            spNormalCaster normalCaster = sg->CreateNormalCaster();
            normalCaster->SetSourceMaterials( originalMaterialTable );
            normalCaster->SetSourceTextures( originalTextureTable );
            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->RunProcessing();
    
            // Set normal map of the created material to point to the combined normal map
            AddSimplygonTexture( lodMaterial, lodTextureTable, 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->RunProcessing();
    
            // Set opacity map of the created material to point to the combined normal map
            AddSimplygonTexture( lodMaterial, lodTextureTable, SG_MATERIAL_CHANNEL_OPACITY, "combinedOpacityMap.png" );
            }
        // END CASTING
        ///////////////////////////////////////////////////////////////////////////////////////////////
    
        spScene ImpostorScene = sg->CreateScene();
        ImpostorScene->GetRootNode()->CreateChildMesh( lodGeometry );
        ImpostorScene->GetMaterialTable()->Copy( lodMaterialTable );
        ImpostorScene->GetTextureTable()->Copy( lodTextureTable );
    
        //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.
        }
    
    In This Article
    Back to top Terms of Use | Privacy and cookies | Trademarks | Copyright © 2019 Microsoft