XSI Batch Converter?

Is there a batch conversion file or tool anywhere for the XSi to Mesh converter?

4,021 views 3 replies
Reply #1 Top

If I could remember the syntax for the program itself I could write you one in a minute, but alas I haven't had to convert anything in months.

Reply #2 Top

Blarg.

Reply #3 Top

Quoting GoaFan77, reply 1
If I could remember the syntax for the program itself I could write you one in a minute, but alas I haven't had to convert anything in months.
End of GoaFan77's quote

I kept this from a thread some time ago now, with the hope of 1 day maybe making a convertdata to work for the newer .xsi layout. But I suck at code and don't know what I am doing. Some time, when I get time, I'd like to learn it more, It would certainly be handy for plugins and the like.

EDit; Ha! smiley face's are just how happy this code is, ..... or just the web page.

// ConvertXSI.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <map>

#include "Engine/DataStructures/AlgorithmLibrary.h"
#include "Engine/Render/Mesh/MeshSystem.h"
#include "Engine/String/StringConvert.h"

#include "XSIParser.h"
#include "dotXSITemplate.h"
#include "dotXSIParam.h"
#include "dotXSIParams.h"
#include "SIBCUtil.h"
#include "Scene.h"
#include "Model.h"
#include "Mesh.h"
#include "MaterialLibrary.h"
#include "Shape.h"
#include "Shape_35.h"
#include "Material.h"
#include "Texture2D.h"
#include "VariantParameter.h"
#include "CustomPSet.h"
#include "ShaderInstanceData.h"
#include "ShaderConnectionPoint.h"
#include "PolygonList.h"
#include "Transform.h"
#include "UVCoordArray.h"
#include "XSIMaterial.h"
#include "XSIShader.h"

struct SceneInfo
{
    Iron::Mesh::Data meshData;
    Iron::DynamicVector<Iron::StdFixedString, true> materialNames;

    SceneInfo() : materialNames(makeFileLocation()) {}
};

Iron::UVCoord ConvertUVCoord(const CSIBCVector2D& uvCoordIn)
{
    Iron::UVCoord uvCoordOut;
    uvCoordOut.m_U = uvCoordIn.m_fX;
    uvCoordOut.m_V = 1.f - uvCoordIn.m_fY;
    return uvCoordOut;
}

Iron::Vector3 ConvertCoordSystem(const CSIBCVector3D& vectorIn)
{
    Iron::Vector3 vectorOut;
    vectorOut.x = vectorIn.m_fX;
    vectorOut.y = vectorIn.m_fY;
    vectorOut.z = -vectorIn.m_fZ; // invert z (see directx docs for converting between left and right handed coordinate systems)
    
    // rotate 180 degrees around y axis.
    vectorOut.Transform(Iron::Matrix33().BuildRotationY(Iron::MathConst::Pi()));

    return vectorOut;
}

Iron::Mesh::Vertex DumpMeshVertex(CSLShape_35* shape, CSLPolygonList* polygonList, int index)
{
    Iron::Mesh::Vertex meshVertex;

    int vertexIndex = polygonList->GetVertexIndicesPtr()[index];
    CSIBCVector3D position = (*shape->GetVertexList())[vertexIndex];
    meshVertex.m_Position = ConvertCoordSystem(position);

    int normalIndex = polygonList->GetNormalIndicesPtr()[index];
    CSIBCVector3D normal = (*shape->GetNormalList())[normalIndex];
    meshVertex.m_Normal = ConvertCoordSystem(normal);

    int colorIndex = -1;
    if (polygonList->GetColorIndicesPtr() != NULL)
    {
        colorIndex = polygonList->GetColorIndicesPtr()[index];
        CSIBCColorf color = (*shape->GetColorList())[colorIndex];
        meshVertex.m_Tangent = ConvertCoordSystem(CSIBCVector3D(color.m_fR, color.m_fG, color.m_fB));
    }

    if (polygonList->GetUVArrayCount() > 0)
    {
        int uvCoordIndex = polygonList->GetUVIndicesPtr(0)[index];
        CSLUVCoordArray* uvCoordArray = shape->UVCoordArrays()[0];
        CSIBCVector2D uvCoord = (*uvCoordArray->GetUVCoordList())[uvCoordIndex];
        meshVertex.m_TextureCoord0 = ConvertUVCoord(uvCoord);
    }

    if (polygonList->GetUVArrayCount() > 1)
    {
        int uvCoordIndex = polygonList->GetUVIndicesPtr(1)[index];
        CSLUVCoordArray* uvCoordArray = shape->UVCoordArrays()[1];
        CSIBCVector2D uvCoord = (*uvCoordArray->GetUVCoordList())[uvCoordIndex];
        meshVertex.m_TextureCoord1 = ConvertUVCoord(uvCoord);
    }

    return meshVertex;
}

