<commit_before>// Modify ConvertFromFLT::setTexture to create a new osg::TexEnvCombiner in texture stateset to handle 
// detail texture
// <NAME>, June 18th 2003.


#include <stdio.h>
#include <string.h>
#include <osg/GL>

#include <osg/Group>
#include <osg/LOD>
#include <osg/MatrixTransform>
#include <osg/Geode>
#include <osg/StateSet>
#include <osg/CullFace>
#include <osg/TexEnv>
#include <osg/TexEnvCombine>
#include <osg/TexGen>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/Point>
#include <osg/Material>
#include <osg/PolygonOffset>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Billboard>
#include <osg/Texture2D>
#include <osg/LightSource>
#include <osg/Image>
#include <osg/Notify>
#include <osg/Sequence>

#include <osgSim/MultiSwitch>
#include <osgSim/DOFTransform>
#include <osgSim/LightPointNode>
#include <osgSim/LightPointSystem>
#include <osgSim/Sector>
#include <osgSim/BlinkSequence>


#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/ReadFile>
#include <osgDB/Registry>


#include "opcodes.h"
#include "flt.h"
#include "flt2osg.h"
#include "FltFile.h"
#include "Registry.h"
#include "Record.h"
#include "HeaderRecord.h"
#include "ColorPaletteRecord.h"
#include "MaterialPaletteRecord.h"
#include "OldMaterialPaletteRecord.h"
#include "TexturePaletteRecord.h"
#include "LightPointPaletteRecords.h"
#include "VertexPoolRecords.h"
#include "OldVertexRecords.h"
#include "GroupRecord.h"
#include "LodRecord.h"
#include "DofRecord.h"
#include "SwitchRecord.h"
#include "ObjectRecord.h"
#include "FaceRecord.h"
#include "MeshRecord.h"
#include "MeshPrimitiveRecord.h"
#include "TransformationRecords.h"
#include "ExternalRecord.h"
#include "LightPointRecord.h"
#include "LightPointSystemRecord.h"
#include "Input.h"
#include "GeoSetBuilder.h"
#include "LongIDRecord.h"
#include "CommentRecord.h"
#include "InstanceRecords.h"
#include "LocalVertexPoolRecord.h"
#include "MultiTextureRecord.h"
#include "UVListRecord.h"
#include "LightSourceRecord.h"
#include "LightSourcePaletteRecord.h"
#include "AttrData.h"
#include "BSPRecord.h"



using namespace flt;

unsigned int mystrnlen(char *s, unsigned int maxLen)
{
    for (unsigned int i = 0; i < maxLen; i++)
    {
        if (!s[i]) return i;
    }
    return maxLen;
}

ConvertFromFLT::ConvertFromFLT() :
    _faceColor(1,1,1,1)
{
    _diOpenFlightVersion = 0;
    _diCurrentOffset = 0;
    _wObjTransparency = 0;
    _nSubfaceLevel = 0;
    _unitScale = 1.0;
    _useTextureAlphaForTranspancyBinning = true;
    _bHdrRgbMode = false;
    _currentLocalVertexPool = 0;
    _doUnitsConversion = true;
}


ConvertFromFLT::~ConvertFromFLT()
{
}


osg::Group* ConvertFromFLT::convert(HeaderRecord* rec)
{
    if (rec==NULL) return NULL;
    return visitHeader(rec);
}


////////////////////////////////////////////////////////////////////

Record* ConvertFromFLT::getVertexFromPool(int nOffset)
{
    VertexPaletteOffsetMap::iterator fitr = _VertexPaletteOffsetMap.find(nOffset);
    if (fitr != _VertexPaletteOffsetMap.end())
    {
        return (*fitr).second;
    }
    else return NULL;
}


void ConvertFromFLT::regisiterVertex(int nOffset, Record* pRec)
{
    _VertexPaletteOffsetMap[nOffset] = pRec;
}


////////////////////////////////////////////////////////////////////


osg::Group* ConvertFromFLT::visitInstanceDefinition(osg::Group& osgParent,InstanceDefinitionRecord* rec)
{
    osg::Group* group = new osg::Group;
    InstancePool* pInstancePool = rec->getFltFile()->getInstancePool();

    visitAncillary(osgParent, *group, rec);

    pInstancePool->addInstance((int)rec->getData()->iInstDefNumber,group);
    visitPrimaryNode(*group, (PrimNodeRecord*)rec);

    return group;
}

osg::Group* ConvertFromFLT::visitInstanceReference(osg::Group& osgParent,InstanceReferenceRecord* rec)
{
    osg::Group* group;
    InstancePool* pInstancePool = rec->getFltFile()->getInstancePool();

    group = pInstancePool->getInstance((int)rec->getData()->iInstDefNumber);
    if (group)
        osgParent.addChild( group );
    else
        osg::notify(osg::INFO) << "Warning: cannot find the instance definition in flt file."<<std::endl;
    return group;
}


osg::Group* ConvertFromFLT::visitAncillary(osg::Group& osgParent, osg::Group& osgPrimary, PrimNodeRecord* rec)
{
    // Note: There are databases that contains nodes with both Matrix and GeneralMatrix
    // ancillary records. We need just one of these to put into the scenegraph
    // Nick.
    bool mxFound  = false;

    osg::Group* parent = &osgParent;
    // Visit ancillary records
    for(int i=0; i < rec->getNumChildren(); i++)
    {
        Record* child = rec->getChild(i);
        if (!child->isAncillaryRecord())
            break;

        switch (child->getOpcode())
        {
        case LONG_ID_OP:
            visitLongID(osgPrimary, (LongIDRecord*)child);
            break;

        case GENERAL_MATRIX_OP:
            // Note: Ancillary record creates osg node
            if (!mxFound)
            {
                parent = visitGeneralMatrix(*parent, osgPrimary, (GeneralMatrixRecord*)child);
                mxFound = true;
            }
            break;

        case MATRIX_OP:
            // Note: Ancillary record creates osg node
            if (!mxFound)
            {
                parent = visitMatrix(*parent, osgPrimary, (MatrixRecord*)child);
                mxFound = true;
            }
            break;

        case COMMENT_OP:
              visitComment(osgPrimary, (CommentRecord*)child);
            break;

        case COLOR_PALETTE_OP:
            visitColorPalette(osgPrimary, (ColorPaletteRecord*)child);
            break;

        case LIGHT_SOURCE_PALETTE_OP:
            visitLightSourcePalette(osgPrimary, (LightSourcePaletteRecord*)child);
            break;

        case MATERIAL_PALETTE_OP:
            visitMaterialPalette(osgPrimary, (MaterialPaletteRecord*)child);
            break;

        case OLD_MATERIAL_PALETTE_OP:
            visitOldMaterialPalette(osgPrimary, (OldMaterialPaletteRecord*)child);
            break;

        case TEXTURE_PALETTE_OP:
            visitTexturePalette(osgPrimary, (TexturePaletteRecord*)child);
            break;

        case LIGHT_PT_APPEARANCE_PALETTE_OP:
            visitLtPtAppearancePalette(osgPrimary, (LtPtAppearancePaletteRecord*)child);
            break;

        case VERTEX_PALETTE_OP:
            visitVertexPalette(osgPrimary, (VertexPaletteRecord*)child);
            break;

        case VERTEX_C_OP:
            visitVertex(osgPrimary, (VertexRecord*)child);
            break;

        case VERTEX_CN_OP:
            visitNormalVertex(osgPrimary, (NormalVertexRecord*)child);
            break;

        case VERTEX_CNT_OP:
            visitNormalTextureVertex(osgPrimary, (NormalTextureVertexRecord*)child);
            break;

        case VERTEX_CT_OP:
            visitTextureVertex(osgPrimary, (TextureVertexRecord*)child);
            break;

        default:

        #ifdef _DEBUG

            osg::notify( osg::INFO ) << "flt::ConvertFromFLT::visitAncillary: "
            << "Unknown opcode: " << child->getOpcode() << "\n";
            
        #endif

            break;
        }
    }
    return parent;
}


