Subversion Repositories spk

Rev

Rev 1 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "bob_dom_bob.h"

#include "bob_stream_operators.h"
//---------------------------------------------------------------------------------
// BOB file
bool bob_dom_bob::load(ibinaryfilestream& is)
{
        int hdr;
        is >> hdr;
        if(hdr!=HDR_BEGIN){
                error(e_badHeader);
                return false;
        }

        if(peek(is)==bob_info::HDR_BEGIN){
                if(!info.load(is)){
                        for(ErrorIterator &it=info.errors.begin(); it!=info.errors.end(); ++it){
                                error(it->code, "info: %s", it->text);
                        }
                        return false;
                }
        }

        if(materials.load(is)==false){
                for(ErrorIterator &it=materials.errors.begin(); it!=materials.errors.end(); ++it){
                        error(it->code, "materials->%s", it->text);
                        return false;
                }
        }

        if(bodies.load(is)==false){
                for(ErrorIterator &it=bodies.errors.begin(); it!=bodies.errors.end(); ++it){
                        error(it->code, "bodies->%s", it->text);
                }
                return false;
        }

        is >> hdr;
        if(hdr!=HDR_END)
                error(e_badEndHeader);

        return(hdr==HDR_END && !is.fail());
}
//---------------------------------------------------------------------------------
bool bob_dom_bob::toFile(obinaryfilestream& os)
{
        os << HDR_BEGIN;
        info.toFile(os);
        materials.toFile(os);
        bodies.toFile(os);
        os << HDR_END;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_bob::toFile(otextfilestream& os)
{
        info.toFile(os);
        os << endl;
        materials.toFile(os);
        if(materials.size())
                os << endl;
        if(bodies.toFile(os, *m_settings, &materials)==false){
                for(bob_bodies::ErrorIterator &e_it=bodies.errors.begin(); e_it!=bodies.errors.end(); ++e_it){
                        error(e_it->severity, e_it->code, "Bodies->%s", e_it->text);
                }
                return false;
        }
        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL  - object
bool bob_material1::load(ibinaryfilestream& is)
{
        is >> index;
        is >> textureID; // int?
        is >> ambient;
        is >> diffuse;
        is >> specular;

        errorCode=is.fail() ? e_notEnoughData : e_noError;

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_material1::toFile(obinaryfilestream& os)
{
        os << index << textureID << ambient << diffuse << specular;

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_material1::toFile(otextfilestream& os)
{
        os << noSemicolons << "MATERIAL";
        switch(type){
                case mat3:
                        os << '3';
                        break;
                case mat5:
                        os << '5';
                        break;
        }
        os << ": " << autoSemicolons;
        os << index << textureID;
        os << ambient << diffuse << specular;

        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL 3 - object
bool bob_material3::load(ibinaryfilestream& is)
{
        base::load(is);
        if(type > mat1){
                is >> transparency; // int?
                is >> selfIllumination;
                is >> shininess;
                short s;
                is >> s;
                destinationBlend = (s & 0x2) > 0;
                twoSided = (s & 0x10) > 0;
                wireframe = (s & 0x8) > 0;
                is >> textureValue;
                is >> enviromentMap;
                is >> bumpMap;
        }
        errorCode=is.fail() ? e_notEnoughData : e_noError;

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_material3::toFile(obinaryfilestream& os)
{
        base::toFile(os);
        os << transparency << selfIllumination << shininess;
        short s=0;
        if(destinationBlend) s|=0x2;
        if(twoSided) s|=0x10;
        if(wireframe) s|=0x8;
        os << s;
        os << textureValue << enviromentMap << bumpMap;

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_material3::toFile(otextfilestream& os)
{
        base::toFile(os);
        os << autoSemicolons;
        os << transparency << selfIllumination << shininess;
        os << noSemicolons << destinationBlend << ';' << twoSided << ';' << autoSemicolons << wireframe;
        os << textureValue;
        os << enviromentMap << bumpMap;
        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL5
bool bob_material5::load(ibinaryfilestream& is)
{
        base::load(is);
        if(type > mat3)
                is >> lightMap;

        errorCode=is.fail() ? e_notEnoughData : e_noError;

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_material5::toFile(obinaryfilestream& os)
{
        base::toFile(os);
        os << lightMap;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_material5::toFile(otextfilestream& os)
{
        base::toFile(os);
        if(type > mat3)
                os << autoSemicolons << lightMap;
        return os.good();
}
//---------------------------------------------------------------------------------
char * material6_value::m_stringTypes[]={
        "SPTYPE_LONG",
        "SPTYPE_BOOL",
        "SPTYPE_FLOAT",
        "",
        "",
        "SPTYPE_FLOAT4",
        "",
        "",
        "SPTYPE_STRING"
};

int material6_value::m_stringTypesCount=9;

bob_error_codes material6_value::load(ibinaryfilestream &is)
{
        is >> name;
        if(is.fail()) return e_error;
        short t;
        is >> t;
        type=(Type)t;

        switch(type){
                case typeBool: // no break
                case typeLong:
                        is >> val.i;
                        break;
                case typeFloat:
                        is >> val.f;
                        break;
                case typeFloat4:
                        is >> val.f4;
                        break;
                case typeString:
                        is >> val.psz;
                        break;
                default:
                        return e_unkMaterialValueType;
        }
        return is.fail() ? e_error : e_noError;
}
//---------------------------------------------------------------------------------
bool material6_value::toFile(obinaryfilestream& os)
{
        os << name << (short) type;

        switch(type){
                case typeBool: // no break
                case typeLong:
                        os << val.i;
                        break;
                case typeFloat:
                        os << val.f;
                        break;
                case typeFloat4:
                        os << val.f4;
                        break;
                case typeString:
                        os << val.psz;
                        break;
                default:
                        return false;
        }
        return os.good();
}
//---------------------------------------------------------------------------------
bool material6_value::toFile(otextfilestream& os)
{
        int old=os.flags();
        os << noSemicolons << name << ';';

        os << typeName() << ';';

        switch(type){
                case typeLong:
                        os << val.i << ';';
                        break;
                case typeBool:
                        os << val.i << ';';
                        break;
                case typeFloat:
                        os << val.f << ';';
                        break;
                case typeFloat4:
                        for(int i=0; i < 4; i++){
                                os << val.f4.f[i] << ';';
                        }
                        break;
                case typeString:
                        os << val.psz << ';';
                        break;
                default:
                        return false;
        }
        os << ' ';
        os.flags(old);
        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL6_VALUES
bob_error_codes bob_material6_values::load(ibinaryfilestream& is)
{
        short count;
        material6_value *v;

        bob_error_codes res;

        is >> count;
        for(int i=0; i < count; i++){
                v=new material6_value();
                if((res=v->load(is))!=e_noError) {
                        delete v;
                        return res;
                }
                push_back(v);
        }
        return is.fail() ? e_error : e_noError;
}
//---------------------------------------------------------------------------------
bool bob_material6_values::toFile(obinaryfilestream& os)
{
        os << (short)size();
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os);
        }
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_material6_values::toFile(otextfilestream& os)
{
        os << (int)size();
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os);
        }
        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL6 - big
bob_error_codes bob_material6::Big::load(ibinaryfilestream& is)
{
        bob_error_codes errorCode;

        is >> technique >> effect;

        errorCode=values.load(is);

        return errorCode;
}
//---------------------------------------------------------------------------------
bool bob_material6::Big::toFile(obinaryfilestream& os)
{
        os << technique << effect;
        values.toFile(os);
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_material6::Big::toFile(otextfilestream& os)
{
        int old=os.flags();
        os << autoSemicolons << technique << effect;
        values.toFile(os);
        os.flags(old);
        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL 6 - small
bob_error_codes bob_material6::Small::load(ibinaryfilestream& is, int flags)
{
        is >> textureFile;
        is >> ambient >> diffuse >> specular;
        is >> transparency >> selfIllumination >> shininess;
        short s=flags;
        //is >> s;
        destinationBlend = (s & 0x2) > 0;
        twoSided = (s & 0x10) > 0;
        wireframe = (s & 0x8) > 0;
        is >> textureValue >> enviromentMap >> bumpMap >> lightMap;
        is >> map4 >> map5;

        return !is.fail() ? e_noError : e_notEnoughData;
}
//---------------------------------------------------------------------------------
bool bob_material6::Small::toFile(obinaryfilestream& os)
{
        static char *empty="";
        os << (textureFile ? textureFile : empty);
        os << ambient << diffuse << specular;
        os << transparency << selfIllumination << shininess;

        os << textureValue << enviromentMap << bumpMap << lightMap;
        os << map4 << map5;

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_material6::Small::toFile(otextfilestream& os)
{
        os << (*textureFile==0 ? "NULL" : textureFile);
        os << ambient << diffuse << specular;
        os << transparency << selfIllumination << shininess;
        os << noSemicolons << destinationBlend << ';' << twoSided << ';' << autoSemicolons << wireframe;
        os << textureValue;
        os << enviromentMap << bumpMap << lightMap << map4 << map5;
        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL 6
bool bob_material6::load(ibinaryfilestream& is)
{
        is >> index;
        is >> flags;

        if(flags==Big::flag) {
                big=new Big();
                errorCode=big->load(is);
        }
        else{
                /*FILE *f=fopen("d:\\mat56.log", "a+");
                fprintf(f, "%s - flags: %d, offset 0x%x\n", is.name(), flags, is.tell());
                fclose(f);
                */

                small=new Small();
                errorCode=small->load(is, flags);
        }

        return errorCode==e_noError;
}
//---------------------------------------------------------------------------------
bool bob_material6::toFile(obinaryfilestream& os)
{
        bool bRes;

        os << index << flags;
        if(big)
                bRes=big->toFile(os);
        else
                bRes=small->toFile(os);

        return bRes;
}
//---------------------------------------------------------------------------------
bool bob_material6::toFile(otextfilestream& os)
{
        int old=os.flags();
        os << noSemicolons << "MATERIAL6: "
        << autoSemicolons << index << noSemicolons << "0x" << hex << autoSemicolons << flags << dec;

        if(big)
                big->toFile(os);
        else if(small)
                small->toFile(os);

        os.flags(old);
        return os.good();
}
//---------------------------------------------------------------------------------
// MATERIAL - section
bool bob_materials::load(ibinaryfilestream& is)
{
        int hdr;
        bob_material::materialType type;

        is >> hdr;
        
        switch(hdr){
                case HDR_MAT5_BEGIN:
                        type=bob_material::mat5;
                        break;
                case HDR_MAT6_BEGIN:
                        type=bob_material::mat6;
                        break;
                default:
                        error(e_badHeader);
                        return false;
        }

        int matCount;
        is >> matCount;

        bob_material *m;
        for(int i=0; i < matCount; i++){
                switch(type) {
                        case bob_material::mat5:
                                m=new bob_material5();
                                break;
                        case bob_material::mat6:
                                m=new bob_material6();
                                break;
                }
                if(m->load(is)==false){
                        error(m->errorCode, "material[%d]: %s", i, bob_traslate_error(m->errorCode));
                        delete m;
                        return false;
                }
                else
                        push_back(m);
        }
        is >> hdr;
        if(hdr!=HDR_END)
                error(e_badEndHeader);

        return (hdr==HDR_END && !is.fail());
}
//---------------------------------------------------------------------------------
bool bob_materials::toFile(obinaryfilestream& os)
{
        int hdr;
        if(size() && front()->type==bob_material::mat6)
                hdr=HDR_MAT6_BEGIN;
        else
                hdr=HDR_MAT5_BEGIN;

        os << hdr << (int)size();
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os);
        }
        os << HDR_END;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_materials::toFile(otextfilestream& os)
{
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os);
                os << endl;
        }
        return os.good();
}
//---------------------------------------------------------------------------------
// BODIES - section
bool bob_bodies::load(ibinaryfilestream& is)
{
        int hdr;
        is >> hdr;
        if(hdr!=HDR_BEGIN){
                error(e_badHeader);
                return false;
        }
        short bodyCount;
        is >> bodyCount;

        child_type *ch;
        for(int i=0; i < bodyCount; i++){
                ch=createChild();
                if(ch->load(is)==false){
                        for(ErrorIterator &it=ch->errors.begin(); it!=ch->errors.end(); ++it){
                                error(it->code, "body[%d]->%s", i, it->text);
                        }
                        return false;
                }
        }

        is >> hdr;
        if(is.fail())
                error(e_notEnoughData);

        if(hdr!=HDR_END)
                error(e_badEndHeader);

        return hdr==HDR_END;
}
//---------------------------------------------------------------------------------
bool bob_bodies::toFile(obinaryfilestream& os)
{
        os << HDR_BEGIN << (short)size();
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os);
        }
        os << HDR_END;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_bodies::toFile(otextfilestream& os, const Settings& settings, const bob_materials *materials)
{
        int i=1;
        os << noSemicolons << "// beginning of bodies (" << (int)size() << ')' << endl;
        for(iterator &it=begin(); it!=end(); ++it, ++i){
                if(it->toFile(os, settings, materials, i)==false){
                        for(bob_body::ErrorIterator &e_it=it->errors.begin(); e_it!=it->errors.end(); ++e_it){
                                error(e_it->severity, e_it->code, "Body[%d]->%s", i, e_it->text);
                        }
                        return false;
                }
                os << endl;
        }
        os << "// end of bodies" << endl;
        return os.good();
}
//---------------------------------------------------------------------------------
// POINTS - section
bool bob_vertices::load(ibinaryfilestream& is)
{
        int hdr;
        is >> hdr;
        if(hdr!=HDR_BEGIN){
                error(e_badHeader);
                return false;
        }

        int pointCount;
        is >> pointCount;

        map.create(pointCount);

        bob_vertex *ch;
        for(int i=0; i < pointCount; i++){
                ch=new bob_vertex();
                if(ch->load(is)==false){
                        error(ch->errorCode, "point[%d]: %s", i, bob_traslate_error(ch->errorCode));
                        delete ch;
                        return false;
                }
                map.addPoint(ch);

        }

        is >> hdr;
        if(hdr!=HDR_END)
                error(e_badEndHeader);

        return hdr==HDR_END && !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_vertices::toFile(obinaryfilestream& os)
{
        os << HDR_BEGIN;

        if(new_vertices.size()){
                os << (int)new_vertices.size();
                for(VertexIterator &it=new_vertices.begin(); it!=new_vertices.end(); ++it){
                        it->toFile(os);
                }
        }
        else{
                os << (int)map.pointsSize();
                for(bob_point_map::iterator &it=map.begin(); it!=map.end(); ++it){
                        it->toFile(os);
                }
        }
        os << HDR_END;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_vertices::toFile(otextfilestream& os, const Settings& settings)
{
        if(settings.rawMode())
                return outputRaw(os);
        else
                return outputBOD(os);
}
//---------------------------------------------------------------------------------
bool bob_vertices::outputBOD(otextfilestream& os)
{
        int oldf=os.flags();
        int i=0;
        os << noSemicolons << "// beginning of points (" << (int)map.uniquePointsSize() << ')' << endl << autoSemicolons;

        bob_vertex *p;
        while(p=map.nextUniquePoint()){
                p->toFile(os, i++);
        }

        os << noSemicolons << "-1; -1; -1; // points end" << endl << endl;

        os.flags(oldf);
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_vertices::outputRaw(otextfilestream& os)
{
        int oldf=os.flags();
        int i=0;

        os << noSemicolons << "// Vertices begin (" << (int)map.pointsSize() << " - " << (int)map.uniquePointsSize() << " unique)" << endl << autoSemicolons;
        for(bob_point_map::iterator &it=map.begin(); it!=map.end(); ++it, i++){
                os << autoSemicolons
                << it->x << it->y << it->z;

                if(it->hasTextureCoords()){
                        os << noSemicolons << "\t\tUV: " << autoSemicolons
                        << it->textureCoords.x << it->textureCoords.y;
                }
                
                if(it->flags & bob_vertex::FLAG_WEIRD_STUFF){
                        os << noSemicolons << "\tWeird coords: " << autoSemicolons
                        << it->weirdCoords.x << it->weirdCoords.y;
                }
                
                os << noSemicolons << "\tNormal: " << autoSemicolons
                << it->normalVector.x << it->normalVector.y << it->normalVector.z
                << noSemicolons << "\tSGBits: " << it->sgbits;

                os << noSemicolons << " // " << i << endl;
        }

        os << noSemicolons << "-1; -1; -1; // Vertices end" << endl << endl;
        
        os.flags(oldf);
        return os.good();
}
//---------------------------------------------------------------------------------
// POINT - object
bool bob_vertex::load(ibinaryfilestream& is)
{
        /*
                possible types
                0x1F - normal + UV + 2 unk values
                0x1B - normal + UV
                0x19 - normal
        */

        is >> flags >> *(vertex*)this;
        
        if((flags & FLAG_DEFAULT)!=FLAG_DEFAULT){
                errorCode=e_unkPointFlags;
                return false;
        }

        if(hasTextureCoords())
                is >> textureCoords;
        
        if(flags & FLAG_WEIRD_STUFF)
                is >> weirdCoords;

        is >> normalVector >> sgbits;

        if(is.fail())
                errorCode=e_notEnoughData;

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_vertex::toFile(obinaryfilestream& os)
{
        // set the default bits before outputting
        flags|=FLAG_DEFAULT;

        os << flags << *(dynamic_cast<vertex*>(this));
        if(hasTextureCoords())
                os << textureCoords;

        if(flags & FLAG_WEIRD_STUFF)
                os << weirdCoords;
                
        os << normalVector << sgbits;

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_vertex::toFile(otextfilestream& os, int idx)
{
        // autoSemicolons is set by parent (points)

        int coords[3];
        getBodCoords(coords);

        os << coords[0] << coords[1] << coords[2];

        os << noSemicolons << "// " << idx << endl << autoSemicolons;
        return os.good();
}
//---------------------------------------------------------------------------------
// X3 vertex_data_record
bool bob_x3vertex_data_record::load(ibinaryfilestream& is)
{
        is >> pointIndex >> tangent >> unk;
        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_x3vertex_data_record::toFile(obinaryfilestream& os)
{
        os << pointIndex << tangent << unk;
        return os.good();
}
//---------------------------------------------------------------------------------
/*int compare_x3vertex_rec(const void *a, const void *b)
{
        const bob_x3vertex_data_record *rec1=*((bob_x3vertex_data_record**)a), *rec2=*((bob_x3vertex_data_record**)b);
        return rec1->pointIndex - rec2->pointIndex;
}*/
//---------------------------------------------------------------------------------
// X3 uv list
bool bob_x3vertex_data::load(ibinaryfilestream& is)
{
        int count;
        bob_x3vertex_data_record *rec;

        is >> count;
        reserve(count);
        for(int i=0; i < count; i++){
                rec=new bob_x3vertex_data_record();
                if(!rec->load(is)){
                        delete rec;
                        return false;
                }
                push_back(rec);
        }
        
        /* 
                there should never be the need to compare the data, because they are written in the order of vertices,
                and thus their vertex indexes are always in sequence
        */
        //sort(compare_x3vertex_rec);

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_x3vertex_data::toFile(obinaryfilestream& os)
{
        os << (int) size();
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os);
        }
        return os.good();
}
//---------------------------------------------------------------------------------

// FACE LIST
bool bob_face_list::load(ibinaryfilestream& is, bool x3data)
{
        int count;

        is >> materialIndex >> count;

        for(int i=0; i < count; i++){
                bob_face *face=new bob_face();
                if(face->load(is)==false){
                        error(face->errorCode, "face[%d]: %s", i, bob_traslate_error(face->errorCode));
                        delete face;
                        return false;
                }
                push_back(face);
        }

        if(x3data && x3vertexData.load(is)==false) return false;

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_face_list::toFile(obinaryfilestream& os, const bob_vertices& vertices, bool x3data)
{
        os << materialIndex << (int)size();
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os);
        }

        if(x3data)
                saveVertexTangents(os, vertices);

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_face_list::saveVertexTangents(obinaryfilestream& os, const bob_vertices& vertices)
{
        // the still unknown God damned vector
        vector vec1(0,0,1); // default value
        vector vec0(0,0,0); // zero value
        
        bool *vert_map=new bool[vertices.new_vertices.size()];
        memset(vert_map, 0, vertices.new_vertices.size() * sizeof(bool));
        
        size_t count_pos=os.tell();
        os << 123; // dummy count
        
        int idx, count=0;
        for(iterator &it=begin(); it!=end(); ++it){
                for(int i=0; i < 3; i++){
                        idx=it->values[i];
                        if(vert_map[idx]==false) {
                                vector &tan=vertices.new_vertices[idx]->tangentVector;
                                os << idx << tan << (tan.isZero() ? vec0 : vec1);
                                vert_map[idx]=true;
                                count++;
                        }
                }
        }
        delete[] vert_map;

        // write the real count
        size_t lastp=os.tell();
        os.seek((int)count_pos, mystream::stream_base::seek_begin);
        os << count;
        os.seek((int)lastp, mystream::stream_base::seek_begin);

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_face_list::outputX3VertexDataRaw(otextfilestream& os)
{
        if(x3vertexData.size()==0) return true;
        
        int old=os.flags();
        os << noSemicolons << "// Material " << materialIndex << endl;
        os << "// Material; point index; tangent; unknown" << endl << autoSemicolons;
        for(bob_x3vertex_data::const_iterator &it=x3vertexData.begin(); it!=x3vertexData.end(); ++it){
                os << materialIndex << it->pointIndex << it->tangent << it->unk << endl;
        }
        os << endl;
        os.flags(old);
        return os.good();
}
//---------------------------------------------------------------------------------
/*
bool bob_face_list::computeAndOutputTangents(otextfilestream& os, const bob_point_map& pointMap)
{
        vertex v1, v2, v3;
        geometry::point2d<double> w1,w2,w3;
        int i1,i2,i3;
        
        vector *tan1, *tan2;
        
        tan1=new vector[pointMap.pointsSize() * 2];
        tan2=tan1 + pointMap.pointsSize();
        
        for(iterator &it=begin(); it!=end(); ++it){
                
                i1=it->values[0]; i2=it->values[1]; i3=it->values[2];
                v1=*pointMap[i1];
                v2=*pointMap[i2];
                v3=*pointMap[i3];
                
                w1=pointMap[i1]->textureCoords;
                w2=pointMap[i2]->textureCoords;
                w3=pointMap[i3]->textureCoords;
                
                double x1 = v2.x - v1.x;
    double x2 = v3.x - v1.x;
    double y1 = v2.y - v1.y;
    double y2 = v3.y - v1.y;
    double z1 = v2.z - v1.z;
    double z2 = v3.z - v1.z;
    
    double s1 = w2.x - w1.x;
    double s2 = w3.x - w1.x;
    double t1 = w2.y - w1.y;
    double t2 = w3.y - w1.y;
    
    double cp=s1 * t2 - s2 * t1;
    
    if(cp!=0) {
                        double r = 1.0 / cp;
                        vector sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
                                                        (t2 * z1 - t1 * z2) * r);
                        vector tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
                                                        (s1 * z2 - s2 * z1) * r);
                        
                        tan1[i1] += sdir;
                        tan1[i2] += sdir;
                        tan1[i3] += sdir;
            
                        tan2[i1] += tdir;
                        tan2[i2] += tdir;
                        tan2[i3] += tdir;
                }
        }

        int oldf=os.flags();
        
        os << noSemicolons << "// vertex tangents" << autoSemicolons << endl;
        
        int i=0;
        for(bob_point_map::const_iterator &it=pointMap.begin(); it!=pointMap.end(); ++it, i++){
                const vector& n=it->normalVector;
                const vector& t=tan1[i];
                const vector& bt=tan2[i];
                
                // Gram-Schmidt orthogonalize
                vector tangent = (t - n * n.dot(t)).normalize();
                
                os << i << tangent << endl;
                
        }
        
        delete[] tan1;
        
        os.flags(oldf);
        
        return os.good();
}*/
//---------------------------------------------------------------------------------
// PARTS - section PART
bool bob_parts::load(ibinaryfilestream& is)
{
        int hdr, count;

        is >> hdr;
        if(hdr!=HDR_BEGIN){
                error(e_badHeader);
                return false;
        }

        is >> count;

        bob_part *part;
        for(int i=0; i < count; i++){
                part=new bob_part();
                if(!part->load(is)){
                        for(ErrorIterator &it=part->errors.begin(); it!=part->errors.end(); ++it){
                                error(it->severity, it->code, "part[%d]->%s", i, it->text);
                        }
                        delete part;
                        return false;
                }
                push_back(part);
        }

        is >> hdr;
        if(hdr!=HDR_END)
                error(e_badEndHeader);

        return hdr==HDR_END && !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_parts::toFile(obinaryfilestream& os, const bob_vertices& vertices)
{
        os << HDR_BEGIN << (int) size();
        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os, vertices);
        }
        os << HDR_END;

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_parts::toFile(otextfilestream& os, const Settings& settings, const bob_materials *materials, const bob_point_map *pointMap)
{
        int i=1;
        for(iterator &it=begin(); it!=end(); ++it, ++i){
                if(it->toFile(os, settings, materials, *pointMap, i)==false){
                        for(bob_part::ErrorIterator &e_it=it->errors.begin(); e_it!=it->errors.end(); ++e_it){
                                error(e_it->severity, e_it->code, "Part[%d]->%s", i, e_it->text);
                        }
                        return false;
                }
        }
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_part_collision_box::load(ibinaryfilestream& is)
{
        is >> sphereOffset >> sphereDiameter;
        is >> boxOffset >> boxSize;
        
        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_part_collision_box::toFile(obinaryfilestream& os)
{
        os << sphereOffset << sphereDiameter;
        os << boxOffset << boxSize;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_part_collision_box::toFile(otextfilestream& os)
{
        int old=os.flags();

        os << noSemicolons;
        os << "/! COLLISION_BOX: " << autoSemicolons;
        
        os << boxOffset.x << boxOffset.y << boxOffset.z;
        os << sphereDiameter << boxSize.x << boxSize.y << boxSize.z;
        
        os << noSemicolons << "!/" << autoSemicolons << endl;

        os.flags(old);
        return os.good();
}
//---------------------------------------------------------------------------------
// PARTS - object

bool bob_part::load(ibinaryfilestream& is)
{
        short count;

        is >> flags >> count;

        for(int i=0; i < count; i++){
                bob_face_list *faces=new bob_face_list();
                if(faces->load(is, (flags & FLAG_X3) > 0)==false){
                        for(ErrorIterator &it=faces->errors.begin(); it!=faces->errors.end(); ++it){
                                error(it->severity, it->code, "face list[%d]->%s", i, it->text);
                        }
                        delete faces;
                        return false;
                }
                push_back(faces);
        }

        if(flags & FLAG_X3)
                collisionBox.load(is);

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_part::toFile(obinaryfilestream& os, const bob_vertices& vertices)
{
        os << flags << (short)size();

        for(iterator &it=begin(); it!=end(); ++it){
                it->toFile(os, vertices, (flags & FLAG_X3) > 0);
        }

        if(flags & FLAG_X3)
                collisionBox.toFile(os);

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_part::toFile(otextfilestream& os, const Settings& settings, const bob_materials *materials, const bob_point_map& pointMap, int idx)
{
        if(settings.rawMode())
                return outputRaw(os, pointMap, idx);
        else
                return outputBOD(os, settings, materials, pointMap, idx);
}
//---------------------------------------------------------------------------------
bool bob_part::outputBOD(otextfilestream& os, const Settings& settings, const bob_materials *materials, const bob_point_map& pointMap, int idx)
{
        bob_face *face;
        const bob_vertex *pnt;
        int line=0;
        int materialIndex;

        int oldf=os.flags();

        os << noSemicolons << "// ----- part " << idx << " (" << (int)numFaces() << " faces) -----" << endl << autoSemicolons;
        for(iterator &facelist_it=begin(); facelist_it!=end(); ++facelist_it){
                materialIndex=facelist_it->materialIndex;

                for(bob_face_list::iterator &face_it=facelist_it->begin(); face_it!=facelist_it->end(); ++face_it, ++line){
                        face=*face_it;

                        os << autoSemicolons << materialIndex;
                        for(int i=0; i < 3; i++){
                                os << pointMap.uniqueIndex(face->values[i]);
                        }

                        pnt=pointMap[face->values[0]];
                        if(pnt==NULL){
                                error(e_pointNotFound);
                                return false;
                        }
                        int SGBits=pnt->sgbits;
                        int magic = 1; // bit 1 is always set

                        /*
                                meaning of "magic" number:
                                -25: smoothing bits and UV coords
                                -17: smoothing bits and no UV coords
                                -9: no smoothing and UV coords
                                -1: no smoothing and no UV coords

                                --obsolete----------------------
                                old text from Checker's sources:
                                if there are no materials used, magic is -17
                                if there are materials and last value of the first point is non zero,
                                magic is -25, otherwise it's -9
                        */

                        const bob_vertex *points_ar[3];
                        for(int i=0; i < 3; i++){
                                pnt=pointMap[face->values[i]];
                                if(pnt==NULL){
                                        error(e_pointNotFound);
                                        return false;
                                }
                                points_ar[i]=pnt;
                        }

                        bool bHasUV=false;
                        for(int i=0; i < 3; i++){
                                if(bHasUV=points_ar[i]->hasTextureCoords())
                                        break;
                        }

                        if(bHasUV)
                                magic|=BOD_FLAG_UV;
                        if(SGBits)
                                magic|=BOD_FLAG_SGBITS;

                        os << (magic * -1);
                        if(SGBits!=0)
                                os << SGBits;

                        // we have uv coords, lets output them
                        if(bHasUV){
                                for(int i=0; i < 3; i++){
                                        
                                        os << points_ar[i]->textureCoords; // correct output
                                }
                                
                                os << noSemicolons << "/! ";
                                
                                outputNormals(os, points_ar);

                                if(settings.extraPntInfo())
                                        outputExtraPtInfo(os, points_ar);
                                
                                os << noSemicolons << "!/ ";
                        }
                        os << noSemicolons << "// " << line << endl;
                }
        }

        // x3 data
        if(flags & FLAG_X3){
                collisionBox.toFile(os);
        }

        int bit=1;
        char str[21];
        for(int i=19; i >= 0; i--){
                str[i]=flags & bit ? '1' : '0';
                bit<<=1;
        }
        str[sizeof(str)-1]=0;

        os << autoSemicolons  << "-99" << str << noSemicolons << "// part " << idx << " end" << endl;

        os.flags(oldf);

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_part::outputX3VertexData(otextfilestream& os, const bob_point_map& pointMap)
{
        int old=os.flags();
        
        os << endl << noSemicolons 
        << "// X3 vertex data (tangent and <unknown>)" << endl
        << autoSemicolons;

        int i=1;
        for(iterator &facelist_it=begin(); facelist_it!=end(); ++facelist_it, i++){
                facelist_it->outputX3VertexDataRaw(os);
                
                //facelist_it->computeAndOutputTangents(os, pointMap);
        }
        os.flags(old);
        
        return true;
}
//---------------------------------------------------------------------------------
void bob_part::outputNormals(otextfilestream& os, const bob_vertex *points[])
{
        int oldf=os.flags();
        bool bSame=true;
        for(int i=1; i < 3; i++){
                bSame&=points[i]->normalVector == points[i - 1]->normalVector;
        }

        os << noSemicolons << "N: { " << autoSemicolons;
        
        for(int i=0; i < (bSame ? 1 : 3); i++){
                os << points[i]->normalVector;
        }
        os << noSemicolons << "} ";
        os.flags(oldf);
}
//---------------------------------------------------------------------------------
// those are the unknown values stored in point after texture coordinates
// I place them after bod part definition and behind comment, so they are invisible to
// bod viewer and x2
void bob_part::outputExtraPtInfo(otextfilestream& os, const bob_vertex *points[])
{
        int oldf=os.flags();

        bool bInfo=false;
        for(int i=0; i < 3; i++){
                bInfo|=(points[i]->flags & bob_vertex::FLAG_WEIRD_STUFF) > 0;
        }
        if(bInfo==false) return;
        
        os << noSemicolons << "XPINFO: ";
        for(int i=0; i < 3; i++){
                os << noSemicolons << "{ " << autoSemicolons
                << points[i]->weirdCoords
                << noSemicolons << "} ";
        }
        os.flags(oldf);
}
//---------------------------------------------------------------------------------
// PART - object
bool bob_face::load(ibinaryfilestream& is)
{
        for(int i=0; i < 3; i++){
                is >> values[i];
        }
        is >> flags;

        if(is.fail())
                errorCode=e_notEnoughData;

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_face::toFile(obinaryfilestream& os)
{
        for(int i=0; i < 3; i++){
                os << values[i];
        }
        os << flags;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_body::load(ibinaryfilestream& is)
{
        is >> bodySize >> bodyFlags;
        if(peek(is)==bob_bones::HDR_BEGIN){
                if(!bones.load(is)){
                        for(ErrorIterator &it=bones.errors.begin(); it!=bones.errors.end(); ++it){
                                error(it->code, "bones: %s", it->text);
                        }
                        return false;
                }
        }
        if(peek(is)==bob_vertices::HDR_BEGIN){
                if(vertices.load(is)==false){
                        for(ErrorIterator &it=vertices.errors.begin(); it!=vertices.errors.end(); ++it){
                                error(it->code, "vertices->%s", it->text);
                        }
                        return false;
                }
        }
        if(peek(is)==bob_weights::HDR_BEGIN){
                if(!weights.load(is)){
                        for(ErrorIterator &it=weights.errors.begin(); it!=weights.errors.end(); ++it){
                                error(it->code, "weights->%s", it->text);
                        }
                        return false;
                }
        }
        if(peek(is)==bob_parts::HDR_BEGIN){
                if(parts.load(is)==false){
                        for(ErrorIterator &it=parts.errors.begin(); it!=parts.errors.end(); ++it){
                                error(it->code, "parts->%s", it->text);
                        }
                        return false;
                }
        }
        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_body::toFile(obinaryfilestream& os)
{
        os << bodySize << bodyFlags;

        bones.toFile(os);
        vertices.toFile(os);
        weights.toFile(os);
        parts.toFile(os, vertices);

        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_body::toFile(otextfilestream& os, const Settings& settings, const bob_materials *materials, int idx)
{
        os << "// beginning of body " << idx << endl;
        os << autoSemicolons << bodySize << endl << endl;

        bones.toFile(os);

        vertices.toFile(os, settings);

        weights.toFile(os, &vertices.map);

        if(parts.toFile(os, settings, materials, &vertices.map)==false){
                for(bob_parts::ErrorIterator &e_it=parts.errors.begin(); e_it!=parts.errors.end(); ++e_it){
                        error(e_it->severity, e_it->code, "Parts->%s", e_it->text);
                }
                return false;
        }
        char str[17]; int bit=1;
        for(int i=15; i >= 0; i--){
                str[i]=bodyFlags & bit ? '1' : '0';
                bit<<=1;
        }
        str[sizeof(str)-1]=0;

        os << "-99" << str << noSemicolons << "// body " << idx << " end" << endl;

        return os.good();
}
//---------------------------------------------------------------------------------
// BONES
bool bob_bones::load(ibinaryfilestream& is)
{
        int hdr, count;
        is >> hdr;
        if(hdr!=HDR_BEGIN){
                error(e_badHeader);
                return false;
        }

        is >> count;
        char *name;
        for(int i=0; i < count; i++){
                is >> name;
                if(name==0){
                        error(e_notEnoughData);
                        delete[] name;
                        return false;
                }
                push_back(name);
        }
        is >> hdr;
        if(hdr!=HDR_END){
                error(e_badEndHeader);
                return false;
        }
        return true;
}
//---------------------------------------------------------------------------------
bool bob_bones::toFile(obinaryfilestream& os)
{
        if(size()==0) return true;

        os << HDR_BEGIN << (int) size();
        for(iterator &it=begin(); it!=end(); ++it){
                os << *it;
        }
        os << HDR_END;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_bones::toFile(otextfilestream& os)
{
        if(size()==0) return true;
        os << noSemicolons << "BONES: " << autoSemicolons << (int)size() << endl;
        for(iterator &it=begin(); it!=end(); ++it){
                os << *it << endl;
        }
        os << endl;
        return os.good();
}
//---------------------------------------------------------------------------------
// WEIGHTS - section
bool bob_weights::load(ibinaryfilestream& is)
{
        int hdr;
        is >> hdr;
        if(hdr!=HDR_BEGIN){
                error(e_badHeader);
                return false;
        }

        int count;
        is >> count;
        resize(count);

        bob_weight *ch;
        for(int i=0; i < count; i++){
                ch=new bob_weight();
                if(!ch->load(is)){
                        error(ch->errorCode, "weight[%d]: %s", i, bob_traslate_error(ch->errorCode));
                        delete ch;
                        return false;
                }
                (*this)[i]=ch;
        }
        is >> hdr;
        if(hdr!=HDR_END){
                error(e_badEndHeader);
                return false;
        }
        return true;
}
//---------------------------------------------------------------------------------
bool bob_weights::toFile(obinaryfilestream& os)
{
        if(new_weights.size()==0 && size()==0) return true;

        os << HDR_BEGIN;
        if(new_weights.size()){
                os << (int)new_weights.size();
                for(WeightList::iterator &it=new_weights.begin(); it!=new_weights.end(); ++it){
                        it->toFile(os);
                }
        }
        else{
                os << (int)size();
                for(iterator &it=begin(); it!=end(); ++it){
                        it->toFile(os);
                }
        }

        os << HDR_END;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_weights::toFile(otextfilestream& os, const bob_point_map *pointMap)
{
        if(size()==0) return true;
        os << noSemicolons << "WEIGHTS " << autoSemicolons << (int) pointMap->uniquePointsSize() << endl;
        int i=0;
        for(bob_point_map::const_index_iterator &it=pointMap->indexBegin(); it!=pointMap->indexEnd(); ++it){
                (*this)[*it]->toFile(os, i++);
        }
        os << autoSemicolons << "-1" << endl << endl;
        return os.good();
}
//---------------------------------------------------------------------------------
// WEIGHT - object
bool bob_weight::load(ibinaryfilestream& is)
{
        short count;
        value v;

        is >> count;

        for(int i=0; i < count; i++){
                is >> v.boneIdx >> v.boneCoefficient;
                values.push_back(v);
        }

        return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_weight::toFile(obinaryfilestream& os)
{
        os << (short) values.size();

        value v;
        for(iterator &it=values.begin(); it!=values.end(); ++it){
                v=*it;
                os << v.boneIdx << v.boneCoefficient;
        }
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_weight::toFile(otextfilestream& os, int idx)
{
        os << autoSemicolons;
        for(iterator &it=values.begin(); it!=values.end(); ++it){
                (*it).toFile(os);
        }
        os << noSemicolons << "-1; // " << idx << endl;
        return os.good();
}
//---------------------------------------------------------------------------------
bool bob_part::outputRaw(otextfilestream& os, const bob_point_map& pointMap, int idx)
{
        int oldf=os.flags();
        os << noSemicolons << "// Part " << idx << endl << "// " << (int)numFaces() << " faces" << endl;
        for(iterator& facelist_it=begin(); facelist_it!=end(); ++facelist_it){
                os << noSemicolons << "// Material " << facelist_it->materialIndex << endl << autoSemicolons;
                for(bob_face_list::iterator& face_it=facelist_it->begin(); face_it!=facelist_it->end(); ++face_it){
                        for(int i=0; i < 3; i++)
                                os << face_it->values[i];
                        os << face_it->flags;
                        
                        os << endl;
                }
        }
        
        bool bRes = true;
        
        if(flags & FLAG_X3) {
                bRes = outputX3VertexData(os, pointMap);
                
                os << noSemicolons << "Collision sphere: offset: " << autoSemicolons << collisionBox.sphereOffset.x << collisionBox.sphereOffset.y << collisionBox.sphereOffset.z << noSemicolons << "diameter: " << autoSemicolons << collisionBox.sphereDiameter << endl;
                os << noSemicolons << "Collision box: offset: " << autoSemicolons << collisionBox.boxOffset.x << collisionBox.boxOffset.y << collisionBox.boxOffset.z << noSemicolons << "size: " << autoSemicolons << collisionBox.boxSize.x << collisionBox.boxSize.y << collisionBox.boxSize.z << endl;
        }
        
        os.flags(oldf);
        return os.good() && bRes;
}
//---------------------------------------------------------------------------------