programming

so the object model is : sep 21

xml is just a file format. lets start with a save.

take the universe..

printTraverse(0,universe);

void printTraverse(int indent,gbDotable* obj)
{
dprintf(indent,"<%s id=/"%s/"\n",obj->getType(),obj->getName());

gbDotableArgs* args=obj->createConstructorArgs();
for(int i=0;isize();i++)
{
gbDotable* argObj=args[i];
printTraverse(indent+1,argObj);
}
delete args; //-- looky all gone before the large data comes in.

dprintf(indent,">\n");

for(int i=0;idotNumChilds();i++)
{
gbDotable* child=obj->dotChild(i);
printTraverse(indent+1,child);
}

dprintf(indent,"\n",obj->getType());
}

remember this is constructor args, so you can put whatever you want in there that will construct the object. if you want to put in every damn thing down to the primitive level, good for you, if not and you want to do it custom, good for you too. constructor means, just like you would want to call it from code. typically your constructor is not gigantic, ie not serialization. that actually is another disadvantage of serialization, the root constructor would end up ginormous and the entire file need to be parsed into place before you can ever create the object? maybe not.

ok. lets look at what a createConstructorArgs method might look like.

void gbGeom::dotFillConstructorArgs(gbConstructorArgs* args)
{
args.push("position",new Float3(gbMatrix44f::extractTranslation(_modelMatrix)));
args.push("rigidBody",_rigidBody); //-- oh my goodness
}

void gbBoxGeom::dotFillConstructorArgs(gbConstructorArgs* args)
{
gbGeom::dotFillConstructorArgs(args);

args->push("size",new Float3(_size)); //-- or just args->push("size",_size) and it will create a Float3
args->push("onClick",_onClick); //-- where onClick is a pointer to a gbScript

//-- if you try and push args.push("position",Float3()) it will override the one there

//-- say that the box had a pointer to the universe, it would not add that to the constructor.
//-- this is not auto serialization, it is "make a list required for construction"
//-- _rigidBody is done here because it makes sense, you could do it manually too
}

the Float3 type gbDotable would have a dotAutoDeleteOnFillConstructorArgs() return true

lets try to load the bitch in. first you need a factory registered by name somewhere.

static gbDotable* gbBoxGeom::dotCreate(gbConstructorArgs* args)
{
return new gbBoxGeom(args);
}

make it a first class citizen

gbBoxGeom::gbBoxGeom(gbConstructorArgs* args) : gbGeom(args)
{
float3 size=args->pop("size",float3(0,0,0));
gbScript* script=dynamic_cast(args->pop("onClick"));
}

hmm. maybe the pop just marks it as popped somehow, but still keeps keeps track of things you have not popped. this is useful if you have overridden args.

wait. is it a good idea to make the constructor a first class citizen. would it be best to just write the factory to use the real constructor?

static gbDotable* gbBoxGeom::dotCreate(gbConstructorArgs* args)
{
float3 size=args->pop("size",float3(0,0,0));
return new gbBoxGeom(size);
}

i don't know. lets talk about the _onClick. it could be a vector. where does that go? it belongs in the data section, not in the constructor.

another problem is the link pass. how does that work? you need to pass in the args again? maybe you could mark certain things that will call you back when you have the value ready.

gbJoint::gbJoint(gbConstructorArgs* args)
{
}

void gbJoint::dotLink(const char* name,gbDotable* obj)
{
if(name=="geomA"){_geomA=dynamic_cast(obj);}
else
if(name=="geomB"){_geomB=dynamic_cast(obj);}
}

hey look, it's magic. i guess the fill needs to do the magic part

void gbGeom::dotFillConstructorArgs(gbConstructorArgs* args)
{
args->push("geomA",new gbDotableRef(_geomA));
args->push("geomB",new gbDotableRef(_geomB));
}

wait. it should be..

Float3("size",_size); Float3 is dotable so it does have a name.

so gbDotableRef should be args->push(new gbDotableRef("geomA",_geomA));

you probably could make the wrapping in a ref automagic by checking to see if the owner is the object you are currently filling from. you can skip that for now though. the gbDotableRef is pretty easy.

once the dotFillConstructorArgs call is complete, it takes any refs and puts them in a global list. whenever an object is created to completion, its checks to see if there are any refs to it, if so it calls the link on those objects.

after all objects are completed, the list should be empty because the last object can't rely on anything that does not exist already. if the list is not empty, then something is wrong.