Subversion Repositories spk

Rev

Rev 1 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "bod_bob_parser.h"
#include "../common/strutils.h"

#define ISMATERIAL(text) ((strcmp(text, "MATERIAL6")==0 || strcmp(text, "MATERIAL5")==0 || \
                          strcmp(text, "MATERIAL3")==0 || strcmp(text, "MATERIAL")==0))

//---------------------------------------------------------------------------------

geometry::point2d<double> bod_bob_parser::uv_coord::NULL_COORDS(123456, 78910);
//---------------------------------------------------------------------------------
bool bod_bob_parser::compile(token_stream& is, obinaryfilestream& os, bool bEmbedded)
{
        bob_dom_bob *bob=loadBOB(is, bEmbedded);
        if(bob)
                bob->toFile(os);
        delete bob;
        return bob!=NULL;
}
//---------------------------------------------------------------------------------
bob_dom_bob * bod_bob_parser::loadBOB(token_stream& is, bool bEmbedded)
{
        token *t;
        bob_dom_bob *bob=new bob_dom_bob(m_settings);
        bool abort=false;

        while((t=is.tok()) && t->type==token::t_hdrinfo){
                if(bob->info.text()!=0)
                        error(is.tok(), S_Warning, "Header info already defined, last value will be used");
                bob->info.text(t->text);
                ++is;
        }

        // first load material definitions, break on first numeric token
        while((t=is.tok()) && t->type!=token::t_closeCrBracket){
                if(t->type!=token::t_text){
                        error(t, S_Error, "Unexpected here: '%s'", t->getText());
                        abort=true; break;
                }
                if(isinteger(t->text)) break;

                if(ISMATERIAL(t->text)){
                        if(!loadMaterial(bob, is)){
                                abort=true; break;
                        }
                }
                else{
                        error(t, S_Error, "Unexpected here '%s'", t->getText());
                        abort=true; break;
                }
        }

        // now load the body definition: body size, points, parts
        if(!abort){
                m_materials=&(bob->materials);
                do{
                        if(abort=(loadBody(bob, is)==false)) break;

                        if(bEmbedded){
                                if(is.tok()==NULL){
                                        error(is.previous(), S_Error, "Unexpected end of document found while loading embedded BOB - do you miss the '}'?");
                                        abort=true; break;
                                }
                                else if(is.tok()->type==token::t_closeCrBracket)
                                        break;
                        }
                        else if(is.eof())
                                break;
                }
                while(1); // while what? while '}' if embedded or while eof if standalone
        }

        if(abort){
                delete bob;
                bob=NULL;
        }

        ++is; // advance past the close bracket (if embedded)

        return bob;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMatRGB(token_stream& is, bob_material5::rgb& rgb)
{
        int vals[3];
        token *t;
        for(int i=0; i < 3; i++){
                t=loadValue(is);
                if(t==NULL) return false;
                if(!isinteger(t->text)){
                        error(t, S_Error, "Expected integer value at position %d in RBG", i);
                        return false;
                }
                vals[i]=atoi(t->text);
        }
        rgb.r=vals[0];
        rgb.g=vals[1];
        rgb.b=vals[2];
        return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMatPair(token_stream& is, bob_material5::pair& pair)
{
        int vals[2];

        for(int i=0; i < 2; i++){
                if(!loadIntValue(is, vals + i))
                        return false;

        }
        pair.value=vals[0];
        pair.strength=vals[1];
        return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMatPair(token_stream& is, bob_material6::Small::pair& pair)
{
        int i;
        if((pair.texture=loadString(is, "texture name"))==0) return false;
        if(strcmp(pair.texture, "NULL")==0){
                delete pair.texture; pair.texture=0;
        }
        if(loadIntValue(is, &i, "texture strength")==false) return false;
        pair.strength=i;
        return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::checkImmediatePlacement(const bod_bob_parser::token *token1, const bod_bob_parser::token *token2)
{
        if(token1->line!=token2->line || (token1->col + strlen(token1->getText())!=token2->col)){
                error(token2, S_Error, "'%s' must be placed directly after '%s'", token2->getText(), token1->getText());
                return false;
        }
        else
                return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMat(bob_material5 *mat, token_stream &is)
{
        do{
                int i;
                bob_material5::rgb rgb;
                bob_material5::pair pair;

                if(!loadIntValue(is, &i)) break;
                mat->index=i;
                if(!loadIntValue(is, &i)) break;
                mat->textureID=i;
                if(!loadMatRGB(is, rgb)) break;
                mat->ambient=rgb;
                if(!loadMatRGB(is, rgb)) break;
                mat->diffuse=rgb;
                if(!loadMatRGB(is, rgb)) break;
                mat->specular=rgb;
                if(mat->type!=bob_material::mat1) {
                        if(!loadIntValue(is, &i)) break;
                        mat->transparency=i;
                        if(!loadIntValue(is, &i)) break;
                        mat->selfIllumination=i;
                        if(!loadMatPair(is, pair)) break;
                        mat->shininess=pair;
                        if(!loadIntValue(is, &i)) break;
                        mat->destinationBlend=i!=0;
                        if(!loadIntValue(is, &i)) break;
                        mat->twoSided=i!=0;
                        if(!loadIntValue(is, &i)) break;
                        mat->wireframe=i!=0;
                        if(!loadIntValue(is, &i)) break;
                        mat->textureValue=i;
                        if(!loadMatPair(is, pair)) break;
                        mat->enviromentMap=pair;
                        if(!loadMatPair(is, pair)) break;
                        mat->bumpMap=pair;

                        if(mat->type==bob_material::mat5){
                                if(!loadMatPair(is, pair)) break;
                                mat->lightMap=pair;
                        }
                }
                return true;
        }
        while(false);
        return false;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMat(bob_material6 *mat, token_stream &is)
{
        int i; bool bRes=false;
        do{
                if(!loadIntValue(is, &i)) break;
                        mat->index=i;

                if(!loadIntValue(is, &i)) break;
                        mat->flags=i;

                if(mat->flags==bob_material6::Big::flag)
                        bRes=loadMaterial6Big(mat, is);
                else
                        bRes=loadMaterial6Small(mat, is);
        }
        while(false);

        return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMaterial6Small(bob_material6 *mat, token_stream &is)
{
        int i;
        bob_material1::rgb rgb;
        bob_material1::pair int_pair;
        bob_material6::Small::pair pair;

        mat->small=new bob_material6::Small();
        do{
                if((mat->small->textureFile=loadString(is, "texture name: "))==0) break;
                if(strcmp(mat->small->textureFile, "NULL")==0)
                        { delete[] mat->small->textureFile; mat->small->textureFile=0; }
                if(!loadMatRGB(is, rgb)) break;
                mat->small->ambient=rgb;
                if(!loadMatRGB(is, rgb)) break;
                mat->small->diffuse=rgb;
                if(!loadMatRGB(is, rgb)) break;
                mat->small->specular=rgb;
                if(!loadIntValue(is, &i)) break;
                mat->small->transparency=i;
                if(!loadIntValue(is, &i)) break;
                mat->small->selfIllumination=i;
                if(!loadMatPair(is, int_pair)) break;
                mat->small->shininess=int_pair;
                if(!loadIntValue(is, &i)) break;
                mat->small->destinationBlend=i!=0;
                if(!loadIntValue(is, &i)) break;
                mat->small->twoSided=i!=0;
                if(!loadIntValue(is, &i)) break;
                mat->small->wireframe=i!=0;
                if(!loadIntValue(is, &i)) break;
                mat->small->textureValue=i;
                if(!loadMatPair(is, pair)) break;
                mat->small->enviromentMap=pair;
                if(!loadMatPair(is, pair)) break;
                mat->small->bumpMap=pair;

                if(!loadMatPair(is, pair)) break;
                mat->small->lightMap=pair;

                if(!loadMatPair(is, pair)) break;
                mat->small->map4=pair;
                if(!loadMatPair(is, pair)) break;
                mat->small->map5=pair;

                short flags=0;
                if(mat->small->destinationBlend) flags&=0x2;
                if(mat->small->twoSided) flags&=0x10;
                if(mat->small->wireframe) flags&=0x8;
                mat->flags=flags;

                return true;
        }
        while(false);
        return false;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMaterial6Big(bob_material6 *mat, token_stream &is)
{
        int count,i;

        bob_material6::Big *big=new bob_material6::Big();
        mat->big=big;

        if(!loadIntValue(is, &i, "Technique idx: ")) return false;
        big->technique=i;

        if((big->effect=loadString(is, "Effect name: "))==0) return false;

        material6_value *val;
        if(!loadIntValue(is, &count, "Value count: ")) return false;
        for(i=0; i < count; i++){
                if((val=loadMat6Value(is))==0) return false;
                big->values.push_back(val);
        }

        return true;
}
//---------------------------------------------------------------------------------
material6_value * bod_bob_parser::loadMat6Value(token_stream& is)
{
        bool bRes=false;
        // name, type, value
        char *name, *strtype;

        material6_value *val=new material6_value();

        token_stream::token *typepos;

        do{
                if((name=loadString(is, "Property name: "))==0) break;
                if(name[0]==0) {
                        error(is.tok(), S_Error, "Empty property name");
                        return false;
                }
                if((strtype=loadString(is, "Property type: "))==0) break;
                typepos=is.tok();

                int type=-1;

                for(int i=0; i < material6_value::typeNameCount(); i++){
                        if(strcmp(strtype, material6_value::typeName(i))==0) {
                                if(strtype[0]!=0)
                                        type=i;
                                break;
                        }
                }

                if(type==-1){
                        error(typepos, S_Error, "Unknown property type: \"%s\"", strtype);
                        break;
                }
                val->type=(material6_value::Type)type;
                val->name=name;
                name=0;

                double d;
                switch(val->type){
                        case material6_value::typeBool: // nobreak
                        case material6_value::typeLong:
                                bRes=loadIntValue(is, &val->val.i, "Property value: ");
                                break;
                        case material6_value::typeString:
                                bRes=((val->val.psz=loadString(is, "Property value: "))!=0);
                                break;
                        case material6_value::typeFloat:
                                bRes=loadDoubleValue(is, &d, "Property value: ");
                                val->val.f=(float)d;
                                break;
                        case material6_value::typeFloat4:
                                for(int i=0; i < 4; i++){
                                        if((bRes=loadDoubleValue(is, &d, "Property value"))==false) break;
                                        val->val.f4.f[i]=(float)d;
                                }
                                break;
                        default:
                                error(0, S_Error, "Don't know how to output type \"%s\"", strtype);
                                bRes=false;
                }
                if(bRes==false)
                        break;

                bRes=true;
        }
        while(0);

        delete[] name; delete[] strtype;

        if(bRes==false) {
                delete val;
                val=0;
        }

        return val;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMaterial(bob_dom_bob *bob, token_stream& is)
{
        bob_material5::materialType type;

        token *t=is.tok();

        if(t){
                if(strcmp(t->getText(), "MATERIAL6")==0)
                        type=bob_material::mat6;
                else if(strcmp(t->getText(), "MATERIAL5")==0)
                        type=bob_material::mat5;
                else if(strcmp(t->getText(), "MATERIAL3")==0)
                        type=bob_material::mat3;
                else if(strcmp(t->getText(), "MATERIAL")==0)
                        type=bob_material5::mat1;
        }

        if(t==NULL || t->type!=token::t_text){
                error(t ? t : is.previous(), S_Error, "Expected material header (MATERIAL, MATERIAL3, MATERIAL5 or MATERIAL6)");
                return NULL;
        }
        t=(++is).tok();
        if(t==NULL || t->type!=token::t_colon){
                error(t ? t : is.previous(), S_Error, "Expected ':' after '%s'", is.previous()->getText());
                return NULL;
        }
        if(!checkImmediatePlacement(is.previous(), is.tok()))
                return NULL;

        ++is;

        bool bRes;
        bob_material *mat;
        switch(type) {
                case bob_material::mat6:
                        mat=new bob_material6();
                        bRes=loadMat((bob_material6*)mat, is);
                        break;
                default: // always mat5 because lower mats are not supported in binary form
                        mat=new bob_material5();
                        mat->type=type;
                        bRes=loadMat((bob_material5*)mat, is);
                        break;
        }

        if(bRes==false)
                delete mat;
        else
                bob->materials.push_back(mat);

        return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadBody(bob_dom_bob *bob, token_stream& is)
{
        static const char *context="Body: ";
        bob_body *body=bob->bodies.createChild();
        VertexList list;

        deleteTempData();

        loadIntValue(is, &(body->bodySize), context);

        if(!loadBones(body, is)){
                bob->bodies.removeChild(--bob->bodies.end());
                return false;
        }
        if(!loadVertices(body, is, list)) {
                bob->bodies.removeChild(--bob->bodies.end());
                return false;
        }

        if(!loadWeights(body, is)){
                bob->bodies.removeChild(--bob->bodies.end());
                return false;
        }

        if(m_weights.size() && m_weights.size()!=list.size()){
                error(0, S_Error, "Number of Weights (%d) does not match number of Points (%d)", m_weights.size(), list.size());
                bob->bodies.removeChild(--bob->bodies.end());
                return false;
        }

        // move the points to array so we can access them with index
        m_vertices.resize(list.size());
        int i=0;
        for(VertexList::iterator &it=list.begin(); it!=list.end(); ++it, ++i){
                m_vertices[i]=*it;
        }
        list.clear();

        // reserve space in the vertex array (some of the vertices will be duplicated)
        body->vertices.new_vertices.reserve((size_t)(m_vertices.size() * 2));
        body->vertices.new_vertices.growby((size_t)(m_vertices.size() * 0.25));

        int endval;
        do{
                if(!loadPart(body, is)) {
                        bob->bodies.removeChild(--bob->bodies.end());
                        return false;
                }

                loadIntValue(is, &endval);
                if(endval!=-99)
                        ungetValue(is);
        }
        while(is.good() && endval!=-99); // -99 means end of body

        if(endval!=-99){
                error(is.previous(), S_Error, "Unexpected end of document found while loading Body. Do you miss the '-99'?");
                bob->bodies.removeChild(--bob->bodies.end());
                return false;
        }
        if(!flagsFromString(is, body->bodyFlags)){
                bob->bodies.removeChild(--bob->bodies.end());
                return false;
        }

        computeVertexTangents(*body);

        return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadVertices(bob_body *body, token_stream& is, VertexList& points)
{
        bool bEnd;
        int x, y, z;
        static const char *context="Vertex: ";

        do{
                loadIntValue(is, &x, context);
                loadIntValue(is, &y, context);
                if(!loadIntValue(is, &z, context)) return false;

                if(x==-1 && y==-1 && z==-1) {
                        bEnd=true;
                        break;
                }
                /* SameVertices will take care of translating the BOD coords to BOB format */
                points.push_back(new SameVertices(x, y, z));
        }
        while(is.good());

        if(!bEnd){
                error(is.previous(), S_Error, "Unexpected end of document found while loading Points. Do you miss the '-1;-1;-1;'?");
                return false;
        }

        return true;
}
//---------------------------------------------------------------------------------
// load part - bunch of faces/uv's
bool bod_bob_parser::loadPart(bob_body *body, token_stream& is)
{
        int matIdx;
        bool bRes=true;
        static const char *context="Parts: ";
        bool x3data=false;

        bob_part *part=new bob_part();

        loadIntValue(is, &matIdx, context);
        do{
                // no longer truth - negative number is direct texture ID of animated texture
                /*if(matIdx < 0)
                        { error(is.previous(), S_Error, "%sMaterial index must be >= 0", context); bRes=false; break; }
                */
                if(matIdx > 0 && ((size_t)matIdx > m_materials->size() - 1))
                        error(is.previous(), S_Warning, "%sMaterial index out of range: %d", context, matIdx);

                if((bRes=loadFace(matIdx, *body, *part, is))==false)
                        break;

                if(is.tok() && is.tok()->type==token::t_openInstrBlock) {
                        x3data = true;

                        if((bRes = loadCollisionBoxData(++is, *part)) == false)
                                break;
                }

                loadIntValue(is, &matIdx, context);
        }
        while(matIdx!=-99 && is.good()); // -99 means end of part

        if(bRes && matIdx != -99){
                error(is.previous(), S_Error, "Unexpected end of document found while loading Part. Do you miss the '-99'?");
                bRes = false;
        }

        if(bRes)
                bRes = flagsFromString(is, part->flags);

        // set x3 flag if either we loaded x3 part values or settings::writeX3Data is set
        if(x3data || m_settings->writeX3Data())
                part->flags|=bob_part::FLAG_X3;

        if(bRes)
                body->parts.push_back(part);
        else
                delete part;

        return bRes;
};
//---------------------------------------------------------------------------------
bool bod_bob_parser::flagsFromString(token_stream& is, int& flags)
{
        bool bRes;
        token *t=loadValue(is);

        flags=0;
        if(bRes=(t!=NULL)){
                int len=(int)strlen(t->text);
                int val=1;
                for(int i=len-1; i >= 0; i--, val<<=1 ){
                        switch(t->text[i]){
                                case '0':
                                        break;
                                case '1':
                                        flags|=val;
                                        break;
                                default:
                                        error(is.previous(), S_Error, "Invalid flags definition. Expected binary number");
                                        bRes=false;
                                        break;
                        }
                        if(bRes==false) break;
                }
        }
        return bRes;
}
//---------------------------------------------------------------------------------
// load face - actual face and its uv's
bool bod_bob_parser::loadFace(int matIdx, bob_body& body, bob_part& part, token_stream& is)
{
        int indexes[3];
        int magic;
        int sgbits;
        double dtexX, dtexY;
        static const char *context="Part: ";

        for(int i=0; i < 3; i++){
                if(loadIntValue(is, indexes + i, context)){
                        if(indexes[i] < 0 || (size_t)indexes[i] >= m_vertices.size()){
                                error(is.previous(), S_Error, "Point index out of range: point %d, index %d", i, indexes[i]);
                                return false;
                        }
                }
                else
                        return false;
        }

        bool bRes=true;

        /*
                original (old) meaning of the 'magic' number (now I'm using flags)
                magic: -1 means no smoothing and no uv, -9 means no smoothing and uv
                        -17 means smoothing and no uv, -25 means smoothing and uv
        */
        
        bRes&=loadIntValue(is, &magic, context);

        if(magic >= 0){
                error(is.tok(), S_Error, "Faces are written using polygons not triangles - cannot continue");
                return false;
        }

        magic*=-1;

        if(magic & bob_part::BOD_FLAG_SGBITS)
                bRes&=loadIntValue(is, &sgbits, context);
        else
                sgbits=0;

        if(bRes==false) return false;

        bob_vertex *vertex;
        bob_face *face=new bob_face();
        bob_vertex *points_ar[3];

        for(int i=0; i < 3; i++){
                if(magic & bob_part::BOD_FLAG_UV){
                        loadDoubleValue(is, &dtexX, context);
                        bRes&=loadDoubleValue(is, &dtexY, context);
                }
                else{
                        dtexX=dtexY=0;
                }

                /*
                        look (or create) for vertex with same coords and same sgbits
                */
                SameSG_UVs *SGgroup=m_vertices[indexes[i]]->findTextureCoords(sgbits);
                uv_coord *uv=NULL;
                
                /*
                        look for the same UV coords or use anything if no UV is specified
                */
                if(magic & bob_part::BOD_FLAG_UV)
                        uv=SGgroup->findUV(dtexX, dtexY);
                else 
                        uv=SGgroup->findNullUV();
                
                if(uv==NULL){
                        if(magic & bob_part::BOD_FLAG_UV)
                                uv=SGgroup->createUV(dtexX, dtexY);
                        else
                                uv=SGgroup->createNullUV();
                                
                        vertex=uv->createBOBVertex();
                        
                        uv->vertexIndex=(int)body.vertices.new_vertices.size();
                        body.vertices.new_vertices.push_back(vertex);

                        points_ar[i]=vertex; // this will be used to fetch values found in intruction block

                        // now insert the appropriate weight
                        if(m_weights.size())
                                body.weights.new_weights.push_back(new bob_weight(*m_weights[indexes[i]]));
                }
                else
                        points_ar[i]=NULL;

                face->values[i]=uv->vertexIndex;
        }
        face->flags=1; // always 1 (or 0)
        
        part.facelist(matIdx)->push_back(face);

        bRes = loadSpecialValues(points_ar, is);
        
        return bRes;
}
//---------------------------------------------------------------------------------
// load special values which may be "hidden" in instruction block after standard part definition
// example: /! { 1;2;3; } { 4;5;6; } { 7;8;9; } !/
bool bod_bob_parser::loadSpecialValues(bob_vertex *points[3], token_stream& is)
{
        bob_vertex dummy_pnt;

        token *t=is.tok();

        if(t && t->type==token::t_openInstrBlock){
                ++is;
                
                t=is.tok();
                while(t != NULL && t->type != token::t_closeInstrBlock){
                        if(t == NULL || t->type != token::t_text){
                                error(t ? t : is.previous(), S_Error, "Expected identifier in instruction block.");
                                return false;
                        }
                        
                        if(strcmp(t->text, "N") == 0){
                                if(!loadFaceNormal(is, points))
                                        return false;
                        }
                        else if(strcmp(t->text, "XPINFO") == 0){
                                t = (++is).tok();
                                if(t == NULL || t->type != token::t_colon){
                                        error(t ? t : is.previous(), S_Error, "Expected ':' after '%s'", is.previous()->getText());
                                        return false;
                                }       
                                ++is;
                                for(int i = 0; i < 3; i++){
                                        if(!loadSpecialPointValues(is, points[i]))
                                                return false;
                                }
                        }
                        else {
                                error(t, S_Error, "Unexpected identifier in instruction block: '%s'", t);
                                return false;
                        }
                        t=is.tok();
                }
                if(t==NULL || t->type!=token::t_closeInstrBlock){
                        error(t ? t : is.previous(), S_Error, "Expected end of instruction block '!/' after '%s'", is.previous());
                        return false;
                }
                ++is;
        }
        return true;
}
//---------------------------------------------------------------------------------
// load one block from extra info in instruction block
// example: { 1; 2; 3; }

// point can be null!
bool bod_bob_parser::loadSpecialPointValues(token_stream& is, bob_vertex *point)
{
        static const char *context="Part - Extra point info: ";

        token *t=is.tok();
        if(t==NULL || t->type!=token::t_openCrBracket){
                error(t ? t : is.previous(), S_Error, "Expected beginning of block after '%s'", is.previous()->getText());
                return false;
        }
        ++is;
        
        double x, y;
        
        if(!loadDoubleValue(is, &x, context))
                return false;
        if(!loadDoubleValue(is, &y, context))
                return false;

        if(point != NULL){
                point->weirdCoords.x = x;
                point->weirdCoords.y = y;
                
                point->flags|=bob_vertex::FLAG_WEIRD_STUFF;
        }

        if(is.tok()==NULL || is.tok()->type!=token::t_closeCrBracket){
                if(is.tok()==NULL)
                        error(is.previous(), S_Error, "Unexpected end of document found while loading extra point info. Do you miss the '}'?");
                else
                        error(t, S_Error, "Unexpected '%s' found while loading extra point info", t->getText());
                return false;
        }

        ++is;

        return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadBones(bob_body *body, token_stream& is)
{
        const token *t=is.tok();
        static const char *context2="Bones: ";
        static const char *context="Bone: ";

        if(!(t && t->type==token::t_text && strcmp(t->text, "BONES")==0))
                return true;

        t=(++is).tok();
        if(t==NULL || t->type!=token::t_colon){
                error(t ? t : is.previous(), S_Error, "Expected ':' after '%s'", is.previous()->getText());
                return false;
        }
        if(!checkImmediatePlacement(is.previous(), t))
                return false;

        int count;
        if(!loadIntValue(++is, &count, context2))
                return false;

        char *name;
        for(int i=0; i < count; i++){
                name=loadString(is, context);
                if(name==NULL) return false;
                body->bones.push_back(name);
        }

        return !is.fail();
}
//---------------------------------------------------------------------------------
// load the weights to temporary array. Weights from this array will then be inflated and added to body
bool bod_bob_parser::loadWeights(bob_body *body, token_stream& is)
{
        const token *t=is.tok();
        static const char *context="Weight: ";
        static const char *context2="Weights: ";

        if(!(t && t->type==token::t_text && strcmp(t->text, "WEIGHTS")==0))
                return true;

        int count;
        if(!loadIntValue(++is, &count, context2))
                return false;

        bool bRes=true;
        int v; double d;
        bob_weight *w;

        m_weights.resize(count, 0);

        for(int i=0; i < count; i++){
                w=new bob_weight();
                do{
                        if(!loadIntValue(is, &v, context))
                                { bRes=false; break; }
                        if(v==-1)
                                break;

                        if(v < 0 || (size_t)v >= body->bones.size()){
                                error(is.previous(), S_Error, "Weight: Bone index out of range (%d)", v);
                                { bRes=false; break; }
                        }

                        if(!loadDoubleValue(is, &d, context))
                                { bRes=false; break; }

                        bob_weight::value val;
                        val.boneIdx=v;

                        __asm{
                                /* 
                                        val.boneCoefficient = bob_weight::value::multiplier * d 
                                */
                                fild bob_weight::value::MULTIPLIER
                                fmul d
                                fistp val.boneCoefficient
                        }
                        w->values.push_back(val);
                }
                while(!is.fail());
                if(is.fail()){
                        error(is.previous(), S_Error, "Unexpected end of document found while loading Weights. Do you miss the '-1'?");
                        bRes=false;
                }
                if(bRes==false){
                        delete w;
                        break;
                }
                m_weights[i]=w;
        }
        if(bRes){
                if(!loadIntValue(is, &v, context2))
                        bRes=false;
                else if(v!=-1){
                        error(is.previous(), S_Error, "Expected '-1' after end of Weights");
                        bRes=false;
                }
        }
        return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadCollisionBoxData(token_stream& is, bob_part& part)
{
        static char *context="Collision box: ";
        static char *HDR="COLLISION_BOX";

        if(is.tok()==NULL || is.tok()->type!=token::t_text || strcmp(is.tok()->text, HDR)!=0) {
                error(is.tok() ? is.tok() : is.previous(), S_Error, "Expected '%s' here", HDR);
                return false;
        }
        if((++is).tok()==NULL || is.tok()->type!=token::t_colon){
                error(is.tok() ? is.tok() : is.previous(), S_Error, "Expected ':' after '%s'", HDR);
                return false;
        }

        ++is;

        loadDoubleValue(is, &part.collisionBox.boxOffset.x, "Box offset");
        loadDoubleValue(is, &part.collisionBox.boxOffset.y, "Box offset");
        if(!loadDoubleValue(is, &part.collisionBox.boxOffset.z, "Box offset"))
                return false;
                
        if(!loadDoubleValue(is, &part.collisionBox.sphereDiameter, "Sphere diameter"))
                return false;
                
        loadDoubleValue(is, &part.collisionBox.boxSize.x, "Box size");
        loadDoubleValue(is, &part.collisionBox.boxSize.y, "Box size");
        if(!loadDoubleValue(is, &part.collisionBox.boxSize.z, "Box size"))
                return false;
        
        part.collisionBox.sphereOffset = part.collisionBox.boxOffset;
        
        if(is.tok()==NULL || is.tok()->type!=token::t_closeInstrBlock){
                error(is.tok() ? is.tok() : is.previous(), S_Error, "Expected '!/' after end of Collision box");
                return false;
        }
        ++is;
        return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadFaceNormal(token_stream& is, bob_vertex *points[3])
{
        token *t;
        t=is.tok();
        if(t==NULL || t->type!=token::t_text || *t->text!='N'){
                error(t, S_Error, "Error loading face normal: expecting 'N:'");
                return false;
        }
        t=(++is).tok();
        if(t==NULL || t->type!=token::t_colon){
                error(t, S_Error, "Expecting ':' after '%'", is.previous()->getText());
                return false;
        }
        ++is;

        t = is.tok();
        if(t == NULL || t->type != token::t_openCrBracket){
                error(t ? t : is.previous(), S_Error, "Expected beginning of block after '%s'", is.previous()->getText());
                return false;
        }
        
        ++is;
        
        // buffer for normals - contains either 1 or 3 records
        normal_vector normals[3];
        
        int i;
        // for each vertex
        for(i = 0; i < 3; i++){
                double d[3];
                
                // load the vertex normal
                for(int j=0; j < 3; j++){
                        if(!loadDoubleValue(is, d + j, "Face normal: "))
                                return false;
                }
                normals[i] = normal_vector(d[0], d[1], d[2]);
                
                t = is.tok();
                // bail out if end of block was reached - case of short (3 numbers) normal block
                if(i == 0 && t && t->type == token::t_closeCrBracket)
                        break;
        }       
        int max = i > 0 ? 2 : 0;
        for(i = 0; i < 3; i++){
                if(points[i]!=NULL)
                        points[i]->normalVector = normals[__min(i, max)];
        }
        
        if(t == NULL || t->type != token::t_closeCrBracket){
                error(t ? t : is.previous(), S_Error, "Expected end of block after '%s'", is.previous()->getText());
                return false;
        }
        ++is;
        
        return true;
}
//---------------------------------------------------------------------------------
/*
        computation taken from C4 3D engine (http://www.terathon.com/code/tangent.php)
*/
void bod_bob_parser::computeVertexTangents(bob_body& body)
{
        typedef ext::array<bob_vertex*> vertex_array;
        
        bob_vertices::VertexArray &vertices=body.vertices.new_vertices;
        
        // stuff for the computation
        vertex v1, v2, v3; // vertices
        geometry::point2d<double> w1, w2, w3; // uv coords
        int i1, i2, i3; // vertex indexes
        
        vector *tmp_tangents=new vector[vertices.size()];
        
        // every part in body
        for(bob_parts::iterator &part_it=body.parts.begin(); part_it!=body.parts.end(); part_it++){
                // every face list in a part
                for(bob_part::iterator &facelist_it=part_it->begin(); facelist_it!=part_it->end(); facelist_it++){
                        // every face in face list
                        for(bob_face_list::iterator &face_it=facelist_it->begin(); face_it!=facelist_it->end(); ++face_it){
                                i1=face_it->values[0]; i2=face_it->values[1]; i3=face_it->values[2];
                                
                                v1=*vertices[i1]; v2=*vertices[i2]; v3=*vertices[i3];
                                w1=vertices[i1]->textureCoords; w2=vertices[i2]->textureCoords; w3=vertices[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);
                                        
                                        tmp_tangents[i1] += sdir;
                                        tmp_tangents[i2] += sdir;
                                        tmp_tangents[i3] += sdir;
                                }
                        }
                }
        }
        
        for(vertex_array::size_type i=0; i < vertices.size(); i++){
                const vector& n=vertices[(vertex_array::difference_type)i]->normalVector;
                const vector& t=tmp_tangents[i];
                
                if(!t.isZero()){
                        // Gram-Schmidt orthogonalize
                        vertices[(vertex_array::difference_type)i]->tangentVector = (t - n * n.dot(t)).normalize();
                }
        }
        
        delete[] tmp_tangents;
}
//---------------------------------------------------------------------------------