void FixupWindingOrder(Iron::Mesh::Triangle* triangle, const Iron::DynamicVector<Iron::Mesh::Vertex, true>& vertices)
{
    Iron::Vector3 v0 = vertices.At(triangle->m_iVertex0).m_Position - vertices.At(triangle->m_iVertex1).m_Position;
    Iron::Vector3 v1 = vertices.At(triangle->m_iVertex0).m_Position - vertices.At(triangle->m_iVertex2).m_Position;
    Iron::Vector3 cross = Iron::Vector3::Normalize(Iron::Vector3::Cross(v0, v1));
    float dot = Iron::Vector3::Dot(cross, vertices.At(triangle->m_iVertex0).m_Normal);
    if (dot <= 0.f)
    {
        //bad winding order!
        Iron::Algorithm::Swap(&triangle->m_iVertex1, &triangle->m_iVertex2);
    }
}

void DumpPolygonListWithMaterial(SceneInfo* sceneInfo, CSLShape_35* shape, CSLPolygonList* polygonList, int materialIndex)
{
    if (polygonList->GetUVArrayCount() > 2)
    {
        printf("Ignoring extra uv channels.\n");
    }

    Iron::DynamicVector<Iron::Mesh::Vertex, true> meshVertices(makeFileLocation());
    Iron::DynamicVector<Iron::Mesh::Triangle, true> meshTriangles(makeFileLocation());

    int baseIndex = 0;
    for (int i = 0; i < polygonList->GetPolygonCount(); ++i)
    {
        int polygonVertexCount = polygonList->GetPolygonVertexCountListPtr()[i];
        int polygonTriCount = polygonVertexCount - 2;
        if ((polygonTriCount < 1) || (polygonTriCount > 2))
        {
            printf("Invalid Polygon Vertex Count: %d\n", polygonVertexCount);
        }
        
        for (int t = 0; t < polygonTriCount; ++t)
        {
            meshVertices.PushBack(DumpMeshVertex(shape, polygonList, baseIndex));
            meshVertices.PushBack(DumpMeshVertex(shape, polygonList, baseIndex + t + 1));
            meshVertices.PushBack(DumpMeshVertex(shape, polygonList, baseIndex + t + 2));

            Iron::Mesh::Triangle meshTriangle;
            meshTriangle.m_iVertex0 = meshVertices.Size() - 3;
            meshTriangle.m_iVertex1 = meshVertices.Size() - 2;
            meshTriangle.m_iVertex2 = meshVertices.Size() - 1;
            meshTriangle.m_iMaterial = materialIndex;
            FixupWindingOrder(&meshTriangle, meshVertices);
            meshTriangles.PushBack(meshTriangle);
        }

        baseIndex += polygonVertexCount;
    }

    int oldVertexCount = sceneInfo->meshData.m_Vertices.Size();
    sceneInfo->meshData.m_Vertices.SetSizePreserve(oldVertexCount + meshVertices.Size());
    for (int i = 0; i < meshVertices.Size(); ++i)
    {
        sceneInfo->meshData.m_Vertices.At(oldVertexCount + i) = meshVertices.At(i);
    }

    int oldTriangleCount = sceneInfo->meshData.m_Triangles.Size();
    sceneInfo->meshData.m_Triangles.SetSizePreserve(oldTriangleCount + meshTriangles.Size());
    for (int i = 0; i < meshTriangles.Size(); ++i)
    {
        meshTriangles.At(i).m_iVertex0 += oldVertexCount;
        meshTriangles.At(i).m_iVertex1 += oldVertexCount;
        meshTriangles.At(i).m_iVertex2 += oldVertexCount;
        sceneInfo->meshData.m_Triangles.At(oldTriangleCount + i) = meshTriangles.At(i);
    }
}

