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;
}
//---------------------------------------------------------------------------------