Rev 114 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "bod_bob_parser.h"#include "../common/strutils.h"//---------------------------------------------------------------------------------bool bod_bob_parser::compile(token_stream& is, bob_dom_obinaryfilestream& os, bool bEmbedded){bob_dom_bob *bob=new bob_dom_bob(m_settings);bob=loadBOB(is, bEmbedded);if(bob)bob->toFile(os);delete bob;return bob!=NULL;}//---------------------------------------------------------------------------------#define ISMATERIAL(text) ((strcmp(text, "MATERIAL6")==0 || strcmp(text, "MATERIAL5")==0 || \strcmp(text, "MATERIAL3")==0 || strcmp(text, "MATERIAL")==0))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 tokenwhile((t=is.tok()) && t->type!=token::t_closeBracket){if(t->type!=token::t_text){error(t, S_Error, "Unexpected here: '%s'", t->getText());abort=true; break;}if(isinteger(t->text)) break;//if(strcmp(t->text, "MATERIAL5")==0 || strcmp(t->text, "MATERIAL3")==0 || strcmp(t->text, "MATERIAL")==0){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, partsif(!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_closeBracket)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)// set the bob type (setting from Settings takes precedence)/*if(bob){switch(m_settings->X3BOB()){case Settings::on:bob->bobType=bobX3;break;case Settings::off:bob->bobType=bobX2;break;default:if(bob->materials.size() && bob->materials.front()->type==bob_dom_material::mat6)bob->bobType=bobX3;elsebob->bobType=bobX2;}}*/return bob;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadMatRGB(token_stream& is, bob_dom_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_dom_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_dom_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;}elsereturn true;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadMat(bob_dom_material5 *mat, token_stream &is){do{int i;bob_dom_material5::rgb rgb;bob_dom_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_dom_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_dom_material::mat5){if(!loadMatPair(is, pair)) break;mat->lightMap=pair;}}return true;}while(false);return false;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadMat(bob_dom_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_dom_material6::Big::flag)bRes=loadMaterial6Big(mat, is);elsebRes=loadMaterial6Small(mat, is);}while(false);return bRes;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadMaterial6Small(bob_dom_material6 *mat, token_stream &is){int i;bob_dom_material1::rgb rgb;bob_dom_material1::pair int_pair;bob_dom_material6::Small::pair pair;mat->small=new bob_dom_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_dom_material6 *mat, token_stream &is){int count,i;bob_dom_material6::Big *big=new bob_dom_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, valuechar *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: // nobreakcase 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_dom_material5::materialType type;token *t=is.tok();if(t){if(strcmp(t->getText(), "MATERIAL6")==0)type=bob_dom_material::mat6;else if(strcmp(t->getText(), "MATERIAL5")==0)type=bob_dom_material::mat5;else if(strcmp(t->getText(), "MATERIAL3")==0)type=bob_dom_material::mat3;else if(strcmp(t->getText(), "MATERIAL")==0)type=bob_dom_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_dom_material *mat;switch(type) {case bob_dom_material::mat6:mat=new bob_dom_material6();bRes=loadMat((bob_dom_material6*)mat, is);break;default: // always mat5 because lower mats are not supported in binary formmat=new bob_dom_material5();mat->type=type;bRes=loadMat((bob_dom_material5*)mat, is);break;}if(bRes==false)delete mat;elsebob->materials.push_back(mat);return bRes;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadBody(bob_dom_bob *bob, token_stream& is){static const char *context="Body: ";bob_dom_body *body=bob->bodies.createChild();PointList list;deleteTempData();loadIntValue(is, &(body->bodySize), context);if(!loadBones(body, is)){bob->bodies.removeChild(--bob->bodies.end());return false;}if(!loadPoints(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 indexm_points.resize(list.size());int i=0;for(PointList::iterator &it=list.begin(); it!=list.end(); ++it, ++i){m_points[i]=*it;}list.clear();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 bodyif(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;}return true;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadPoints(bob_dom_body *body, token_stream& is, PointList& points){bool bEnd;int x, y, z;static const char *context="Point: ";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;}__asm {fild xfdiv bob_dom_point::multiplier;fistp xfild yfdiv bob_dom_point::multiplier;fistp yfild zfdiv bob_dom_point::multiplier;fistp z}points.push_back(new point_t(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'sbool bod_bob_parser::loadPart(bob_dom_body *body, token_stream& is){int matIdx;bool bRes=true;static const char *context="Parts: ";bool secUV=false;bob_dom_part *part=new bob_dom_part();loadIntValue(is, &matIdx, context);do{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) {secUV=true;if((bRes=loadX3PartValues(++is, *part))==false)break;if((bRes=loadSecondaryUVs(is, *part))==false)break;}loadIntValue(is, &matIdx, context);}while(matIdx!=-99 && is.good()); // -99 means end of partif(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);if(secUV)part->flags|=bob_dom_part::x3flag;if(bRes)body->parts.push_back(part);elsedelete 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'sbool bod_bob_parser::loadFace(int matIdx, bob_dom_body& body, bob_dom_part& part, token_stream& is){int indexes[3];int magic;int lastPntVal;double dtexX, dtexY;int texX, texY;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_points.size()){error(is.previous(), S_Error, "Point index out of range: point %d, index %d", i, indexes[i]);return false;}}elsereturn false;}bool bRes=true;// magic: -1 means no smoothing and no uv, -9 means no smoothing and uv// -17 means smoothing and no uv, -25 means smoothing and uvbRes&=loadIntValue(is, &magic, context);if(magic!=-9 && magic!=-1)bRes&=loadIntValue(is, &lastPntVal, context);elselastPntVal=0;if(bRes==false) return false;bob_dom_point *point;bob_dom_face *face=new bob_dom_face();bob_dom_point *points_ar[3];int magic_ar[3];for(int i=0; i < 3; i++){if(magic!=-17 && magic!=-1){loadDoubleValue(is, &dtexX, context);bRes&=loadDoubleValue(is, &dtexY, context);// this is like texX=(int)(dtexX / bob_dom_part::multiplier) with rounding 'to nearest'__asm {fld dtexXfdiv bob_dom_part::multiplierfistp texXfld dtexYfdiv bob_dom_part::multiplierfistp texY}}else{texX=0;texY=0;}texture_coords_t *coords;texture_coord_t *coord=0;coords=m_points[indexes[i]]->findTextureCoords(lastPntVal);// I can't remember why -9 is duplicated - it is because it does not have smoothing?// should -1 be also here?if(magic!=-9 /*&& magic!=-1 ?? */){coord=coords->findCoords(texX, texY);}if(coord==NULL){coord=new texture_coord_t();coord->x=texX;coord->y=texY;coords->push_back(coord);point=new bob_dom_point();point->x=m_points[indexes[i]]->x;point->y=m_points[indexes[i]]->y;point->z=m_points[indexes[i]]->z;point->textureCoords.left=texX;point->textureCoords.right=texY;point->values.push_back(lastPntVal);coord->index=(int)body.points.new_points.size();body.points.new_points.push_back(point);points_ar[i]=point; // this will be used to fetch values found in intruction block// now insert the appropriate weightif(m_weights.size())body.weights.new_weights.push_back(new bob_dom_weight(*m_weights[indexes[i]]));}elsepoints_ar[i]=NULL;magic_ar[i]=magic;face->values[i]=coord->index;}face->values[3]=1;part.facelist(matIdx)->push_back(face);return loadSpecialValues(points_ar, magic_ar, is);}//---------------------------------------------------------------------------------// 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_dom_point *points[3], int magic[3], token_stream& is){// we must now fill the special coordinates to the points// they will be either specified in instruction block or we will use default valuesbob_dom_point dummy_pnt;dummy_pnt.values.push_back(1);token *t=is.tok();if(t && t->type==token::t_openInstrBlock){++is;for(int i=0; i < 3; i++){// if thte point was already loaded it is null, but we must still consume the values// and check errors so I load them to dummy pointif(points[i]!=NULL){if(!loadSpecialPointValues(is, points[i]))return false;}else{if(!loadSpecialPointValues(is, &dummy_pnt))return false;dummy_pnt.values.clear();// the "last value" must be there otherwise loadSpecialPointValues would faildummy_pnt.values.push_back(1);}}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;}// use default valueselse{int count;for(int i=0; i < 3; i++){if(points[i]==NULL) continue;bob_dom_point::iterator last=--points[i]->values.end();// megic ==-17 means we are not using mateial so we can use the smallest point type 0x19if(magic[i]==-17){points[i]->type=0x19;count=1;}else{points[i]->type=0x1B;count=3;}for(int j=0; j < count; j++){points[i]->values.insert(last, 0);}}}return true;}//---------------------------------------------------------------------------------// load one block from extra info in instruction block// example: { 1; 2; 3; }bool bod_bob_parser::loadSpecialPointValues(token_stream& is, bob_dom_point *point){double d;int v;static const char *context="Part - Extra point info: ";// point already has the "last point value" pushed into list, so we must// push new values before itbob_dom_point::iterator &last=--point->values.end();token *t=is.tok();if(t==NULL || t->type!=token::t_openBracket){error(t ? t : is.previous(), S_Error, "Expected beginning of block after '%s'", is.previous()->getText());return false;}++is;while((t=is.tok())!=NULL && !(t->type==token::t_closeBracket || t->type==token::t_closeInstrBlock)){if(!loadDoubleValue(is, &d, context))return false;__asm{fld dfdiv bob_dom_part::multiplierfistp v}point->values.insert(last, v);}if(t==NULL || t->type!=token::t_closeBracket){if(t==NULL)error(is.previous(), S_Error, "Unexpected end of document found while loading extra point info. Do you miss the '}'?");elseerror(t, S_Error, "Unexpected '%s' found while loading extra point info", t->getText());return false;}switch(point->values.size()){case 2:point->type=0x19;break;case 4:point->type=0x1B;break;case 6:point->type=0x1F;break;default:error(t, S_Error, "Invalid number of values in extra point info block (%d) must be 1, 3 or 5", point->values.size() - 1);return false;}++is;return true;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadBones(bob_dom_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 bodybool bod_bob_parser::loadWeights(bob_dom_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_dom_weight *w;m_weights.resize(count, 0);for(int i=0; i < count; i++){w=new bob_dom_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_dom_weight::value val;val.boneIdx=v;__asm{// val.boneCoefficient = bob_dom_weight::value::multiplier * dfild bob_dom_weight::value::multiplierfmul dfistp 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::loadSecondaryUVs(token_stream& is, bob_dom_part& part){bool bRes;do{if((bRes=loadSecondaryUVRecord(is, part))==false) break;}while(is.tok() && is.tok()->type!=token::t_closeInstrBlock);if(bRes){bRes=(is.tok() && is.tok()->type==token::t_closeInstrBlock);if(bRes==false)error(is.tok() ? is.tok() : is.previous(), S_Error, "Unexpected end of document found while loading secondary UV's. Do you miss the '!/'?");else++is;}return bRes;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadSecondaryUVRecord(token_stream& is, bob_dom_part& part){int matIdx, pointIdx;bool bRes=false;double d;bob_face_list *faces;bob_x3uv *uv=new bob_x3uv();do{if(!loadIntValue(is, &matIdx, "")) break;if(!loadIntValue(is, &pointIdx, "")) break;for(int i=0; i < 6; i++){if((bRes=loadDoubleValue(is, &d, ""))==false) break;uv->values[i]=(float) d;}uv->idx=pointIdx;faces=part[matIdx];if(faces==NULL){error(is.tok(), S_Error, "Secondary UVs: Face list with material idx %d not found", matIdx);break;}faces->x3uvlist.push_back(uv);bRes=true;}while(0);if(bRes==false)delete uv;return bRes;}//---------------------------------------------------------------------------------bool bod_bob_parser::loadX3PartValues(token_stream& is, bob_dom_part& part){static char *context="X3 part values: ";static char *hdr="PART_VALUES_RAW";if(is.tok()==NULL || is.tok()->type!=token::t_text || strcmp(is.tok()->text, "PART_VALUES_RAW")!=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;int v;bool bRes;for(int i=0; i < 10; i++){if((bRes=loadIntValue(is, &v, context))==false)break;part.values[i]=v;}return bRes;}//---------------------------------------------------------------------------------