WavefrontExample.cpp

<< Click to Display Table of Contents >>

Navigation:  Simplygon 7.1 examples >

WavefrontExample.cpp

///////////////////////////////////////////////////////////////////////////
//
//  System:    Simplygon
//  File:      WavefrontExample.cpp
//  Language:  C++
//
//  Copyright (c) 2015 Donya Labs AB. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////
//
//  #Description#
//
//  This example shows how to manually import/export geometry
//  data to/from Simplygon. The example implements a wavefront importer/
//  exporter, and also a per-corner data indexer/packer, to remove duplicate
//  data items when outputting to a wavefront file.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
#include "../Common/OBJReader.h"
#include "../Common/IndexArrayHandler.h"
void RunExample();
spGeometryData ReadObj( const std::string& path );
void WriteObj( IGeometryData *data , const std::string& path );
void RunReductionProcessing( spScene scene, float triangle_ratio );
/////////////////////////////////////////////////////////////////////
// Main function
int main( int argc , char* argv[] )
    {
    InitExample();
    // Run the example code
    RunExample();
    DeinitExample();
    return 0;
    }
void RunExample()
    {
    // Read a geometry from file
    std::string assetPath = GetAssetPath();
    spGeometryData geom = ReadObj( (assetPath + "testmodel.obj").c_str() );
    // Put the geometry in a scene
    spScene scene = sg->CreateScene();
    scene->GetRootNode()->CreateChildMesh(geom);
    // Reduce to 50% triangles.
    // This is done in-place, geometry contains the
    // reduced data on return.
    RunReductionProcessing( scene, 0.50f );
    // Output the geometry to file
    std::string outputObjPath = GetExecutablePath() + "output50.obj";
    WriteObj( geom , outputObjPath.c_str() );
    // Reduce again to 25% triangles (i.e. 12.5% of original geometry)
    RunReductionProcessing( scene, 0.25f );
    // Output the geometry to file
    outputObjPath = GetExecutablePath() + _T("output25.obj");
    WriteObj( geom , outputObjPath.c_str() );
    }