void DumpPolygonList(SceneInfo* sceneInfo, CSLShape_35* shape, CSLPolygonList* polygonList)
{
    if (polygonList->GetMaterial() == NULL)
    {
        printf("PolygonList has no material.\n");
    }
    else
    {
        int materialIndex = -1;
        for (int i = 0; i < sceneInfo->materialNames.Size(); ++i)
        {
            if (Iron::String::IsEqual(sceneInfo->materialNames.At(i), polygonList->GetMaterial()->GetName()))
            {
                materialIndex = i;
                break;
            }
        }

        if (materialIndex == -1)
        {
            printf("Valid Material not found for PolygonList: %s\n", polygonList->GetMaterial()->GetName());
        }
        else
        {
            DumpPolygonListWithMaterial(sceneInfo, shape, polygonList, materialIndex);
        }
    }
}

void DumpMesh(SceneInfo* sceneInfo, CSLModel* model)
{
    CSLMesh* mesh = (CSLMesh*)model->Primitive();
    CSLShape_35* shape = (CSLShape_35*)mesh->Shape();
    
    for (int i = 0; i < mesh->GetPolygonListCount(); ++i)
    {
        CSLPolygonList* polygonList = mesh->PolygonLists()[i];
        DumpPolygonList(sceneInfo, shape, polygonList);
    }
}