osg::Group* ConvertFromFLT::visitPrimaryNode(osg::Group& osgParent, PrimNodeRecord* rec)
{
    osg::Group* osgPrim = NULL;
    GeoSetBuilder   geoSetBuilder;
    GeoSetBuilder   billboardBuilder;

    // Visit
    for(int i=0; i < rec->getNumChildren(); i++)
    {
        Record* child = rec->getChild(i);

        if (child && child->isPrimaryNode())
        {
            switch (child->getOpcode())
            {
            case MESH_OP:

                if( ((MeshRecord*)child)->getData()->swTemplateTrans == 2)  //Axis type rotate
                    visitMesh(osgParent, &billboardBuilder, (MeshRecord*)child);
                else
                    visitMesh(osgParent, &geoSetBuilder, (MeshRecord*)child);
                break;

            case FACE_OP:
            {
                FaceRecord* fr = (FaceRecord*)child;
                if( fr->getData()->swTemplateTrans == 2)  //Axis type rotate
                    visitFace(&billboardBuilder, fr);
                else
                    visitFace(&geoSetBuilder, fr);
            }
            break;
            case LIGHT_POINT_OP:
#ifdef USE_DEPRECATED_LIGHTPOINT
                visitLightPoint(&geoSetBuilder, (LightPointRecord*)child);
#else
                visitLightPoint(osgParent, (LightPointRecord*)child);
#endif
                break;
            case INDEXED_LIGHT_PT_OP:
                visitLightPointIndex(osgParent, (LightPointIndexRecord*)child);
                break;
            case LIGHT_PT_SYSTEM_OP:
                osgPrim = visitLightPointSystem(osgParent, (LightPointSystemRecord*)child);
                break;
            case GROUP_OP:
                osgPrim = visitGroup(osgParent, (GroupRecord*)child);
                break;
            case LIGHT_SOURCE_OP:
                osgPrim = visitLightSource(osgParent, (LightSourceRecord*)child);
                break;
            case LOD_OP:
                osgPrim = visitLOD(osgParent, (LodRecord*)child);
                break;
            case OLD_LOD_OP:
                osgPrim = visitOldLOD(osgParent, (OldLodRecord*)child);
                break;
            case DOF_OP:
                osgPrim = visitDOF(osgParent, (DofRecord*)child);
                break;
            case BSP_OP:
                osgPrim = visitBSP(osgParent, (BSPRecord*)child);
                break;
            case SWITCH_OP:
                osgPrim = visitSwitch(osgParent, (SwitchRecord*)child);
                break;
            case OBJECT_OP:
                osgPrim = visitObject(osgParent, (ObjectRecord*)child);
                break;
            case INSTANCE_REFERENCE_OP:
                osgPrim = visitInstanceReference(osgParent, (InstanceReferenceRecord*)child);
                break;
            case INSTANCE_DEFINITION_OP:
                osgPrim = visitInstanceDefinition(osgParent, (InstanceDefinitionRecord*)child);
                break;
            case EXTERNAL_REFERENCE_OP:
                osgPrim = visitExternal(osgParent, (ExternalRecord*)child);
                break;
            case ROAD_CONSTRUCTION_OP:
                // treat road construction record as a group record for now
                osgPrim = visitRoadConstruction(osgParent, (GroupRecord*)child);
                break;
        
            default:

            #ifdef _DEBUG
            
                osg::notify(osg::INFO) << "In ConvertFromFLT::visitPrimaryNode(), unknown opcode: " << child->getOpcode() << std::endl;

            #endif

                break;
            }
        }
    }

    if( !geoSetBuilder.empty() )
    {
        osg::Geode* geode = new osg::Geode;        
        geoSetBuilder.createOsgGeoSets(geode );
    
        if (geode->getNumDrawables() > 0)
            osgParent.addChild( geode );
    }

    if( !billboardBuilder.empty() )
    {
        osg::Billboard* billboard = new osg::Billboard;
        billboardBuilder.createOsgGeoSets(billboard );
        
        if (billboard->getNumDrawables() > 0)
            osgParent.addChild( billboard );
    }

    return osgPrim;
}


void ConvertFromFLT::visitLongID(osg::Group& osgParent, LongIDRecord* rec)
{
    SLongID *pSLongID = (SLongID*)rec->getData();

    unsigned int stringLength = mystrnlen(pSLongID->szIdent,rec->getBodyLength());
    osgParent.setName(std::string(pSLongID->szIdent,stringLength));
}