// This function reads an .obj file and returns a geometry object
spGeometryData ReadObj( const std::string& path )
    {
    ObjRead rd;
    // Create the Geometry. All geometrydata will be loaded into this object
    spGeometryData geom = sg->CreateGeometryData();
    // Array with vertex-coordinates. Will contain 3 real-values for each vertex in the geometry.
    spRealArray coords = geom->GetCoords();
    // Array with triangle-data. Will contain 3 ids for each corner of each triangle, so the triangles know what vertices to use.
    spRidArray vertex_ids = geom->GetVertexIds();
    // Data from the file, stores on per-vertex basis.
    spRealArray tmpnormals = sg->CreateRealArray();
    spRealArray tmptexcoords = sg->CreateRealArray();
    // Arrays that data will be stored in, before loading it to the GeometryData
    // since the stored data need to be in per-triangle basis when loaded to the GeometryData.
    spRealArray cornernormals = sg->CreateRealArray();
    spRealArray cornertexcoords = sg->CreateRealArray();
    // Regular arrays that the obj-reader will store the data into.
    int vertex_count;
    int triangle_count;
    float *pCoords;
    int *pTriangles;
    float *pNormals = NULL;
    float *pTexcoords = NULL;
    // Attempt to load an obj-file.
    if(    !rd.Load(
        path.c_str(),
        vertex_count,
        triangle_count,
        &pCoords,
        &pTexcoords,
        &pNormals,
        &pTriangles,
        false ) )
        {
        fprintf(stderr,"Error: Failed to load input file\n");
        exit(-1);
        return spGeometryData();
        }
    // NOTE: must set tuplesize before tuplecount, or the arraysize will be wrong
    // Normals contains 3 real-values for each tuple
    tmpnormals->SetTupleSize( 3 );
    // Textures contains 3 real-values for each tuple
    tmptexcoords->SetTupleSize( 2 );
    // Data from the file is stored on per-vertex basis, so size on these arrays must be same size
    tmpnormals->SetTupleCount( vertex_count );
    tmptexcoords->SetTupleCount( vertex_count );
    // Set vertex- and triangle-counts for the Geometry.
    geom->SetVertexCount(vertex_count);
    geom->SetTriangleCount(triangle_count);
    // Arrays that normals and texcoords will be loaded into, per triangle-corner basis
    spRealArray normals;
    spRealArray texcoords;
    if(pNormals)
        {
        geom->AddNormals();
        normals = geom->GetNormals();
        }
    if(pTexcoords)
        {
        geom->AddTexCoords( 0 );
        texcoords = geom->GetTexCoords( 0 );       
        }
    // add vertex-coordinates to the Geometry, and also, if they exist, normal and texture-data to the
    // arrays that will be used to convert to data that fits the Geometry.
    for( int v=0; v<vertex_count; ++v )
        {
        coords->SetTuple( v , &pCoords[v*3] );
        if(pNormals)
            tmpnormals->SetTuple( v, &pNormals[v*3] );
        if(pTexcoords)
            tmptexcoords->SetTuple( v, &pTexcoords[v*2] );
        }
    // add triangles to the Geometry. (The ids for what vertices each triangle use)
    for( int t=0; t<triangle_count; ++t )
        {
        vertex_ids->SetItem( t*3+0 , pTriangles[t*3+0] );
        vertex_ids->SetItem( t*3+1 , pTriangles[t*3+1] );
        vertex_ids->SetItem( t*3+2 , pTriangles[t*3+2] );
        }
    // Load data into the Geometry, first convert it from per-vertex basis to per-triangle-corner basis,
    // then copy the data to the arrays belonging to the Geometry.
    if(pNormals)
        {
        CreateIndependentArray( tmpnormals, vertex_ids, cornernormals );
        normals->DeepCopy( cornernormals );
        }
    if(pTexcoords)
        {
        CreateIndependentArray( tmptexcoords, vertex_ids, cornertexcoords );
        texcoords->DeepCopy( cornertexcoords );
        }
    // make sure the bounding box for the geometry is up-to-date
    geom->CalculateExtents(true);
    // run the geometry through the validator to make sure all is setup correctly
    spGeometryValidator validator = sg->CreateGeometryValidator();
    bool valid = validator->ValidateGeometry( geom );
    // if not valid, report the geometry, and exit
    if( !valid )
        {
        rid error_value = validator->GetErrorValue();
        rstring error_string = validator->GetErrorString();
        fprintf( stderr , "Validation failed:\n\tError: %s\n", error_string.GetText() );
        exit(-1);
        }
    if( pNormals ) delete [] pNormals;
    if( pTexcoords ) delete [] pTexcoords;
    if( pCoords ) delete [] pCoords;
    if( pTriangles ) delete [] pTriangles;
    return geom;
    }
// This function runs the Simplygon reduction processing to a specified triangle count
void RunReductionProcessing( spScene scene, float triangle_ratio )
    {
    // Create the reduction processor. Set the geometry that is to be processed
    spReductionProcessor red = sg->CreateReductionProcessor();
    red->SetScene(scene);
    ///////////////////////////////////////////////////
    //
    // Set the Repair Settings. Current settings will mean that all visual gaps will remain in the geometry and thus
    // hinder the reduction on geometries that contains gaps, holes and tjunctions.
    spRepairSettings repair_settings = red->GetRepairSettings();
    // Only vertices that actually share the same position will be welded together
    repair_settings->SetWeldDist( 0.0f );
    // Only t-junctions with no actual visual distance will be fixed.
    repair_settings->SetTjuncDist( 0.0f );
    ///////////////////////////////////////////////////
    //
    // Set the Reduction Settings.
    spReductionSettings reduction_settings = red->GetReductionSettings();
    // Reduce to the triangle count
    reduction_settings->SetTriangleRatio( triangle_ratio );
    ///////////////////////////////////////////////////
    //
    // Set the Normal Calculation Settings.
    spNormalCalculationSettings normal_settings = red->GetNormalCalculationSettings();
    // Will completely recalculate the normals.
    normal_settings->SetReplaceNormals( true );
    normal_settings->SetHardEdgeAngleInRadians( 3.14159f * 90.f / 180.0f );   
    // Run the process
    red->RunProcessing();   
    }