void DumpPoints(SceneInfo* sceneInfo, CSLModel* rootModel)
{
    for (int i = 0; i < rootModel->GetChildrenCount(); ++i)
    {
        CSLModel* childModel = rootModel->GetChildrenList()[i];
        if (childModel->GetPrimitiveType() != CSLTemplate::SI_NULL_OBJECT)
        {
            printf("Ignoring '%s' underneath root point. Must be a Null Object!\n", childModel->GetName());
        }
        else
        {
            if (strlen(childModel->GetName()) < 6)
            {
                printf("Invalid Point Name: %s\n", childModel->GetName());
            }
            else
            {
                //chop off first 5 characters (just used to uniquly identify points)
                const char* pointName = childModel->GetName() + 5;
                printf("Point found: '%s'\n", pointName);

                childModel->Transform()->ComputeGlobalMatrix();
                CSIBCMatrix4x4 transform = childModel->Transform()->GetGlobalMatrix();
                
                int oldPointCount = sceneInfo->meshData.m_Points.Size();
                sceneInfo->meshData.m_Points.SetSizePreserve(oldPointCount + 1);

                sceneInfo->meshData.m_Points.At(oldPointCount).m_DataString = pointName;

                CSIBCVector3D translation;
                transform.GetTranslation(translation);
                sceneInfo->meshData.m_Points.At(oldPointCount).m_Position = ConvertCoordSystem(translation);
                            
                /*
                CSIBCVector3D rotation;
                transform.GetRotation(rotation);

                //convert to left-handed
                //http://www.inframez.com/papers/dotxsi_dev.htm
                rotation.m_fX = -rotation.m_fX;
                rotation.m_fY = -rotation.m_fY;

                printf(_T("%.2f, %.2f, %.2f\n"), rotation.m_fX, rotation.m_fY, rotation.m_fZ);
                Iron::Matrix33* o = &sceneInfo->meshData.m_Points.At(oldPointCount).m_Orientation;
                o->BuildIdentity();
                (*o) *= Iron::Matrix33().BuildRotationX(rotation.m_fX);
                (*o) *= Iron::Matrix33().BuildRotationZ(rotation.m_fY);
                (*o) *= Iron::Matrix33().BuildRotationY(rotation.m_fZ);
                */
                
                /*
                CSIBCVector3D orientationXAxis;
                CSIBCVector3D orientationYAxis;
                CSIBCVector3D orientationZAxis;
                transform.GetOrientation(orientationXAxis, orientationYAxis, orientationZAxis);
                Iron::Vector3 xAxis = Iron::Vector3(orientationXAxis.m_fX, orientationXAxis.m_fY, orientationXAxis.m_fZ).Normalize();
                Iron::Vector3 yAxis = Iron::Vector3(orientationYAxis.m_fX, orientationYAxis.m_fY, orientationYAxis.m_fZ).Normalize();
                Iron::Vector3 zAxis = Iron::Vector3(orientationZAxis.m_fX, orientationZAxis.m_fY, orientationZAxis.m_fZ).Normalize();
                //xAxis = ConvertCoordSystem(CSIBCVector3D(xAxis.x, xAxis.y, xAxis.z));
                //yAxis = ConvertCoordSystem(CSIBCVector3D(yAxis.x, yAxis.y, yAxis.z));
                //zAxis = ConvertCoordSystem(CSIBCVector3D(zAxis.x, zAxis.y, zAxis.z));
                Iron::Matrix33* o = &sceneInfo->meshData.m_Points.At(oldPointCount).m_Orientation;
                o->m00 = xAxis.x;
                o->m01 = xAxis.y;
                o->m02 = xAxis.z;
                o->m10 = yAxis.x;
                o->m11 = yAxis.y;
                o->m12 = yAxis.z;
                o->m20 = zAxis.x;
                o->m21 = zAxis.y;
                o->m22 = zAxis.z;
                */

                CSIBCVector3D eulerRot;// = childModel->Transform()->GetEulerRotation();
                transform.GetRotation(eulerRot);
                float ex = eulerRot.GetX();
                float ey = eulerRot.GetY();
                float ez = eulerRot.GetZ();
                Iron::Matrix33* o = &sceneInfo->meshData.m_Points.At(oldPointCount).m_Orientation;
                (*o) = Iron::Matrix33().BuildEulerRotation(
                    ey,
                    ex,
                    ez);
            }
        }
    }
}

void DumpNullObject(SceneInfo* sceneInfo, CSLModel* model)
{
    if (strcmp(model->GetName(), "rootpoint") == 0)
    {
        DumpPoints(sceneInfo, model);
    }
}

void DumpModelRecursive(SceneInfo* sceneInfo, CSLModel* model)
{
    if (model != NULL)
    {
        switch (model->GetPrimitiveType())
        {
            case CSLTemplate::SI_MODEL:
            case CSLTemplate::SI_CAMERA:
            case CSLTemplate::SI_DIRECTIONAL_LIGHT:
                break;

            case CSLTemplate::SI_MESH:
                DumpMesh(sceneInfo, model);
                break;

            case CSLTemplate::SI_NULL_OBJECT:
                DumpNullObject(sceneInfo, model);
                break;

            default:
                printf("Ignoring unknown node type (%d): %s\n", model->GetPrimitiveType(), model->GetName());
                break;
        }

        CSLModel** children = model->GetChildrenList();
        for (int i = 0; i < model->GetChildrenCount(); ++i)
        {
            DumpModelRecursive(sceneInfo, children[i]);
        }
    }
}

CSLXSIShader* FindPhongShader(CSLXSIMaterial* material)
{
    CSLXSIShader* phongShader = NULL;

    for (int i = 0; i < material->GetShaderCount(); ++i)
    {
        CSLXSIShader* shader = material->GetShaderList()[i];
        if (strcmp(shader->GetProgID(), "Softimage.material-phong.1") == 0)
        {
            phongShader = shader;
            break;
        }
    }

    return phongShader;
}

