simbcon分析
这是领域内一经典文章,可贵的是其代码是开源的,对其对待分析有助于国内水平的普遍提高。
因为刚开始写,不完整,不严密,可能不正确,暂时不允许任何形式的软、硬拷贝及转载等。
同行交流请加gtak: justin.seeley.cn@gmail.com
按问答的方式,直接切入。
1. 模型是如何载入的?
模型存放在 src/data下,相关有目录有texture,objects,models,characters
查看 src\appgui\init下 input.conf
loadRBFile ../data/characters/bip2D.rbs
loadController ../data/controllers/bip2D/iWalk.sb
知道,其模型及相应控制器(后话)
bip2D.rbs:
Define the rigid bodies that will make up the character - this is the same as the normal bip3d, but has toe
格式如下,有几个地方含义不明确,看后文分析。
A_RigidBody
name 部位名
mesh mesh存放位置
colour 颜色
mass 质量
moi 惯性张量?
[CDP_Sphere | CDP_Box] 碰撞体
position 位置(重心)
frictionCoefficient 摩擦系数
restitutionCoefficient 恢复力系数?
[planer]
[ODEGroundParameters ?]
/End
ArticulatedFigure
root pelvis
[ballInSocketJoint|hingeJont|universalJoint] ?
name 关节名
parent 父骨头
child 孩子骨头
jointPPos 父亲位置?
jointCPos 孩子位置?
jointLimis 约束参数
/joint
。
。
/End
我们从Character类下手,寻找载入的方法。
看其构造函数,直接传入ArticulatatedFigure,转向此类.构造函数无特殊之处,有loadFromFile方法,应该是重点:
//this is where it happens. while (!feof(f)){ //get a line from the file... fgets(buffer, 200, f); if (strlen(buffer)>195) throwError("The input file contains a line that is longer than ~200 characters - not allowed"); char *line = lTrim(buffer); int lineType = getRBLineType(line); switch (lineType) { case RB_ROOT: sscanf(line, "%s", tempName); if (root != NULL) throwError("This articulated figure already has a root"); root = world->getARBByName(tempName); if (root == NULL) throwError("The articulated rigid body \'%s\' cannot be found!", tempName); break; case RB_JOINT_TYPE_UNIVERSAL: tempJoint = new UniversalJoint(line); tempJoint->loadFromFile(f, world); tempJoint->child->AFParent = this; tempJoint->parent->AFParent = this; break; case RB_JOINT_TYPE_HINGE: tempJoint = new HingeJoint(line); tempJoint->loadFromFile(f, world); tempJoint->child->AFParent = this; tempJoint->parent->AFParent = this; break; case RB_JOINT_TYPE_BALL_IN_SOCKET: tempJoint = new BallInSocketJoint(line); tempJoint->loadFromFile(f, world); tempJoint->child->AFParent = this; tempJoint->parent->AFParent = this; break; case RB_END_ARTICULATED_FIGURE: //make sure that the root does not have a parent, otherwise we'll end up with loops in the articulated figure] if (root->pJoint != NULL) throwError("The root of the articulated figure is not allowed to have a parent!"); return;//and... done break; case RB_NOT_IMPORTANT: if (strlen(line)!=0 && line[0] != '#') tprintf("Ignoring input line: \'%s\'\n", line); break; default: throwError("Incorrect articulated body input file: \'%s\' - unexpected line.", buffer); } }
分析这段代码发现,其读取的是文件的后半段,即定义关节部分,推测在其之前还有对同一个文件的调用。
Find All Reference发现:abstractrbengine中loadRBsFromFile方法:
while (!feof(f)){ //get a line from the file... fgets(buffer, 200, f); if (strlen(buffer)>195) throwError("The input file contains a line that is longer than ~200 characters - not allowed"); char *line = lTrim(buffer); int lineType = getRBLineType(line); switch (lineType) { case RB_RB: //create a new rigid body and have it load its own info... newBody = new RigidBody(); newBody->loadFromFile(f); objects.push_back(newBody); break; case RB_ARB: //create a new articulated rigid body and have it load its own info... newBody = new ArticulatedRigidBody(); newBody->loadFromFile(f); objects.push_back(newBody); //remember it as an articulated rigid body to be able to link it with other ABs later on ABs.push_back((ArticulatedRigidBody*)newBody); break; case RB_ARTICULATED_FIGURE: //we have an articulated figure to worry about... newFigure = new ArticulatedFigure(); AFs.push_back(newFigure); newFigure->loadFromFile(f, this); newFigure->addJointsToList(&jts); break; case RB_NOT_IMPORTANT: if (strlen(line)!=0 && line[0] != '#') tprintf("Ignoring input line: \'%s\'\n", line); break; default: throwError("Incorrect rigid body input file: \'%s\' - unexpected line.", buffer); } }
正是我们想要的。
还可以看到,可以载入RigidBody、ArtiulatedRigidBody、ArticulatedFigure
再看RidgidBody的loadFromFile方法:
//this is where it happens. while (!feof(f)){ //get a line from the file... fgets(buffer, 200, f); if (strlen(buffer)>195) throwError("The input file contains a line that is longer than ~200 characters - not allowed"); char *line = lTrim(buffer); int lineType = getRBLineType(line); switch (lineType) { case RB_NAME: sscanf(line, "%s", this->name); break; case RB_MESH_NAME: sscanf(line, "%s", meshName); tmpMesh = OBJReader::loadOBJFile(meshName); tmpMesh->computeNormals(); tmpMesh->dontUseTextureMapping(); meshes.push_back(tmpMesh); break; case RB_MASS: if (sscanf(line, "%lf", &t)!=1) throwError("Incorrect rigid body input file - a mass needs to be specified if the 'mass' keyword is used."); this->props.setMass(t); break; case RB_MOI: if (sscanf(line, "%lf %lf %lf", &t1, &t2, &t3)!=3) throwError("Incorrect rigid body input file - the three principal moments of inertia need to be specified if the 'moi' keyword is used."); if (t1<=0 || t2<=0 || t3<=0) throwError("Incorrect values for the principal moments of inertia."); this->props.setMOI(t1, t2, t3); break; case RB_END_RB: return;//and... done break; case RB_COLOUR: if (sscanf(line, "%lf %lf %lf %lf", &r, &g, &b, &a)!=4) throwError("Incorrect rigid body input file - colour parameter expects 4 arguments (colour %s)\n", line); if (meshes.size()>0) meshes[meshes.size()-1]->setColour(r, g, b, a); break; case RB_SPHERE: if (sscanf(line, "%lf %lf %lf %lf", &p1.x, &p1.y, &p1.z, &r)!=4) throwError("Incorrect rigid body input file - 4 arguments are required to specify a sphere collision detection primitive\n", line); cdps.push_back(new SphereCDP(this, p1, r)); break; case RB_CAPSULE: if (sscanf(line, "%lf %lf %lf %lf %lf %lf %lf", &p1.x, &p1.y, &p1.z, &p2.x, &p2.y, &p2.z, &r)!=7) throwError("Incorrect rigid body input file - 7 arguments are required to specify a capsule collision detection primitive\n", line); cdps.push_back(new CapsuleCDP(this, p1, p2, r)); break; case RB_BOX: if (sscanf(line, "%lf %lf %lf %lf %lf %lf", &p1.x, &p1.y, &p1.z, &p2.x, &p2.y, &p2.z)!=6) throwError("Incorrect rigid body input file - 6 arguments are required to specify a box collision detection primitive\n", line); cdps.push_back(new BoxCDP(this, p1, p2)); break; case RB_PLANE: if (sscanf(line, "%lf %lf %lf %lf %lf %lf", &n.x, &n.y, &n.z, &p1.x, &p1.y, &p1.z)!=6) throwError("Incorrect rigid body input file - 6 arguments are required to specify a plane collision detection primitive\n", line); cdps.push_back(new PlaneCDP(this, n, p1)); break; case RB_NOT_IMPORTANT: if (strlen(line)!=0 && line[0] != '#') tprintf("Ignoring input line: \'%s\'\n", line); break; case RB_LOCKED: this->props.lockBody(); break; case RB_POSITION: if (sscanf(line, "%lf %lf %lf", &state.position.x, &state.position.y, &state.position.z)!=3) throwError("Incorrect rigid body input file - 3 arguments are required to specify the world coordinates position of a rigid body\n", line); break; case RB_ORIENTATION: if (sscanf(line, "%lf %lf %lf %lf", &t, &t1, &t2, &t3)!=4) throwError("Incorrect rigid body input file - 4 arguments are required to specify the world coordinates orientation of a rigid body\n", line); state.orientation = Quaternion::getRotationQuaternion(t, Vector3d(t1, t2, t3).toUnit()) * state.orientation; break; case RB_VELOCITY: if (sscanf(line, "%lf %lf %lf", &state.velocity.x, &state.velocity.y, &state.velocity.z)!=3) throwError("Incorrect rigid body input file - 3 arguments are required to specify the world coordinates velocity of a rigid body\n", line); break; case RB_ANGULAR_VELOCITY: if (sscanf(line, "%lf %lf %lf", &state.angularVelocity.x, &state.angularVelocity.y, &state.angularVelocity.z)!=3) throwError("Incorrect rigid body input file - 3 arguments are required to specify the world coordinates angular velocity of a rigid body\n", line); break; case RB_FRICTION_COEFF: if (sscanf(line, "%lf", &props.mu)!=1) throwError("Incorrect rigid body input file - Expecting a value for the friction coefficient"); if (props.mu<0) throwError("Incorrect rigid body input file - Friction coefficient should be >= 0"); break; case RB_RESTITUTION_COEFF: if (sscanf(line, "%lf", &props.epsilon)!=1) throwError("Incorrect rigid body input file - Expecting a value for the restitution coefficient"); if (props.epsilon<0 || props.epsilon>1) throwError("Incorrect rigid body input file - restitution coefficient should be between 0 and 1"); break; case RB_ODE_GROUND_COEFFS: if (sscanf(line, "%lf %lf", &t1, &t2)!=2) throwError("Two parameters need to be provided for the ODE ground parameter settings"); props.groundSoftness = t1; props.groundPenalty = t2; break; case RB_PLANAR: props.isPlanar = true; break; default: throwError("Incorrect rigid body input file: \'%s\' - unexpected line.", buffer); }
再往上,发现是SimBiConFramework的构造函数的调用,再往上发现在是ControllerEditor中的调用,在其中构造出character之后,还对controller进行了构造。
因此载入过程基本明了了。除了有一个地方,我没有提,这个地方也非常重要。
这整个过程完成后,模型中的骨头、关节都已在内存中了。
其中使用了一些设计模式,有时间再补充上去。
下一章要问的是,如何显示出来的?