void ConvertFromFLT::visitComment(osg::Node& osgParent, CommentRecord* rec)
{
    SComment *pSComment = (SComment*)rec->getData();

    //std::cout << "ConvertFromFLT::visitComment '"<<std::string(pSComment->szComment,pSComment->RecHeader.length()-4)<<"'"<<std::endl;
    //std::cout << "ConvertFromFLT::visitComment cstyle string '"<<pSComment->szComment<<"'"<<std::endl;

    unsigned int stringLength = mystrnlen(pSComment->szComment,rec->getBodyLength());
    std::string commentfield(pSComment->szComment,stringLength);
    unsigned int front_of_line = 0;
    unsigned int end_of_line = 0;
    while (end_of_line<commentfield.size())
    {
        if (commentfield[end_of_line]=='\r')
        {
            osgParent.addDescription( std::string( commentfield, front_of_line, end_of_line-front_of_line) );
        
            if (end_of_line+1<commentfield.size() &&
                commentfield[end_of_line+1]=='\n') ++end_of_line;

            ++end_of_line;
            front_of_line = end_of_line;
        }
        else if (commentfield[end_of_line]=='\n')
        {
            osgParent.addDescription( std::string( commentfield, front_of_line, end_of_line-front_of_line) );
            ++end_of_line;
            front_of_line = end_of_line;
        }
        else ++end_of_line;
    }
    if (front_of_line<end_of_line)
    {
        osgParent.addDescription( std::string( commentfield, front_of_line, end_of_line-front_of_line) );
    }
    
}


osg::Group* ConvertFromFLT::visitHeader(HeaderRecord* rec)
{
    SHeader *pSHeader = (SHeader*)rec->getData();

    // Version
    _diOpenFlightVersion = pSHeader->diFormatRevLev;
    osg::notify(osg::INFO) << "Version " << _diOpenFlightVersion << std::endl;

    // Unit scale
    if ( _doUnitsConversion ) 
    {
        switch (rec->getFltFile()->getDesiredUnits())
        {
        case FltFile::ConvertToMeters:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 1.0;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 1000.0;
                break;
            case HeaderRecord::FEET:
                _unitScale = 0.3048;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.02540;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 1852.0;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToKilometers:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 0.001;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 1.0;
                break;
            case HeaderRecord::FEET:
                _unitScale = 0.0003048;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.0000254;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 1.852;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToFeet:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 3.2808399;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 3280.839895;
                break;
            case HeaderRecord::FEET:
                _unitScale = 1.0;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.0833333;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 6076.1154856 ;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToInches:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 39.3700787;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 39370.0787402;
                break;
            case HeaderRecord::FEET:
                _unitScale = 12.0;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 1.0;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 72913.3858268;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToNauticalMiles:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 0.0005399568;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 0.5399568;
                break;
            case HeaderRecord::FEET:
                _unitScale = 0.0001646;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.0000137;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 1.0;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        default:
            _unitScale = 1.0;
            break;
        }
    }
    else
    {    
        _unitScale = 1.0;
    }

    // Flight v.11 & v.12 use integer coordinates
    if (rec->getFlightVersion() < 13)
    {
        if (pSHeader->iMultDivUnit >= 0)
            _unitScale *= (double)pSHeader->iMultDivUnit;
        else
            _unitScale /= (double)(-pSHeader->iMultDivUnit);
    }

    _bHdrRgbMode = (pSHeader->dwFlags & 0x40000000) ? true : false;    // RGB space (=packed color)

    // Create root group node
    osg::Group* group = new osg::Group;
    group->setName(pSHeader->szIdent);
    visitAncillary(*group, *group, rec);
    visitPrimaryNode(*group, rec);

    return group;
}


                                 /*osgParent*/