void DumpTextureFileName(Iron::StdFixedString* fileName, CSLXSIMaterial* material, CSLXSIShader* phongShader, char* searchName, char* mapName)
{
    CSLShaderConnectionPoint* searchConnectionPoint = NULL;
    for (int i = 0; i < phongShader->GetConnectionPointCount(); ++i)
    {
        CSLShaderConnectionPoint* connectionPoint = phongShader->GetConnectionPointList()[i];
        if (strcmp(connectionPoint->GetName(), searchName) == 0)
        {
            searchConnectionPoint = connectionPoint;
            break;
        }
    }

    if ((searchConnectionPoint == NULL) || (searchConnectionPoint->GetShader() == NULL))
    {
        //printf("Shader connection '%s' not found in matrial:'%s' - shader:'%s'\n", searchName, material->GetName(), phongShader->GetName());
    }
    else
    {
        CSLShaderConnectionPoint* texConnectionPoint = NULL;

        for (int i = 0; i < searchConnectionPoint->GetShader()->GetConnectionPointCount(); ++i)
        {
            CSLShaderConnectionPoint* connectionPoint = searchConnectionPoint->GetShader()->GetConnectionPointList()[i];
            if (strcmp(connectionPoint->GetName(), "tex") == 0)
            {
                texConnectionPoint = connectionPoint;
                break;
            }
        }

        if (texConnectionPoint == NULL)
        {
            printf("'tex' not found in shader:'%s'\n", searchConnectionPoint->GetShader()->GetName());
        }
        else
        {
            (*fileName) = texConnectionPoint->GetImage();

            // for some reason the extension is changed from ".dds" to "_dds" in .xsi export, not to sure why, hack it back ...
            int fileNameLength = Iron::String::GetLength(*fileName);
            if ((fileNameLength > 4) && fileName->GetBuffer()[fileNameLength - 4] == _T('_'))
            {
                fileName->GetBuffer()[fileNameLength - 4] = _T('.');
            }
            else
            {
                printf("Unexpected texture file name: %s\n", fileName->GetBuffer());
                (*fileName) = _T("");
            }
        }
    }
}

CSLVariantParameter* FindParam(CSLXSIShader* shader, const char* name)
{
    CSLVariantParameter* foundParam = NULL;
    for (int i = 0; i < shader->GetParameterCount(); ++i)
    {
        CSLVariantParameter* param = shader->GetParameterList()[i];
        if (strcmp(param->GetName(), name) == 0)
        {
            foundParam = param;
            break;
        }
    }
    return foundParam;
}

void DumpMaterials(SceneInfo* sceneInfo, CSLMaterialLibrary* materialLibrary)
{
    if (materialLibrary == NULL)
    {
        printf("No material library exists!\n");
    }
    else
    {
        int materialCount = materialLibrary->GetMaterialCount();
        for (int i = 0; i < materialCount; ++i)
        {
            CSLBaseMaterial* baseMaterial = materialLibrary->GetMaterialList()[i];
            if (baseMaterial->Type() != CSLTemplate::XSI_MATERIAL)
            {
                printf("Ignoring unknown material type (%d): %s\n", baseMaterial->Type(), baseMaterial->GetName());
            }
            else
            {
                CSLXSIMaterial* material = (CSLXSIMaterial*)baseMaterial;
                CSLXSIShader* phongShader = FindPhongShader(material);
                if (phongShader == NULL)
                {
                    printf("Phong shader not found in material: %s\n", material->GetName());
                }
                else
                {
                    Iron::Mesh::Material meshMaterial;
                    DumpTextureFileName(&meshMaterial.m_DiffuseTextureFileName, material, phongShader, "diffuse", "DiffuseMap");
                    DumpTextureFileName(&meshMaterial.m_NormalTextureFileName, material, phongShader, "ambient", "NormalMap");
                    DumpTextureFileName(&meshMaterial.m_SelfIlluminationTextureFileName, material, phongShader, "specular", "DataMap");
                    meshMaterial.m_Ambient = Iron::Color::WHITE;
                    meshMaterial.m_Diffuse = Iron::Color::WHITE;
                    meshMaterial.m_Specular = Iron::Color::WHITE;
                    meshMaterial.m_Emissive = Iron::Color::WHITE;
                    
                    float glossiness = 1.f;
                    CSLVariantParameter* glossinessParam = FindParam(phongShader, "shiny");
                    if (glossinessParam != NULL)
                    {
                        glossiness = glossinessParam->GetFloatValue();
                    }
                    meshMaterial.m_Glossiness = glossiness;

                    sceneInfo->meshData.m_Materials.SetSizePreserve(sceneInfo->meshData.m_Materials.Size() + 1);
                    sceneInfo->meshData.m_Materials.At(sceneInfo->meshData.m_Materials.Size() - 1) = meshMaterial;
                    sceneInfo->materialNames.PushBack(material->GetName());

                    printf("Material found: '%s'\n", material->GetName());
                    printf(" cl = '%s'\n", meshMaterial.m_DiffuseTextureFileName.GetBuffer());
                    printf(" nm = '%s'\n", meshMaterial.m_NormalTextureFileName.GetBuffer());
                    printf(" da = '%s'\n", meshMaterial.m_SelfIlluminationTextureFileName.GetBuffer());
                }
            }
        }
    }
}

