/* ============ idAASSettings::ParseBBoxes ============ */ bool idAASSettings::ParseBBoxes( idLexer &src ) { idToken token; idBounds bounds; numBoundingBoxes = 0; if ( !src.ExpectTokenString( "{" ) ) { return false; } while( src.ReadToken( &token ) ) { if ( token == "}" ) { return true; } src.UnreadToken( &token ); src.Parse1DMatrix( 3, bounds[0].ToFloatPtr() ); if ( !src.ExpectTokenString( "-" ) ) { return false; } src.Parse1DMatrix( 3, bounds[1].ToFloatPtr() ); boundingBoxes[numBoundingBoxes++] = bounds; } return false; }
/* ================ Reachability_Read ================ */ bool Reachability_Read( idLexer &src, idReachability *reach ) { reach->travelType = src.ParseInt(); reach->toAreaNum = src.ParseInt(); src.Parse1DMatrix( 3, reach->start.ToFloatPtr() ); src.Parse1DMatrix( 3, reach->end.ToFloatPtr() ); reach->edgeNum = src.ParseInt(); reach->travelTime = src.ParseInt(); return true; }
/* ==================== idRenderModelMD5::ParseJoint ==================== */ void idRenderModelMD5::ParseJoint( idLexer& parser, idMD5Joint* joint, idJointQuat* defaultPose ) { // // parse name // idToken token; parser.ReadToken( &token ); joint->name = token; // // parse parent // int num = parser.ParseInt(); if( num < 0 ) { joint->parent = NULL; } else { if( num >= joints.Num() - 1 ) { parser.Error( "Invalid parent for joint '%s'", joint->name.c_str() ); } joint->parent = &joints[ num ]; } // // parse default pose // parser.Parse1DMatrix( 3, defaultPose->t.ToFloatPtr() ); parser.Parse1DMatrix( 3, defaultPose->q.ToFloatPtr() ); defaultPose->q.w = defaultPose->q.CalcW(); }
/* ================ idAASFileLocal::ParseVertices ================ */ bool idAASFileLocal::ParseVertices( idLexer &src ) { int numVertices, i; idVec3 vec; numVertices = src.ParseInt(); vertices.Resize( numVertices ); if ( !src.ExpectTokenString( "{" ) ) { return false; } for ( i = 0; i < numVertices; i++ ) { src.ParseInt(); if ( !src.Parse1DMatrix( 3, vec.ToFloatPtr() ) ) { return false; } vertices.Append( vec ); } if ( !src.ExpectTokenString( "}" ) ) { return false; } return true; }
/* ================ idAASFileLocal::ParsePlanes ================ */ bool idAASFileLocal::ParsePlanes( idLexer &src ) { int numPlanes, i; idPlane plane; idVec4 vec; numPlanes = src.ParseInt(); planeList.Resize( numPlanes ); if ( !src.ExpectTokenString( "{" ) ) { return false; } for ( i = 0; i < numPlanes; i++ ) { src.ParseInt(); if ( !src.Parse1DMatrix( 4, vec.ToFloatPtr() ) ) { return false; } plane.SetNormal( vec.ToVec3() ); plane.SetDist( vec[3] ); planeList.Append( plane ); } if ( !src.ExpectTokenString( "}" ) ) { return false; } return true; }
/* ============ idAASSettings::ParseVector ============ */ bool idAASSettings::ParseVector( idLexer &src, idVec3 &vec ) { if ( !src.ExpectTokenString( "=" ) ) { return false; } return ( src.Parse1DMatrix( 3, vec.ToFloatPtr() ) != 0 ); }
/* ================ idDeclAF::ParseBody ================ */ bool idDeclAF::ParseBody( idLexer &src ) { bool hasJoint = false; idToken token; idAFVector angles; idDeclAF_Body *body = new idDeclAF_Body; bodies.Alloc() = body; body->SetDefault( this ); if ( !src.ExpectTokenType( TT_STRING, 0, &token ) || !src.ExpectTokenString( "{" ) ) { return false; } body->name = token; if ( !body->name.Icmp( "origin" ) || !body->name.Icmp( "world" ) ) { src.Error( "a body may not be named \"origin\" or \"world\"" ); return false; } while( src.ReadToken( &token ) ) { if ( !token.Icmp( "model" ) ) { if ( !src.ExpectTokenType( TT_NAME, 0, &token ) ) { return false; } if ( !token.Icmp( "box" ) ) { body->modelType = TRM_BOX; if ( !src.ExpectTokenString( "(" ) || !body->v1.Parse( src ) || !src.ExpectTokenString( "," ) || !body->v2.Parse( src ) || !src.ExpectTokenString( ")" ) ) { return false; } } else if ( !token.Icmp( "octahedron" ) ) { body->modelType = TRM_OCTAHEDRON; if ( !src.ExpectTokenString( "(" ) || !body->v1.Parse( src ) || !src.ExpectTokenString( "," ) || !body->v2.Parse( src ) || !src.ExpectTokenString( ")" ) ) { return false; } } else if ( !token.Icmp( "dodecahedron" ) ) { body->modelType = TRM_DODECAHEDRON; if ( !src.ExpectTokenString( "(" ) || !body->v1.Parse( src ) || !src.ExpectTokenString( "," ) || !body->v2.Parse( src ) || !src.ExpectTokenString( ")" ) ) { return false; } } else if ( !token.Icmp( "cylinder" ) ) { body->modelType = TRM_CYLINDER; if ( !src.ExpectTokenString( "(" ) || !body->v1.Parse( src ) || !src.ExpectTokenString( "," ) || !body->v2.Parse( src ) || !src.ExpectTokenString( "," ) ) { return false; } body->numSides = src.ParseInt(); if ( !src.ExpectTokenString( ")" ) ) { return false; } } else if ( !token.Icmp( "cone" ) ) { body->modelType = TRM_CONE; if ( !src.ExpectTokenString( "(" ) || !body->v1.Parse( src ) || !src.ExpectTokenString( "," ) || !body->v2.Parse( src ) || !src.ExpectTokenString( "," ) ) { return false; } body->numSides = src.ParseInt(); if ( !src.ExpectTokenString( ")" ) ) { return false; } } else if ( !token.Icmp( "bone" ) ) { body->modelType = TRM_BONE; if ( !src.ExpectTokenString( "(" ) || !body->v1.Parse( src ) || !src.ExpectTokenString( "," ) || !body->v2.Parse( src ) || !src.ExpectTokenString( "," ) ) { return false; } body->width = src.ParseFloat(); if ( !src.ExpectTokenString( ")" ) ) { return false; } } else if ( !token.Icmp( "custom" ) ) { src.Error( "custom models not yet implemented" ); return false; } else { src.Error( "unkown model type %s", token.c_str() ); return false; } } else if ( !token.Icmp( "origin" ) ) { if ( !body->origin.Parse( src ) ) { return false; } } else if ( !token.Icmp( "angles" ) ) { if ( !angles.Parse( src ) ) { return false; } body->angles = idAngles( angles.ToVec3().x, angles.ToVec3().y, angles.ToVec3().z ); } else if ( !token.Icmp( "joint" ) ) { if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) { return false; } body->jointName = token; hasJoint = true; } else if ( !token.Icmp( "mod" ) ) { if ( !src.ExpectAnyToken( &token ) ) { return false; } body->jointMod = JointModFromString( token.c_str() ); } else if ( !token.Icmp( "density" ) ) { body->density = src.ParseFloat(); } else if ( !token.Icmp( "inertiaScale" ) ) { src.Parse1DMatrix( 9, body->inertiaScale[0].ToFloatPtr() ); } else if ( !token.Icmp( "friction" ) ) { body->linearFriction = src.ParseFloat(); src.ExpectTokenString( "," ); body->angularFriction = src.ParseFloat(); src.ExpectTokenString( "," ); body->contactFriction = src.ParseFloat(); } else if ( !token.Icmp( "contents" ) ) { ParseContents( src, body->contents ); } else if ( !token.Icmp( "clipMask" ) ) { ParseContents( src, body->clipMask ); } else if ( !token.Icmp( "selfCollision" ) ) { body->selfCollision = src.ParseBool(); } else if ( !token.Icmp( "containedjoints" ) ) { if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) { return false; } body->containedJoints = token; } else if ( !token.Icmp( "frictionDirection" ) ) { if ( !body->frictionDirection.Parse( src ) ) { return false; } } else if ( !token.Icmp( "contactMotorDirection" ) ) { if ( !body->contactMotorDirection.Parse( src ) ) { return false; } } else if ( token == "}" ) { break; } else { src.Error( "unknown token %s in body", token.c_str() ); return false; } } if ( body->modelType == TRM_INVALID ) { src.Error( "no model set for body" ); return false; } if ( !hasJoint ) { src.Error( "no joint set for body" ); return false; } body->clipMask |= CONTENTS_MOVEABLECLIP; return true; }
/* ==================== idMD5Mesh::ParseMesh ==================== */ void idMD5Mesh::ParseMesh( idLexer& parser, int numJoints, const idJointMat* joints ) { idToken token; idToken name; parser.ExpectTokenString( "{" ); // // parse name // if( parser.CheckTokenString( "name" ) ) { parser.ReadToken( &name ); } // // parse shader // parser.ExpectTokenString( "shader" ); parser.ReadToken( &token ); idStr shaderName = token; shader = declManager->FindMaterial( shaderName ); // // parse texture coordinates // parser.ExpectTokenString( "numverts" ); int count = parser.ParseInt(); if( count < 0 ) { parser.Error( "Invalid size: %s", token.c_str() ); } this->numVerts = count; idList<idVec2> texCoords; idList<int> firstWeightForVertex; idList<int> numWeightsForVertex; texCoords.SetNum( count ); firstWeightForVertex.SetNum( count ); numWeightsForVertex.SetNum( count ); int numWeights = 0; int maxweight = 0; for( int i = 0; i < texCoords.Num(); i++ ) { parser.ExpectTokenString( "vert" ); parser.ParseInt(); parser.Parse1DMatrix( 2, texCoords[ i ].ToFloatPtr() ); firstWeightForVertex[ i ] = parser.ParseInt(); numWeightsForVertex[ i ] = parser.ParseInt(); if( !numWeightsForVertex[ i ] ) { parser.Error( "Vertex without any joint weights." ); } numWeights += numWeightsForVertex[ i ]; if( numWeightsForVertex[ i ] + firstWeightForVertex[ i ] > maxweight ) { maxweight = numWeightsForVertex[ i ] + firstWeightForVertex[ i ]; } } // // parse tris // parser.ExpectTokenString( "numtris" ); count = parser.ParseInt(); if( count < 0 ) { parser.Error( "Invalid size: %d", count ); } idList<int> tris; tris.SetNum( count * 3 ); numTris = count; for( int i = 0; i < count; i++ ) { parser.ExpectTokenString( "tri" ); parser.ParseInt(); tris[ i * 3 + 0 ] = parser.ParseInt(); tris[ i * 3 + 1 ] = parser.ParseInt(); tris[ i * 3 + 2 ] = parser.ParseInt(); } // // parse weights // parser.ExpectTokenString( "numweights" ); count = parser.ParseInt(); if( count < 0 ) { parser.Error( "Invalid size: %d", count ); } if( maxweight > count ) { parser.Warning( "Vertices reference out of range weights in model (%d of %d weights).", maxweight, count ); } idList<vertexWeight_t> tempWeights; tempWeights.SetNum( count ); assert( numJoints < 256 ); // so we can pack into bytes for( int i = 0; i < count; i++ ) { parser.ExpectTokenString( "weight" ); parser.ParseInt(); int jointnum = parser.ParseInt(); if( ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { parser.Error( "Joint Index out of range(%d): %d", numJoints, jointnum ); } tempWeights[ i ].joint = jointnum; tempWeights[ i ].jointWeight = parser.ParseFloat(); parser.Parse1DMatrix( 3, tempWeights[ i ].offset.ToFloatPtr() ); } // create pre-scaled weights and an index for the vertex/joint lookup idVec4* scaledWeights = ( idVec4* ) Mem_Alloc16( numWeights * sizeof( scaledWeights[0] ), TAG_MD5_WEIGHT ); int* weightIndex = ( int* ) Mem_Alloc16( numWeights * 2 * sizeof( weightIndex[0] ), TAG_MD5_INDEX ); memset( weightIndex, 0, numWeights * 2 * sizeof( weightIndex[0] ) ); count = 0; for( int i = 0; i < texCoords.Num(); i++ ) { int num = firstWeightForVertex[i]; for( int j = 0; j < numWeightsForVertex[i]; j++, num++, count++ ) { scaledWeights[count].ToVec3() = tempWeights[num].offset * tempWeights[num].jointWeight; scaledWeights[count].w = tempWeights[num].jointWeight; weightIndex[count * 2 + 0] = tempWeights[num].joint * sizeof( idJointMat ); } weightIndex[count * 2 - 1] = 1; } parser.ExpectTokenString( "}" ); // update counters c_numVerts += texCoords.Num(); c_numWeights += numWeights; c_numWeightJoints++; for( int i = 0; i < numWeights; i++ ) { c_numWeightJoints += weightIndex[i * 2 + 1]; } // // build a base pose that can be used for skinning // idDrawVert* basePose = ( idDrawVert* )Mem_ClearedAlloc( texCoords.Num() * sizeof( *basePose ), TAG_MD5_BASE ); for( int j = 0, i = 0; i < texCoords.Num(); i++ ) { idVec3 v = ( *( idJointMat* )( ( byte* )joints + weightIndex[j * 2 + 0] ) ) * scaledWeights[j]; while( weightIndex[j * 2 + 1] == 0 ) { j++; v += ( *( idJointMat* )( ( byte* )joints + weightIndex[j * 2 + 0] ) ) * scaledWeights[j]; } j++; basePose[i].Clear(); basePose[i].xyz = v; basePose[i].SetTexCoord( texCoords[i] ); } // build the weights and bone indexes into the verts, so they will be duplicated // as necessary at mirror seems static int maxWeightsPerVert; static float maxResidualWeight; const int MAX_VERTEX_WEIGHTS = 4; idList< bool > jointIsUsed; jointIsUsed.SetNum( numJoints ); for( int i = 0; i < jointIsUsed.Num(); i++ ) { jointIsUsed[i] = false; } numMeshJoints = 0; maxJointVertDist = 0.0f; //----------------------------------------- // new-style setup for fixed four weights and normal / tangent deformation // // Several important models have >25% residual weight in joints after the // first four, which is worrisome for using a fixed four joint deformation. //----------------------------------------- for( int i = 0; i < texCoords.Num(); i++ ) { idDrawVert& dv = basePose[i]; // some models do have >4 joint weights, so it is necessary to sort and renormalize // sort the weights and take the four largest int weights[256]; const int numWeights = numWeightsForVertex[ i ]; for( int j = 0; j < numWeights; j++ ) { weights[j] = firstWeightForVertex[i] + j; } // bubble sort for( int j = 0; j < numWeights; j++ ) { for( int k = 0; k < numWeights - 1 - j; k++ ) { if( tempWeights[weights[k]].jointWeight < tempWeights[weights[k + 1]].jointWeight ) { SwapValues( weights[k], weights[k + 1] ); } } } if( numWeights > maxWeightsPerVert ) { maxWeightsPerVert = numWeights; } const int usedWeights = Min( MAX_VERTEX_WEIGHTS, numWeights ); float totalWeight = 0; for( int j = 0; j < numWeights; j++ ) { totalWeight += tempWeights[weights[j]].jointWeight; } assert( totalWeight > 0.999f && totalWeight < 1.001f ); float usedWeight = 0; for( int j = 0; j < usedWeights; j++ ) { usedWeight += tempWeights[weights[j]].jointWeight; } const float residualWeight = totalWeight - usedWeight; if( residualWeight > maxResidualWeight ) { maxResidualWeight = residualWeight; } byte finalWeights[MAX_VERTEX_WEIGHTS] = { 0 }; byte finalJointIndecies[MAX_VERTEX_WEIGHTS] = { 0 }; for( int j = 0; j < usedWeights; j++ ) { const vertexWeight_t& weight = tempWeights[weights[j]]; const int jointIndex = weight.joint; const float fw = weight.jointWeight; assert( fw >= 0.0f && fw <= 1.0f ); const float normalizedWeight = fw / usedWeight; finalWeights[j] = idMath::Ftob( normalizedWeight * 255.0f ); finalJointIndecies[j] = jointIndex; } // Sort the weights and indices for hardware skinning for( int k = 0; k < 3; ++k ) { for( int l = k + 1; l < 4; ++l ) { if( finalWeights[l] > finalWeights[k] ) { SwapValues( finalWeights[k], finalWeights[l] ); SwapValues( finalJointIndecies[k], finalJointIndecies[l] ); } } } // Give any left over to the biggest weight finalWeights[0] += Max( 255 - finalWeights[0] - finalWeights[1] - finalWeights[2] - finalWeights[3], 0 ); dv.color[0] = finalJointIndecies[0]; dv.color[1] = finalJointIndecies[1]; dv.color[2] = finalJointIndecies[2]; dv.color[3] = finalJointIndecies[3]; dv.color2[0] = finalWeights[0]; dv.color2[1] = finalWeights[1]; dv.color2[2] = finalWeights[2]; dv.color2[3] = finalWeights[3]; for( int j = usedWeights; j < 4; j++ ) { assert( dv.color2[j] == 0 ); } for( int j = 0; j < usedWeights; j++ ) { if( !jointIsUsed[finalJointIndecies[j]] ) { jointIsUsed[finalJointIndecies[j]] = true; numMeshJoints++; } const idJointMat& joint = joints[finalJointIndecies[j]]; float dist = ( dv.xyz - joint.GetTranslation() ).Length(); if( dist > maxJointVertDist ) { maxJointVertDist = dist; } } } meshJoints = ( byte* ) Mem_Alloc( numMeshJoints * sizeof( meshJoints[0] ), TAG_MODEL ); numMeshJoints = 0; for( int i = 0; i < numJoints; i++ ) { if( jointIsUsed[i] ) { meshJoints[numMeshJoints++] = i; } } // build the deformInfo and collect a final base pose with the mirror // seam verts properly including the bone weights deformInfo = R_BuildDeformInfo( texCoords.Num(), basePose, tris.Num(), tris.Ptr(), shader->UseUnsmoothedTangents() ); for( int i = 0; i < deformInfo->numOutputVerts; i++ ) { for( int j = 0; j < 4; j++ ) { if( deformInfo->verts[i].color[j] >= numJoints ) { idLib::FatalError( "Bad joint index" ); } } } Mem_Free( basePose ); }
/* ==================== idMD5Mesh::ParseMesh ==================== */ void idMD5Mesh::ParseMesh( idLexer &parser, int numJoints, const idJointMat *joints ) { idToken token; idToken name; int num; int count; int jointnum; idStr shaderName; int i, j; idList<int> tris; idList<int> firstWeightForVertex; idList<int> numWeightsForVertex; int maxweight; idList<vertexWeight_t> tempWeights; parser.ExpectTokenString( "{" ); // // parse name // if ( parser.CheckTokenString( "name" ) ) { parser.ReadToken( &name ); } // // parse shader // parser.ExpectTokenString( "shader" ); parser.ReadToken( &token ); shaderName = token; shader = declManager->FindMaterial( shaderName ); // // parse texture coordinates // parser.ExpectTokenString( "numverts" ); count = parser.ParseInt(); if ( count < 0 ) { parser.Error( "Invalid size: %s", token.c_str() ); } texCoords.SetNum( count ); firstWeightForVertex.SetNum( count ); numWeightsForVertex.SetNum( count ); numWeights = 0; maxweight = 0; for( i = 0; i < texCoords.Num(); i++ ) { parser.ExpectTokenString( "vert" ); parser.ParseInt(); parser.Parse1DMatrix( 2, texCoords[ i ].ToFloatPtr() ); firstWeightForVertex[ i ] = parser.ParseInt(); numWeightsForVertex[ i ] = parser.ParseInt(); if ( !numWeightsForVertex[ i ] ) { parser.Error( "Vertex without any joint weights." ); } numWeights += numWeightsForVertex[ i ]; if ( numWeightsForVertex[ i ] + firstWeightForVertex[ i ] > maxweight ) { maxweight = numWeightsForVertex[ i ] + firstWeightForVertex[ i ]; } } // // parse tris // parser.ExpectTokenString( "numtris" ); count = parser.ParseInt(); if ( count < 0 ) { parser.Error( "Invalid size: %d", count ); } tris.SetNum( count * 3 ); numTris = count; for( i = 0; i < count; i++ ) { parser.ExpectTokenString( "tri" ); parser.ParseInt(); tris[ i * 3 + 0 ] = parser.ParseInt(); tris[ i * 3 + 1 ] = parser.ParseInt(); tris[ i * 3 + 2 ] = parser.ParseInt(); } // // parse weights // parser.ExpectTokenString( "numweights" ); count = parser.ParseInt(); if ( count < 0 ) { parser.Error( "Invalid size: %d", count ); } if ( maxweight > count ) { parser.Warning( "Vertices reference out of range weights in model (%d of %d weights).", maxweight, count ); } tempWeights.SetNum( count ); for( i = 0; i < count; i++ ) { parser.ExpectTokenString( "weight" ); parser.ParseInt(); jointnum = parser.ParseInt(); if ( ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { parser.Error( "Joint Index out of range(%d): %d", numJoints, jointnum ); } tempWeights[ i ].joint = jointnum; tempWeights[ i ].jointWeight = parser.ParseFloat(); parser.Parse1DMatrix( 3, tempWeights[ i ].offset.ToFloatPtr() ); } // create pre-scaled weights and an index for the vertex/joint lookup scaledWeights = (idVec4 *) Mem_Alloc16( numWeights * sizeof( scaledWeights[0] ) ); weightIndex = (int *) Mem_Alloc16( numWeights * 2 * sizeof( weightIndex[0] ) ); memset( weightIndex, 0, numWeights * 2 * sizeof( weightIndex[0] ) ); count = 0; for( i = 0; i < texCoords.Num(); i++ ) { num = firstWeightForVertex[i]; for( j = 0; j < numWeightsForVertex[i]; j++, num++, count++ ) { scaledWeights[count].ToVec3() = tempWeights[num].offset * tempWeights[num].jointWeight; scaledWeights[count].w = tempWeights[num].jointWeight; weightIndex[count * 2 + 0] = tempWeights[num].joint * sizeof( idJointMat ); } weightIndex[count * 2 - 1] = 1; } tempWeights.Clear(); numWeightsForVertex.Clear(); firstWeightForVertex.Clear(); parser.ExpectTokenString( "}" ); // update counters c_numVerts += texCoords.Num(); c_numWeights += numWeights; c_numWeightJoints++; for ( i = 0; i < numWeights; i++ ) { c_numWeightJoints += weightIndex[i*2+1]; } // // build the information that will be common to all animations of this mesh: // silhouette edge connectivity and normal / tangent generation information // idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) ); for ( i = 0; i < texCoords.Num(); i++ ) { verts[i].Clear(); verts[i].st = texCoords[i]; } TransformVerts( verts, joints ); deformInfo = R_BuildDeformInfo( texCoords.Num(), verts, tris.Num(), tris.Ptr(), shader->UseUnsmoothedTangents() ); }