void ConvertFromFLT::visitColorPalette(osg::Group& , ColorPaletteRecord* rec)
{
    if (!rec->getFltFile()->useInternalColorPalette()) return;

    ColorPool* pColorPool = rec->getFltFile()->getColorPool();
    int flightVersion = rec->getFlightVersion();

    if (flightVersion > 13)
    {
        SColorPalette* pCol = (SColorPalette*)rec->getData();
        int colors = (flightVersion >= 1500) ? 1024 : 512;

        for (int i=0; i < colors; i++)
        {
            osg::Vec4 color(pCol->Colors[i].get());
            color[3] = 1.0f;    // Force alpha to one
            pColorPool->addColor(i, color);
        }
    }
    else    // version 11, 12 & 13
    {
        SOldColorPalette* pCol = (SOldColorPalette*)rec->getData();
        unsigned int i;
        for (i=0; i < sizeof(pCol->Colors)/sizeof(pCol->Colors[0]); i++)
        {
            osg::Vec4 color(pCol->Colors[i].get());
            color[3] = 1.0f;    // Force alpha to one
            pColorPool->addColor(i, color);
        }

        for (i=0; i < sizeof(pCol->FixedColors)/sizeof(pCol->FixedColors[0]); i++)
        {
            osg::Vec4 color(pCol->FixedColors[i].get());
            color[3] = 1.0f;    // Force alpha to one
            pColorPool->addColor(i+(4096>>7), color);
        }
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitLightSourcePalette(osg::Group& , LightSourcePaletteRecord* rec)
{

    SLightSourcePalette* pLight = (SLightSourcePalette*)rec->getData();

    osg::Light* light = new osg::Light();
    
    light->setAmbient( osg::Vec4(
                pLight->sfAmbientRGBA[0], pLight->sfAmbientRGBA[1],
                pLight->sfAmbientRGBA[2], pLight->sfAmbientRGBA[3] ) );
    light->setDiffuse( osg::Vec4(
                pLight->sfDiffuseRGBA[0], pLight->sfDiffuseRGBA[1],
                pLight->sfDiffuseRGBA[2], pLight->sfDiffuseRGBA[3] ) );
    light->setSpecular( osg::Vec4(
                pLight->sfSpecularRGBA[0], pLight->sfSpecularRGBA[1],
                pLight->sfSpecularRGBA[2], pLight->sfSpecularRGBA[3] ) );
    light->setConstantAttenuation( pLight->sfConstantAttuenation );
    light->setLinearAttenuation( pLight->sfLinearAttuenation );
    light->setQuadraticAttenuation( pLight->sfQuadraticAttuenation );
    //light->setSpotExponent( pLight->sfDropoff );
    //light->setSpotCutoff( pLight->sfCutoff );

    LightPool* pLightPool = rec->getFltFile()->getLightPool();
    pLightPool->addLight( pLight->diIndex, light );
}

                                 /*osgParent*/
void ConvertFromFLT::visitMaterialPalette(osg::Group&, MaterialPaletteRecord* rec)
{
    if (!rec->getFltFile()->useInternalMaterialPalette()) return;

    SMaterial* pSMaterial = (SMaterial*)rec->getData();
    MaterialPool* pMaterialPool = rec->getFltFile()->getMaterialPool();
    if (pSMaterial && pMaterialPool)
    {
        MaterialPool::PoolMaterial* pPoolMat = new MaterialPool::PoolMaterial;

        pPoolMat->Ambient   = pSMaterial->Ambient;
        pPoolMat->Diffuse   = pSMaterial->Diffuse;
        pPoolMat->Specular  = pSMaterial->Specular;
        pPoolMat->Emissive  = pSMaterial->Emissive;
        pPoolMat->sfShininess = pSMaterial->sfShininess;
        pPoolMat->sfAlpha   = pSMaterial->sfAlpha;

        pMaterialPool->addMaterial((int)pSMaterial->diIndex, pPoolMat);
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitOldMaterialPalette(osg::Group& , OldMaterialPaletteRecord* rec)
{
    if (!rec->getFltFile()->useInternalMaterialPalette()) return;

    SOldMaterial* pSMaterial = (SOldMaterial*)rec->getData();
    MaterialPool* pMaterialPool = rec->getFltFile()->getMaterialPool();

    if (pSMaterial && pMaterialPool )
    {
        for (int i=0; i < 64; i++)
        {
            MaterialPool::PoolMaterial* pPoolMat = new MaterialPool::PoolMaterial;

            pPoolMat->Ambient   = pSMaterial->mat[i].Ambient;
            pPoolMat->Diffuse   = pSMaterial->mat[i].Diffuse;
            pPoolMat->Specular  = pSMaterial->mat[i].Specular;
            pPoolMat->Emissive  = pSMaterial->mat[i].Emissive;
            pPoolMat->sfShininess = pSMaterial->mat[i].sfShininess;
            pPoolMat->sfAlpha   = pSMaterial->mat[i].sfAlpha;
            
            pMaterialPool->addMaterial(i, pPoolMat);
        }
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitTexturePalette(osg::Group& , TexturePaletteRecord* rec)
{
    int nIndex;
    char* pFilename;

    if (!rec->getFltFile()->useInternalTexturePalette()) return;


    if (rec->getFlightVersion() > 13)
    {
        STexturePalette* pTexture = (STexturePalette*)rec->getData();
        pFilename = pTexture->szFilename;
        nIndex = pTexture->diIndex;
    }
    else // version 11, 12 & 13
    {
        SOldTexturePalette* pOldTexture = (SOldTexturePalette*)rec->getData();
        pFilename = pOldTexture->szFilename;
        nIndex = pOldTexture->diIndex;
    }

    TexturePool* pTexturePool = rec->getFltFile()->getTexturePool();
    if (pTexturePool == NULL) return;


    std::string textureName(pFilename);
    pTexturePool->addTextureName(nIndex, textureName);

    CERR<<"pTexturePool->addTextureName("<<nIndex<<", "<<textureName<<")"<<std::endl;
}


void ConvertFromFLT::visitLtPtAppearancePalette(osg::Group& /*osgParent*/, LtPtAppearancePaletteRecord* rec)
{
    SLightPointAppearancePalette* ltPtApp = (SLightPointAppearancePalette*)rec->getData();
    LtPtAppearancePool* pool = rec->getFltFile()->getLtPtAppearancePool();
    assert( pool );
    if (ltPtApp && pool)
    {
        LtPtAppearancePool::PoolLtPtAppearance* entry = new LtPtAppearancePool::PoolLtPtAppearance;

        entry->_iBackColorIdx = ltPtApp->backColor;
        entry->_sfIntensity = ltPtApp->intensity;
        entry->_sfMinPixelSize = ltPtApp->minPixelSize;
        entry->_sfMaxPixelSize = ltPtApp->maxPixelSize;
        entry->_sfActualSize = ltPtApp->actualSize;
        entry->_iDirectionality = ltPtApp->directionality;
        entry->_sfHLobeAngle = ltPtApp->horizLobeAngle;
        entry->_sfVLobeAngle = ltPtApp->vertLobeAngle;

        pool->add(ltPtApp->index, entry);
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitVertexPalette(osg::Group& , VertexPaletteRecord* rec)
{
    _diCurrentOffset = rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitVertex(osg::Group& , VertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitNormalVertex(osg::Group& , NormalVertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitTextureVertex(osg::Group& , TextureVertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitNormalTextureVertex(osg::Group& , NormalTextureVertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}


osg::Group* ConvertFromFLT::visitBSP(osg::Group& osgParent, BSPRecord* rec)
{
    // create group node for the time being
        osg::Group* group = new osg::Group;
        group->setName(rec->getData()->szIdent);

        visitAncillary(osgParent, *group, rec)->addChild( group );
        visitPrimaryNode(*group, rec);

        return group;
}

osg::Group* ConvertFromFLT::visitGroup(osg::Group& osgParent, GroupRecord* rec)
{
	const int fltVer = rec->getFltFile()->getFlightVersion();

    SGroup* currentGroup = (SGroup*) rec->getData();

    const bool forwardAnim = (currentGroup->dwFlags & GroupRecord::FORWARD_ANIM)!=0;
	// OpenFlight 15.8 adds backwards animations
    const bool backwardAnim = ( (fltVer >= 1580) &&
		((currentGroup->dwFlags & GroupRecord::BACKWARD_ANIM) != 0) );
	// Regardless of forwards or backwards, animation could have swing bit set
	const osg::Sequence::LoopMode loopMode = ( (currentGroup->dwFlags & GroupRecord::SWING_ANIM) == 0 ) ?
		osg::Sequence::LOOP : osg::Sequence::SWING;
     
    if( forwardAnim || backwardAnim)
    {
        osg::Sequence* animSeq = new osg::Sequence;
        
        visitAncillary(osgParent, *animSeq, rec)->addChild( animSeq );
        visitPrimaryNode(*animSeq, rec);

		const int numReps = (fltVer >= 1580) ?
			currentGroup->iLoopCount : 1000000;
		const float frameDuration = (fltVer >= 1580) ?
			currentGroup->fLoopDuration / (float)animSeq->getNumChildren() : 0.f;
        animSeq->setDuration( frameDuration, numReps );

        if ( forwardAnim )
            animSeq->setInterval( loopMode, 0, -1 );
        else // Backwards animation
            animSeq->setInterval( loopMode, -1, 0 );

        animSeq->setMode( osg::Sequence::START );<commit_msg>Fixed tabbing to be all four spaced tabs<commit_after>// Modify ConvertFromFLT::setTexture to create a new osg::TexEnvCombiner in texture stateset to handle 
// detail texture
// <NAME>, June 18th 2003.


#include <stdio.h>
#include <string.h>
#include <osg/GL>

#include <osg/Group>
#include <osg/LOD>
#include <osg/MatrixTransform>
#include <osg/Geode>
#include <osg/StateSet>
#include <osg/CullFace>
#include <osg/TexEnv>
#include <osg/TexEnvCombine>
#include <osg/TexGen>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/Point>
#include <osg/Material>
#include <osg/PolygonOffset>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Billboard>
#include <osg/Texture2D>
#include <osg/LightSource>
#include <osg/Image>
#include <osg/Notify>
#include <osg/Sequence>

#include <osgSim/MultiSwitch>
#include <osgSim/DOFTransform>
#include <osgSim/LightPointNode>
#include <osgSim/LightPointSystem>
#include <osgSim/Sector>
#include <osgSim/BlinkSequence>


#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/ReadFile>
#include <osgDB/Registry>


#include "opcodes.h"
#include "flt.h"
#include "flt2osg.h"
#include "FltFile.h"
#include "Registry.h"
#include "Record.h"
#include "HeaderRecord.h"
#include "ColorPaletteRecord.h"
#include "MaterialPaletteRecord.h"
#include "OldMaterialPaletteRecord.h"
#include "TexturePaletteRecord.h"
#include "LightPointPaletteRecords.h"
#include "VertexPoolRecords.h"
#include "OldVertexRecords.h"
#include "GroupRecord.h"
#include "LodRecord.h"
#include "DofRecord.h"
#include "SwitchRecord.h"
#include "ObjectRecord.h"
#include "FaceRecord.h"
#include "MeshRecord.h"
#include "MeshPrimitiveRecord.h"
#include "TransformationRecords.h"
#include "ExternalRecord.h"
#include "LightPointRecord.h"
#include "LightPointSystemRecord.h"
#include "Input.h"
#include "GeoSetBuilder.h"
#include "LongIDRecord.h"
#include "CommentRecord.h"
#include "InstanceRecords.h"
#include "LocalVertexPoolRecord.h"
#include "MultiTextureRecord.h"
#include "UVListRecord.h"
#include "LightSourceRecord.h"
#include "LightSourcePaletteRecord.h"
#include "AttrData.h"
#include "BSPRecord.h"



using namespace flt;

unsigned int mystrnlen(char *s, unsigned int maxLen)
{
    for (unsigned int i = 0; i < maxLen; i++)
    {
        if (!s[i]) return i;
    }
    return maxLen;
}

ConvertFromFLT::ConvertFromFLT() :
    _faceColor(1,1,1,1)
{
    _diOpenFlightVersion = 0;
    _diCurrentOffset = 0;
    _wObjTransparency = 0;
    _nSubfaceLevel = 0;
    _unitScale = 1.0;
    _useTextureAlphaForTranspancyBinning = true;
    _bHdrRgbMode = false;
    _currentLocalVertexPool = 0;
    _doUnitsConversion = true;
}


ConvertFromFLT::~ConvertFromFLT()
{
}


osg::Group* ConvertFromFLT::convert(HeaderRecord* rec)
{
    if (rec==NULL) return NULL;
    return visitHeader(rec);
}


////////////////////////////////////////////////////////////////////

Record* ConvertFromFLT::getVertexFromPool(int nOffset)
{
    VertexPaletteOffsetMap::iterator fitr = _VertexPaletteOffsetMap.find(nOffset);
    if (fitr != _VertexPaletteOffsetMap.end())
    {
        return (*fitr).second;
    }
    else return NULL;
}


void ConvertFromFLT::regisiterVertex(int nOffset, Record* pRec)
{
    _VertexPaletteOffsetMap[nOffset] = pRec;
}


////////////////////////////////////////////////////////////////////


osg::Group* ConvertFromFLT::visitInstanceDefinition(osg::Group& osgParent,InstanceDefinitionRecord* rec)
{
    osg::Group* group = new osg::Group;
    InstancePool* pInstancePool = rec->getFltFile()->getInstancePool();

    visitAncillary(osgParent, *group, rec);

    pInstancePool->addInstance((int)rec->getData()->iInstDefNumber,group);
    visitPrimaryNode(*group, (PrimNodeRecord*)rec);

    return group;
}

osg::Group* ConvertFromFLT::visitInstanceReference(osg::Group& osgParent,InstanceReferenceRecord* rec)
{
    osg::Group* group;
    InstancePool* pInstancePool = rec->getFltFile()->getInstancePool();

    group = pInstancePool->getInstance((int)rec->getData()->iInstDefNumber);
    if (group)
        osgParent.addChild( group );
    else
        osg::notify(osg::INFO) << "Warning: cannot find the instance definition in flt file."<<std::endl;
    return group;
}


osg::Group* ConvertFromFLT::visitAncillary(osg::Group& osgParent, osg::Group& osgPrimary, PrimNodeRecord* rec)
{
    // Note: There are databases that contains nodes with both Matrix and GeneralMatrix
    // ancillary records. We need just one of these to put into the scenegraph
    // Nick.
    bool mxFound  = false;

    osg::Group* parent = &osgParent;
    // Visit ancillary records
    for(int i=0; i < rec->getNumChildren(); i++)
    {
        Record* child = rec->getChild(i);
        if (!child->isAncillaryRecord())
            break;

        switch (child->getOpcode())
        {
        case LONG_ID_OP:
            visitLongID(osgPrimary, (LongIDRecord*)child);
            break;

        case GENERAL_MATRIX_OP:
            // Note: Ancillary record creates osg node
            if (!mxFound)
            {
                parent = visitGeneralMatrix(*parent, osgPrimary, (GeneralMatrixRecord*)child);
                mxFound = true;
            }
            break;

        case MATRIX_OP:
            // Note: Ancillary record creates osg node
            if (!mxFound)
            {
                parent = visitMatrix(*parent, osgPrimary, (MatrixRecord*)child);
                mxFound = true;
            }
            break;

        case COMMENT_OP:
              visitComment(osgPrimary, (CommentRecord*)child);
            break;

        case COLOR_PALETTE_OP:
            visitColorPalette(osgPrimary, (ColorPaletteRecord*)child);
            break;

        case LIGHT_SOURCE_PALETTE_OP:
            visitLightSourcePalette(osgPrimary, (LightSourcePaletteRecord*)child);
            break;

        case MATERIAL_PALETTE_OP:
            visitMaterialPalette(osgPrimary, (MaterialPaletteRecord*)child);
            break;

        case OLD_MATERIAL_PALETTE_OP:
            visitOldMaterialPalette(osgPrimary, (OldMaterialPaletteRecord*)child);
            break;

        case TEXTURE_PALETTE_OP:
            visitTexturePalette(osgPrimary, (TexturePaletteRecord*)child);
            break;

        case LIGHT_PT_APPEARANCE_PALETTE_OP:
            visitLtPtAppearancePalette(osgPrimary, (LtPtAppearancePaletteRecord*)child);
            break;

        case VERTEX_PALETTE_OP:
            visitVertexPalette(osgPrimary, (VertexPaletteRecord*)child);
            break;

        case VERTEX_C_OP:
            visitVertex(osgPrimary, (VertexRecord*)child);
            break;

        case VERTEX_CN_OP:
            visitNormalVertex(osgPrimary, (NormalVertexRecord*)child);
            break;

        case VERTEX_CNT_OP:
            visitNormalTextureVertex(osgPrimary, (NormalTextureVertexRecord*)child);
            break;

        case VERTEX_CT_OP:
            visitTextureVertex(osgPrimary, (TextureVertexRecord*)child);
            break;

        default:

        #ifdef _DEBUG

            osg::notify( osg::INFO ) << "flt::ConvertFromFLT::visitAncillary: "
            << "Unknown opcode: " << child->getOpcode() << "\n";
            
        #endif

            break;
        }
    }
    return parent;
}


osg::Group* ConvertFromFLT::visitPrimaryNode(osg::Group& osgParent, PrimNodeRecord* rec)
{
    osg::Group* osgPrim = NULL;
    GeoSetBuilder   geoSetBuilder;
    GeoSetBuilder   billboardBuilder;

    // Visit
    for(int i=0; i < rec->getNumChildren(); i++)
    {
        Record* child = rec->getChild(i);

        if (child && child->isPrimaryNode())
        {
            switch (child->getOpcode())
            {
            case MESH_OP:

                if( ((MeshRecord*)child)->getData()->swTemplateTrans == 2)  //Axis type rotate
                    visitMesh(osgParent, &billboardBuilder, (MeshRecord*)child);
                else
                    visitMesh(osgParent, &geoSetBuilder, (MeshRecord*)child);
                break;

            case FACE_OP:
            {
                FaceRecord* fr = (FaceRecord*)child;
                if( fr->getData()->swTemplateTrans == 2)  //Axis type rotate
                    visitFace(&billboardBuilder, fr);
                else
                    visitFace(&geoSetBuilder, fr);
            }
            break;
            case LIGHT_POINT_OP:
#ifdef USE_DEPRECATED_LIGHTPOINT
                visitLightPoint(&geoSetBuilder, (LightPointRecord*)child);
#else
                visitLightPoint(osgParent, (LightPointRecord*)child);
#endif
                break;
            case INDEXED_LIGHT_PT_OP:
                visitLightPointIndex(osgParent, (LightPointIndexRecord*)child);
                break;
            case LIGHT_PT_SYSTEM_OP:
                osgPrim = visitLightPointSystem(osgParent, (LightPointSystemRecord*)child);
                break;
            case GROUP_OP:
                osgPrim = visitGroup(osgParent, (GroupRecord*)child);
                break;
            case LIGHT_SOURCE_OP:
                osgPrim = visitLightSource(osgParent, (LightSourceRecord*)child);
                break;
            case LOD_OP:
                osgPrim = visitLOD(osgParent, (LodRecord*)child);
                break;
            case OLD_LOD_OP:
                osgPrim = visitOldLOD(osgParent, (OldLodRecord*)child);
                break;
            case DOF_OP:
                osgPrim = visitDOF(osgParent, (DofRecord*)child);
                break;
            case BSP_OP:
                osgPrim = visitBSP(osgParent, (BSPRecord*)child);
                break;
            case SWITCH_OP:
                osgPrim = visitSwitch(osgParent, (SwitchRecord*)child);
                break;
            case OBJECT_OP:
                osgPrim = visitObject(osgParent, (ObjectRecord*)child);
                break;
            case INSTANCE_REFERENCE_OP:
                osgPrim = visitInstanceReference(osgParent, (InstanceReferenceRecord*)child);
                break;
            case INSTANCE_DEFINITION_OP:
                osgPrim = visitInstanceDefinition(osgParent, (InstanceDefinitionRecord*)child);
                break;
            case EXTERNAL_REFERENCE_OP:
                osgPrim = visitExternal(osgParent, (ExternalRecord*)child);
                break;
            case ROAD_CONSTRUCTION_OP:
                // treat road construction record as a group record for now
                osgPrim = visitRoadConstruction(osgParent, (GroupRecord*)child);
                break;
        
            default:

            #ifdef _DEBUG
            
                osg::notify(osg::INFO) << "In ConvertFromFLT::visitPrimaryNode(), unknown opcode: " << child->getOpcode() << std::endl;

            #endif

                break;
            }
        }
    }

    if( !geoSetBuilder.empty() )
    {
        osg::Geode* geode = new osg::Geode;        
        geoSetBuilder.createOsgGeoSets(geode );
    
        if (geode->getNumDrawables() > 0)
            osgParent.addChild( geode );
    }

    if( !billboardBuilder.empty() )
    {
        osg::Billboard* billboard = new osg::Billboard;
        billboardBuilder.createOsgGeoSets(billboard );
        
        if (billboard->getNumDrawables() > 0)
            osgParent.addChild( billboard );
    }

    return osgPrim;
}


void ConvertFromFLT::visitLongID(osg::Group& osgParent, LongIDRecord* rec)
{
    SLongID *pSLongID = (SLongID*)rec->getData();

    unsigned int stringLength = mystrnlen(pSLongID->szIdent,rec->getBodyLength());
    osgParent.setName(std::string(pSLongID->szIdent,stringLength));
}


void ConvertFromFLT::visitComment(osg::Node& osgParent, CommentRecord* rec)
{
    SComment *pSComment = (SComment*)rec->getData();

    //std::cout << "ConvertFromFLT::visitComment '"<<std::string(pSComment->szComment,pSComment->RecHeader.length()-4)<<"'"<<std::endl;
    //std::cout << "ConvertFromFLT::visitComment cstyle string '"<<pSComment->szComment<<"'"<<std::endl;

    unsigned int stringLength = mystrnlen(pSComment->szComment,rec->getBodyLength());
    std::string commentfield(pSComment->szComment,stringLength);
    unsigned int front_of_line = 0;
    unsigned int end_of_line = 0;
    while (end_of_line<commentfield.size())
    {
        if (commentfield[end_of_line]=='\r')
        {
            osgParent.addDescription( std::string( commentfield, front_of_line, end_of_line-front_of_line) );
        
            if (end_of_line+1<commentfield.size() &&
                commentfield[end_of_line+1]=='\n') ++end_of_line;

            ++end_of_line;
            front_of_line = end_of_line;
        }
        else if (commentfield[end_of_line]=='\n')
        {
            osgParent.addDescription( std::string( commentfield, front_of_line, end_of_line-front_of_line) );
            ++end_of_line;
            front_of_line = end_of_line;
        }
        else ++end_of_line;
    }
    if (front_of_line<end_of_line)
    {
        osgParent.addDescription( std::string( commentfield, front_of_line, end_of_line-front_of_line) );
    }
    
}


osg::Group* ConvertFromFLT::visitHeader(HeaderRecord* rec)
{
    SHeader *pSHeader = (SHeader*)rec->getData();

    // Version
    _diOpenFlightVersion = pSHeader->diFormatRevLev;
    osg::notify(osg::INFO) << "Version " << _diOpenFlightVersion << std::endl;

    // Unit scale
    if ( _doUnitsConversion ) 
    {
        switch (rec->getFltFile()->getDesiredUnits())
        {
        case FltFile::ConvertToMeters:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 1.0;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 1000.0;
                break;
            case HeaderRecord::FEET:
                _unitScale = 0.3048;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.02540;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 1852.0;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToKilometers:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 0.001;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 1.0;
                break;
            case HeaderRecord::FEET:
                _unitScale = 0.0003048;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.0000254;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 1.852;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToFeet:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 3.2808399;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 3280.839895;
                break;
            case HeaderRecord::FEET:
                _unitScale = 1.0;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.0833333;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 6076.1154856 ;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToInches:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 39.3700787;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 39370.0787402;
                break;
            case HeaderRecord::FEET:
                _unitScale = 12.0;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 1.0;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 72913.3858268;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        case FltFile::ConvertToNauticalMiles:
            switch (pSHeader->swVertexCoordUnit)
            {
            case HeaderRecord::METERS:
                _unitScale = 0.0005399568;
                break;
            case HeaderRecord::KILOMETERS:
                _unitScale = 0.5399568;
                break;
            case HeaderRecord::FEET:
                _unitScale = 0.0001646;
                break;
            case HeaderRecord::INCHES:
                _unitScale = 0.0000137;
                break;
            case HeaderRecord::NAUTICAL_MILES:
                _unitScale = 1.0;
                break;
            default:
                _unitScale = 1.0;
            }
            break;

        default:
            _unitScale = 1.0;
            break;
        }
    }
    else
    {    
        _unitScale = 1.0;
    }

    // Flight v.11 & v.12 use integer coordinates
    if (rec->getFlightVersion() < 13)
    {
        if (pSHeader->iMultDivUnit >= 0)
            _unitScale *= (double)pSHeader->iMultDivUnit;
        else
            _unitScale /= (double)(-pSHeader->iMultDivUnit);
    }

    _bHdrRgbMode = (pSHeader->dwFlags & 0x40000000) ? true : false;    // RGB space (=packed color)

    // Create root group node
    osg::Group* group = new osg::Group;
    group->setName(pSHeader->szIdent);
    visitAncillary(*group, *group, rec);
    visitPrimaryNode(*group, rec);

    return group;
}


                                 /*osgParent*/
void ConvertFromFLT::visitColorPalette(osg::Group& , ColorPaletteRecord* rec)
{
    if (!rec->getFltFile()->useInternalColorPalette()) return;

    ColorPool* pColorPool = rec->getFltFile()->getColorPool();
    int flightVersion = rec->getFlightVersion();

    if (flightVersion > 13)
    {
        SColorPalette* pCol = (SColorPalette*)rec->getData();
        int colors = (flightVersion >= 1500) ? 1024 : 512;

        for (int i=0; i < colors; i++)
        {
            osg::Vec4 color(pCol->Colors[i].get());
            color[3] = 1.0f;    // Force alpha to one
            pColorPool->addColor(i, color);
        }
    }
    else    // version 11, 12 & 13
    {
        SOldColorPalette* pCol = (SOldColorPalette*)rec->getData();
        unsigned int i;
        for (i=0; i < sizeof(pCol->Colors)/sizeof(pCol->Colors[0]); i++)
        {
            osg::Vec4 color(pCol->Colors[i].get());
            color[3] = 1.0f;    // Force alpha to one
            pColorPool->addColor(i, color);
        }

        for (i=0; i < sizeof(pCol->FixedColors)/sizeof(pCol->FixedColors[0]); i++)
        {
            osg::Vec4 color(pCol->FixedColors[i].get());
            color[3] = 1.0f;    // Force alpha to one
            pColorPool->addColor(i+(4096>>7), color);
        }
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitLightSourcePalette(osg::Group& , LightSourcePaletteRecord* rec)
{

    SLightSourcePalette* pLight = (SLightSourcePalette*)rec->getData();

    osg::Light* light = new osg::Light();
    
    light->setAmbient( osg::Vec4(
                pLight->sfAmbientRGBA[0], pLight->sfAmbientRGBA[1],
                pLight->sfAmbientRGBA[2], pLight->sfAmbientRGBA[3] ) );
    light->setDiffuse( osg::Vec4(
                pLight->sfDiffuseRGBA[0], pLight->sfDiffuseRGBA[1],
                pLight->sfDiffuseRGBA[2], pLight->sfDiffuseRGBA[3] ) );
    light->setSpecular( osg::Vec4(
                pLight->sfSpecularRGBA[0], pLight->sfSpecularRGBA[1],
                pLight->sfSpecularRGBA[2], pLight->sfSpecularRGBA[3] ) );
    light->setConstantAttenuation( pLight->sfConstantAttuenation );
    light->setLinearAttenuation( pLight->sfLinearAttuenation );
    light->setQuadraticAttenuation( pLight->sfQuadraticAttuenation );
    //light->setSpotExponent( pLight->sfDropoff );
    //light->setSpotCutoff( pLight->sfCutoff );

    LightPool* pLightPool = rec->getFltFile()->getLightPool();
    pLightPool->addLight( pLight->diIndex, light );
}

                                 /*osgParent*/
void ConvertFromFLT::visitMaterialPalette(osg::Group&, MaterialPaletteRecord* rec)
{
    if (!rec->getFltFile()->useInternalMaterialPalette()) return;

    SMaterial* pSMaterial = (SMaterial*)rec->getData();
    MaterialPool* pMaterialPool = rec->getFltFile()->getMaterialPool();
    if (pSMaterial && pMaterialPool)
    {
        MaterialPool::PoolMaterial* pPoolMat = new MaterialPool::PoolMaterial;

        pPoolMat->Ambient   = pSMaterial->Ambient;
        pPoolMat->Diffuse   = pSMaterial->Diffuse;
        pPoolMat->Specular  = pSMaterial->Specular;
        pPoolMat->Emissive  = pSMaterial->Emissive;
        pPoolMat->sfShininess = pSMaterial->sfShininess;
        pPoolMat->sfAlpha   = pSMaterial->sfAlpha;

        pMaterialPool->addMaterial((int)pSMaterial->diIndex, pPoolMat);
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitOldMaterialPalette(osg::Group& , OldMaterialPaletteRecord* rec)
{
    if (!rec->getFltFile()->useInternalMaterialPalette()) return;

    SOldMaterial* pSMaterial = (SOldMaterial*)rec->getData();
    MaterialPool* pMaterialPool = rec->getFltFile()->getMaterialPool();

    if (pSMaterial && pMaterialPool )
    {
        for (int i=0; i < 64; i++)
        {
            MaterialPool::PoolMaterial* pPoolMat = new MaterialPool::PoolMaterial;

            pPoolMat->Ambient   = pSMaterial->mat[i].Ambient;
            pPoolMat->Diffuse   = pSMaterial->mat[i].Diffuse;
            pPoolMat->Specular  = pSMaterial->mat[i].Specular;
            pPoolMat->Emissive  = pSMaterial->mat[i].Emissive;
            pPoolMat->sfShininess = pSMaterial->mat[i].sfShininess;
            pPoolMat->sfAlpha   = pSMaterial->mat[i].sfAlpha;
            
            pMaterialPool->addMaterial(i, pPoolMat);
        }
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitTexturePalette(osg::Group& , TexturePaletteRecord* rec)
{
    int nIndex;
    char* pFilename;

    if (!rec->getFltFile()->useInternalTexturePalette()) return;


    if (rec->getFlightVersion() > 13)
    {
        STexturePalette* pTexture = (STexturePalette*)rec->getData();
        pFilename = pTexture->szFilename;
        nIndex = pTexture->diIndex;
    }
    else // version 11, 12 & 13
    {
        SOldTexturePalette* pOldTexture = (SOldTexturePalette*)rec->getData();
        pFilename = pOldTexture->szFilename;
        nIndex = pOldTexture->diIndex;
    }

    TexturePool* pTexturePool = rec->getFltFile()->getTexturePool();
    if (pTexturePool == NULL) return;


    std::string textureName(pFilename);
    pTexturePool->addTextureName(nIndex, textureName);

    CERR<<"pTexturePool->addTextureName("<<nIndex<<", "<<textureName<<")"<<std::endl;
}


void ConvertFromFLT::visitLtPtAppearancePalette(osg::Group& /*osgParent*/, LtPtAppearancePaletteRecord* rec)
{
    SLightPointAppearancePalette* ltPtApp = (SLightPointAppearancePalette*)rec->getData();
    LtPtAppearancePool* pool = rec->getFltFile()->getLtPtAppearancePool();
    assert( pool );
    if (ltPtApp && pool)
    {
        LtPtAppearancePool::PoolLtPtAppearance* entry = new LtPtAppearancePool::PoolLtPtAppearance;

        entry->_iBackColorIdx = ltPtApp->backColor;
        entry->_sfIntensity = ltPtApp->intensity;
        entry->_sfMinPixelSize = ltPtApp->minPixelSize;
        entry->_sfMaxPixelSize = ltPtApp->maxPixelSize;
        entry->_sfActualSize = ltPtApp->actualSize;
        entry->_iDirectionality = ltPtApp->directionality;
        entry->_sfHLobeAngle = ltPtApp->horizLobeAngle;
        entry->_sfVLobeAngle = ltPtApp->vertLobeAngle;

        pool->add(ltPtApp->index, entry);
    }
}

                                 /*osgParent*/
void ConvertFromFLT::visitVertexPalette(osg::Group& , VertexPaletteRecord* rec)
{
    _diCurrentOffset = rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitVertex(osg::Group& , VertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitNormalVertex(osg::Group& , NormalVertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitTextureVertex(osg::Group& , TextureVertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}

                                 /*osgParent*/
void ConvertFromFLT::visitNormalTextureVertex(osg::Group& , NormalTextureVertexRecord* rec)
{
    regisiterVertex(_diCurrentOffset, rec);
    _diCurrentOffset += rec->getSize();
}


osg::Group* ConvertFromFLT::visitBSP(osg::Group& osgParent, BSPRecord* rec)
{
    // create group node for the time being
        osg::Group* group = new osg::Group;
        group->setName(rec->getData()->szIdent);

        visitAncillary(osgParent, *group, rec)->addChild( group );
        visitPrimaryNode(*group, rec);

        return group;
}

osg::Group* ConvertFromFLT::visitGroup(osg::Group& osgParent, GroupRecord* rec)
{
    const int fltVer = rec->getFltFile()->getFlightVersion();

    SGroup* currentGroup = (SGroup*) rec->getData();

    const bool forwardAnim = (currentGroup->dwFlags & GroupRecord::FORWARD_ANIM)!=0;
    // OpenFlight 15.8 adds backwards animations
    const bool backwardAnim = ( (fltVer >= 1580) &&
        ((currentGroup->dwFlags & GroupRecord::BACKWARD_ANIM) != 0) );
    // Regardless of forwards or backwards, animation could have swing bit set
    const osg::Sequence::LoopMode loopMode = ( (currentGroup->dwFlags & GroupRecord::SWING_ANIM) == 0 ) ?
        osg::Sequence::LOOP : osg::Sequence::SWING;
     
    if( forwardAnim || backwardAnim)
    {
        osg::Sequence* animSeq = new osg::Sequence;
        
        visitAncillary(osgParent, *animSeq, rec)->addChild( animSeq );
        visitPrimaryNode(*animSeq, rec);

        const int numReps = (fltVer >= 1580) ?
            currentGroup->iLoopCount : 1000000;
        const float frameDuration = (fltVer >= 1580) ?
            currentGroup->fLoopDuration / (float)animSeq->getNumChildren() : 0.f;
        animSeq->setDuration( frameDuration, numReps );

        if ( forwardAnim )
            animSeq->setInterval( loopMode, 0, -1 );
        else // Backwards animation
            animSeq->setInterval( loopMode, -1, 0 );

        animSeq->setMode( osg::Sequence::START );
{'fraction_non_alphanumeric': 0.12111213183599243, 'fraction_numerical': 0.012111213183599243, 'mean_word_length': 2.2739995089614533, 'pattern_counts': {'":': 0, '<': 201, '<?xml version=': 0, '>': 485, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1660998', 'n_tokens_mistral': 17095, 'n_tokens_neox': 16170, 'n_words': 2853}