void CalculateBoundingInformation(SceneInfo* sceneInfo)
{
    sceneInfo->meshData.CalculateBoundingInformation();
    printf("Bounding Information Calculated - Radius:%.2f\n", sceneInfo->meshData.m_BoundingRadius);
}

class SortDupVertexMapValues
{
public:
    bool operator()(std::vector<int>& a, std::vector<int>& b)
    {
        return (a[0] < b[0]);
    }
};

void OptimizeMesh(SceneInfo* sceneInfo)
{
    printf("Optimizing...\n");
    printf("Pre-Optimization Vertex Count = %d\n", sceneInfo->meshData.m_Vertices.Size());

    typedef std::map< int, std::vector<int> > DupVertexMap;

    // first - build a map to remember every vertex-index of duplicate vertices (checksum -> vertex indices).
    DupVertexMap dupVertexMap;
    for (int i = 0; i < sceneInfo->meshData.m_Vertices.Size(); ++i)
    {
        const Iron::Mesh::Vertex& vertex = sceneInfo->meshData.m_Vertices.At(i);
        int vertexCheckSum = vertex.GetCheckSum();
        dupVertexMap[vertexCheckSum].push_back(i);

        int otherVertexIndex = *dupVertexMap[vertexCheckSum].begin();
        const Iron::Mesh::Vertex& otherVertex = sceneInfo->meshData.m_Vertices.At(otherVertexIndex);
        if(vertex != otherVertex)
        {
            printf("FAILED VERTEX CHECKSUM\n");
        }
    }

    // second - build a vector of vertex-index-vectors, we just needed the map to create this more useful data structure.
    typedef std::vector< std::vector<int> > DupVertexMapValues;
    DupVertexMapValues dupVertexMapValues;
    for (DupVertexMap::const_iterator i = dupVertexMap.begin(); i != dupVertexMap.end(); ++i)
    {
        dupVertexMapValues.push_back(i->second);
    }
    std::sort(dupVertexMapValues.begin(), dupVertexMapValues.end(), SortDupVertexMapValues());
    
    // third - build up a vertex index remap dictionary, then update all triangles with it.
    typedef std::map< int, int > VertexIndexRemap;
    VertexIndexRemap vertexIndexRemap;
    for (int i = 0; i < dupVertexMapValues.size(); ++i)
    {
        const std::vector<int>& vertexIndices = dupVertexMapValues.at(i);
        for (int j = 0; j < vertexIndices.size(); ++j)
        {
            vertexIndexRemap[ vertexIndices.at(j) ] = i;
        }
    }

    for (int i = 0; i < sceneInfo->meshData.m_Triangles.Size(); ++i)
    {
        Iron::Mesh::Triangle* triangle = &sceneInfo->meshData.m_Triangles.At(i);
        triangle->m_iVertex0 = vertexIndexRemap[triangle->m_iVertex0];
        triangle->m_iVertex1 = vertexIndexRemap[triangle->m_iVertex1];
        triangle->m_iVertex2 = vertexIndexRemap[triangle->m_iVertex2];
    }

    // fourth - create a new smaller set of vertices.
    Iron::DynamicArray<Iron::Mesh::Vertex> newVertices(makeFileLocation());
    newVertices.SetSizeNoPreserve((int)dupVertexMapValues.size());
    for (int i = 0; i < dupVertexMapValues.size(); ++i)
    {
        newVertices.At(i) = sceneInfo->meshData.m_Vertices.At(dupVertexMapValues.at(i).at(0));
    }
    sceneInfo->meshData.m_Vertices = newVertices;

    printf("Post-Optimization Vertex Count = %d\n", sceneInfo->meshData.m_Vertices.Size());
}

