<< 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);
}