void WriteObj( IGeometryData *data , const std::string& path )
    {
    FILE *fp = fopen(path.c_str(),"wt");
    if(!fp)
        {
        printf("Error: Cannot write file\n");
        return;
        }
    unsigned int vc = data->GetVertexCount();
    unsigned int tc = data->GetTriangleCount();
    unsigned int i;
    spRealArray vdata = data->GetCoords();
    spRidArray tdata = data->GetVertexIds();
    spRealArray texdata = data->GetTexCoords( 0 );
    spRealArray normaldata = data->GetNormals();
    spRidArray texcoordsindexarray = sg->CreateRidArray();
    spRidArray normalsindexarray = sg->CreateRidArray();
    spRealArray texcoordsvaluearray = sg->CreateRealArray();
    spRealArray normalsvaluearray = sg->CreateRealArray();
    // The data in texdata and normals data is stored on per triangle-corner basis,
    // this data will be stored in unique values i the values arrays, and with indexes for the triangle-corners
    // in the index arrays.
    if(!texdata.IsNull())
        CreateIndexArray( texdata, texcoordsvaluearray, texcoordsindexarray );
    if(!normaldata.IsNull())
        CreateIndexArray( normaldata, normalsvaluearray, normalsindexarray );
    // write vertices
    fprintf(fp,"\n# %i Vertices \n\n", int(vc));
    for( i=0; i<vc; ++i )
        {
        fprintf(fp,"v %f %f %f\n",
            vdata->GetItem(i*3+0),
            vdata->GetItem(i*3+1),
            vdata->GetItem(i*3+2)
            );
        }
    // write texcoords
    if( !texdata.IsNull() )
        {
        fprintf(fp,"\n# %i Texcoords \n\n", int(texcoordsvaluearray->GetTupleCount()));
        for( i=0; i<texcoordsvaluearray->GetTupleCount(); ++i)
            {
            fprintf(fp,"vt %f %f\n",
                texcoordsvaluearray->GetItem(i*2+0),
                texcoordsvaluearray->GetItem(i*2+1)
                );
            }
        }
    // write normals
    if( !normaldata.IsNull() )
        {
        fprintf(fp,"\n# %i Normals \n\n", int(normalsvaluearray->GetTupleCount()));
        for( i=0; i<normalsvaluearray->GetTupleCount(); ++i)
            {
            fprintf(fp,"vn %f %f %f\n",
                normalsvaluearray->GetItem(i*3+0),
                normalsvaluearray->GetItem(i*3+1),
                normalsvaluearray->GetItem(i*3+2)
                );
            }
        }
    // Write the triangles
    unsigned int v_i;
    unsigned int vt_i;
    unsigned int vn_i;
    fprintf(fp,"\n# %i Triangles \n\n", int(tc));
    for( unsigned int t=0; t < tc; ++t)
        {
        fprintf(fp,"f ");
        for( i=0; i<3; ++i)
            {
            v_i = tdata->GetItem(t*3+i);
            if( !texdata.IsNull() )
                {
                vt_i = texcoordsindexarray->GetItem(t*3+i);
                if( !normaldata.IsNull() )
                    {
                    vn_i = normalsindexarray->GetItem(t*3+i);
                    // all indiced
                    fprintf(fp,"%d/%d/%d ",v_i+1,vt_i+1,vn_i+1);
                    }
                else
                    {
                    // vertices and texture coordinates indiced
                    fprintf(fp,"%d/%d ",v_i+1,vt_i+1);
                    }
                }
            else
                {
                if( !normaldata.IsNull() )
                    {
                    vn_i = normalsindexarray->GetItem(t*3+i);
                    // vertices and normals indiced
                    fprintf(fp,"%d//%d ",v_i+1,vn_i+1);
                    }
                else
                    {
                    // only vertices indiced
                    fprintf(fp,"%d ",v_i+1);
                    }
                }
            }
        fprintf(fp,"\n");
        }
    fclose(fp);
    }