void ValidateMesh(SceneInfo* sceneInfo)
{
    for (int i = 0; i < sceneInfo->meshData.m_Triangles.Size(); ++i)
    {
        const Iron::Mesh::Triangle& triangle = sceneInfo->meshData.m_Triangles.At(i);
        
        int duplicateVertexIndex = -1;
        if (triangle.m_iVertex0 == triangle.m_iVertex1)
        {
            duplicateVertexIndex = triangle.m_iVertex1;
        }
        else if (triangle.m_iVertex0 == triangle.m_iVertex2)
        {
            duplicateVertexIndex = triangle.m_iVertex2;
        }
        else if (triangle.m_iVertex1 == triangle.m_iVertex2)
        {
            duplicateVertexIndex = triangle.m_iVertex2;
        }

        if (duplicateVertexIndex != -1)
        {
            const Iron::Mesh::Vertex& vertex = sceneInfo->meshData.m_Vertices.At(duplicateVertexIndex);
            printf("Error *** Found duplicate vertex at [%.1f , %.1f , %.1f]\n", vertex.m_Position.x, vertex.m_Position.y, vertex.m_Position.z);
        }
    }
}

void DumpScene(SceneInfo* sceneInfo, CSLScene* scene)
{
    DumpMaterials(sceneInfo, scene->GetMaterialLibrary());
    DumpModelRecursive(sceneInfo, scene->Root());
    CalculateBoundingInformation(sceneInfo);
}

int _tmain(int argc, _TCHAR* argv[])
{
    SI_Error siError = SI_SUCCESS;

    if (argc < 3)
    {
        printf("%s expects at least 2 arguments: <in.xsi> <out.mesh> <--nooptimize>\n", argv[0]);
        siError = SI_FILE_NOT_FOUND;
    }
    else
    {
        char* inFileName = argv[1];
        char* outFileName = argv[2];
        
        bool optimize = true;
        if (argc > 3)
        {
            const TCHAR* option = argv[3];
            if (Iron::String::IsEqual(option, _T("--nooptimize")))
            {
                optimize = false;
            }
            else
            {
                printf("unknown option found: %s\n", option);
            }
        }

        CSLScene scene;
        siError = scene.Open(inFileName);
        if (siError != SI_SUCCESS)
        {
            printf("Failed to Open %s.\n", inFileName);
        }
        else
        {
            siError = scene.Read();
            if (siError != SI_SUCCESS)
            {
                printf("Failed to Read %s.\n", inFileName);
            }
            else
            {
                SceneInfo info;
                DumpScene(&info, &scene);

                if (optimize)
                {
                    OptimizeMesh(&info);
                }

                ValidateMesh(&info);

                info.meshData.m_hasValidTangents = true;
                if(!g_MeshSystem.SaveMeshData(outFileName, info.meshData, Iron::FileTranslation::Text))
                {
                    printf("Failed to Save %s.\n", outFileName);
                }
            }

            scene.Close();
        }
    }

    return siError;
}