void SetupAttachmentScene() { sprintf(gTitleString, "Attachment Demo"); // Create objects in scene groundPlane = CreateGroundPlane(); NxActor* box1 = CreateBox(NxVec3(-7,12.25,0), NxVec3(2.5,1,1), 0); NxActor* box2 = CreateBox(NxVec3(0,12.25,0), NxVec3(2.5,1,1), 0); NxActor* box3 = CreateBox(NxVec3(7,12.25,0), NxVec3(2.5,1,1), 0); NxActor* attachedBox = CreateBox(NxVec3(-7.2,4.5,1.6), NxVec3(1.25,1,1), 1); NxActor* attachedSphere = CreateSphere(NxVec3(-0.25,4.0,2.0), 1.3, 1); NxActor* attachedCapsule = CreateCapsule(NxVec3(9.0,5.5,2.0),2.0, 1, 1); NxReal damping = 0.3; attachedBox->setAngularDamping(damping); attachedBox->setLinearDamping(damping); attachedSphere->setAngularDamping(damping); attachedSphere->setLinearDamping(damping); attachedCapsule->setAngularDamping(damping); attachedCapsule->setLinearDamping(damping); NxQuat q; q.fromAngleAxis(90,NxVec3(0,0,1)); attachedCapsule->setGlobalOrientationQuat(q); // Cloth NxClothDesc clothDesc; clothDesc.globalPose.M.rotX(1.3); clothDesc.thickness = 0.3; clothDesc.attachmentResponseCoefficient = 1; clothDesc.flags |= NX_CLF_BENDING; clothDesc.flags |= NX_CLF_BENDING_ORTHO; clothDesc.flags |= NX_CLF_DAMPING | NX_CLF_VISUALIZATION; if (gHardwareCloth) clothDesc.flags |= NX_CLF_HARDWARE; // Cloth attaching to sphere clothDesc.globalPose.t = NxVec3(0.75,5,2); MyCloth* regularCloth1 = new MyCloth(gScene, clothDesc, 2, 8, 0.4); regularCloth1->getNxCloth()->attachToCollidingShapes(NX_CLOTH_ATTACHMENT_TWOWAY); gCloths.push_back(regularCloth1); // Cloth attaching to box clothDesc.globalPose.t = NxVec3(-6.2,5,2); MyCloth* regularCloth2 = new MyCloth(gScene, clothDesc, 2, 8, 0.4); regularCloth2->getNxCloth()->attachToCollidingShapes(NX_CLOTH_ATTACHMENT_TWOWAY); gCloths.push_back(regularCloth2); // Cloth attaching to capsule clothDesc.globalPose.t = NxVec3(8.0,5,2); clothDesc.attachmentTearFactor = 2.0; MyCloth* regularCloth3 = new MyCloth(gScene, clothDesc, 2, 8, 0.4); regularCloth3->getNxCloth()->attachToShape(box3->getShapes()[0], NX_CLOTH_ATTACHMENT_TEARABLE); regularCloth3->getNxCloth()->attachToShape(attachedCapsule->getShapes()[0], NX_CLOTH_ATTACHMENT_TWOWAY); gCloths.push_back(regularCloth3); }
void SetupTearingScene() { sprintf(gTitleString, "Tearing Demo"); // Create the objects in the scene groundPlane = CreateGroundPlane(); NxActor* bar = CreateBox(NxVec3(0,12,0), NxVec3(3,0.5,0.5), 0); NxActor* box = CreateBox(NxVec3(-2.3,4.0,0), NxVec3(0.5,0.5,0.5), 10); // Cloth NxClothDesc clothDesc; clothDesc.globalPose.t = NxVec3(2.5,12,0); clothDesc.globalPose.M.rotX(-NxHalfPiF32); clothDesc.thickness = 0.1; clothDesc.tearFactor = 2; clothDesc.flags |= NX_CLF_BENDING; clothDesc.flags |= NX_CLF_COLLISION_TWOWAY; clothDesc.flags |= NX_CLF_TEARABLE | NX_CLF_VISUALIZATION; // Tearable cloth if (gHardwareCloth) clothDesc.flags |= NX_CLF_HARDWARE; MyCloth* regularCloth = new MyCloth(gScene, clothDesc, 5, 8, 0.1, "rug512.bmp", gTearLines); gCloths.push_back(regularCloth); regularCloth->getNxCloth()->attachToShape(*bar->getShapes(), 0); regularCloth->getNxCloth()->attachToShape(*box->getShapes(), NX_CLOTH_ATTACHMENT_TWOWAY); }
// // EPhysXPhysEngine::BuildConvexMesh // NxConvexMesh *ESciVis::BuildConvexMesh( const IPxTriMesh input_mesh ) { //IPxTriMesh mesh = input_mesh->Clone(); //mesh->SetFormat( GE_MESH_POSITION ); //mesh->MergeVertices(); //This command causes convex cook crash NxConvexMeshDesc convex_mesh_desc; NxArray<NxVec3> verts; for (uint i=0; i<input_mesh->GetVertexNum(); i++) { EVertex v; v = input_mesh->GetVertex(i); verts.push_back( NxVec3(v.position.x, v.position.y, v.position.z) ); } convex_mesh_desc.numVertices = input_mesh->GetVertexNum(); convex_mesh_desc.pointStrideBytes = sizeof(NxVec3); convex_mesh_desc.points = &verts[0]; convex_mesh_desc.flags = NX_CF_COMPUTE_CONVEX; ASSERT( convex_mesh_desc.isValid() ); MemoryWriteBuffer buf; bool r = nx_cook->NxCookConvexMesh(convex_mesh_desc, buf); if (!r) { RUNTIME_ERROR("mesh contains to many vertices"); } return nx->createConvexMesh(MemoryReadBuffer(buf.data)); }
// ---------------------------------------------------------------------------------------------------------- void MeshHash::query(const NxBounds3 &bounds, NxArray<int> &itemIndices, int maxIndices) { int x1,y1,z1; int x2,y2,z2; int x,y,z; cellCoordOf(bounds.min, x1,y1,z1); cellCoordOf(bounds.max, x2,y2,z2); itemIndices.clear(); for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { for (z = z1; z <= z2; z++) { int h = hashFunction(x,y,z); MeshHashRoot &r = mHashIndex[h]; if (r.timeStamp != mTime) continue; int i = r.first; while (i >= 0) { MeshHashEntry &entry = mEntries[i]; itemIndices.push_back(entry.itemIndex); if (maxIndices >= 0 && (int)itemIndices.size() >= maxIndices) return; i = entry.next; } } } } }
// ---------------------------------------------------------------------------------------------------------- void MeshHash::query(const NxVec3 &pos, NxArray<int> &itemIndices, int maxIndices) { int x,y,z; cellCoordOf(pos, x,y,z); itemIndices.clear(); int h = hashFunction(x,y,z); MeshHashRoot &r = mHashIndex[h]; if (r.timeStamp != mTime) return; int i = r.first; while (i >= 0) { MeshHashEntry &entry = mEntries[i]; itemIndices.push_back(entry.itemIndex); if (maxIndices >= 0 && (int)itemIndices.size() >= maxIndices) return; i = entry.next; } }
// ----------------------------------------------------------------------- void VertexWelder::initialize(const NxClothMeshDesc& unweldedMesh) { NxArray<NxU32> mapping; NxReal squaredEpsilon = mEpsilon * mEpsilon; for (NxU32 i = 0; i < unweldedMesh.numVertices; i++) { const NxVec3& curVec = *(const NxVec3*)(((const char*)unweldedMesh.points) + (i * unweldedMesh.pointStrideBytes)); // Find Vertex in newVertices NxU32 newIndex = 0; for (newIndex = 0; newIndex < mNewVertices.size(); newIndex++) { NxVec3& newVec = mNewVertices[newIndex]; if ((mEpsilon == 0 && newVec == curVec) || (newVec.distanceSquared(curVec) < squaredEpsilon)) //if (newVec == curVec) { break; } } if (newIndex == mNewVertices.size()) { // Not found in previous list mNewVertices.push_back(curVec); } mapping.push_back(newIndex); } // Store mapping mMappingSize = mapping.size(); mMappingSpace = unweldedMesh.numTriangles * 3; mMappingDomain = mNewVertices.size(); mMapping = (NxU32*)malloc(sizeof(NxU32) * mMappingSpace); memcpy(mMapping, &mapping[0], sizeof(NxU32) * mMappingSize); memset(((NxU32*)mMapping) + mMappingSize, 0, sizeof(NxU32) * (mMappingSpace - mMappingSize)); mapping.clear(); if (mNewVertices.size() < unweldedMesh.numVertices) { mUsed = true; } else { return; } if (unweldedMesh.flags & NX_MF_16_BIT_INDICES) { mNewFaces16 = (NxU16*)malloc(sizeof(NxU16) * unweldedMesh.numTriangles * 3); } else { mNewFaces32 = (NxU32*)malloc(sizeof(NxU32) * unweldedMesh.numTriangles * 3); } for (NxU32 i = 0; i < unweldedMesh.numTriangles; i++) { NxU32 triangles[3]; const char* triangleChar = ((const char*)unweldedMesh.triangles) + (unweldedMesh.triangleStrideBytes * i); if (mNewFaces16) { const NxU16* tris = (const NxU16*)triangleChar; triangles[0] = tris[0]; triangles[1] = tris[1]; triangles[2] = tris[2]; } else { assert(mNewFaces32 != NULL); const NxU32* tris = (const NxU32*)triangleChar; triangles[0] = tris[0]; triangles[1] = tris[1]; triangles[2] = tris[2]; } for (NxU32 j = 0; j < 3; j++) { triangles[j] = getMapping(triangles[j]); } if (mNewFaces16) { for (NxU32 j = 0; j < 3; j++) { mNewFaces16[3*i+j] = (NxU16)(triangles[j] & 0xffff); } } else { for (NxU32 j = 0; j < 3; j++) { mNewFaces32[3*i+j] = triangles[j]; } } } }
void VertexWelder::update(NxMeshData meshData) { assert(mWriteVerticesPtr != NULL); bool updateVertices = (*(meshData.dirtyBufferFlagsPtr) & (NX_MDF_VERTICES_POS_DIRTY | NX_MDF_VERTICES_NORMAL_DIRTY)) > 0; bool updateIndices = (*(meshData.dirtyBufferFlagsPtr) & (NX_MDF_INDICES_DIRTY | NX_MDF_PARENT_INDICES_DIRTY)) > 0; NxU32 numNewVertices = *meshData.numVerticesPtr; NxU32 numTriangles = *meshData.numIndicesPtr / 3; NxU32 oldMappingDomain = mMappingDomain; NxArray<NewVertex> newVertices; NxArray<DifficultVertex> difficultVertices; mMappingDomainAddition = 0; if (updateVertices) { if (mMappingDomain < numNewVertices) { #ifdef DEBUG_WELDER printf("------------------------------------\n"); #endif for (NxU32 i = mMappingDomain; i < numNewVertices; i++) { NewVertex v; v.index = i; v.parent = *(NxU32*)(((char*)meshData.parentIndicesBegin) + meshData.parentIndicesByteStride * i); while (v.parent >= (NxI32)mMappingDomain) { v.parent = *(NxU32*)(((char*)meshData.parentIndicesBegin) + meshData.parentIndicesByteStride * v.parent); } #ifdef DEBUG_WELDER printf("New Vertex: %d %d\n", v.index, v.parent); #endif newVertices.push_back(v); } std::sort(newVertices.begin(), newVertices.end(), sortParent); } for (NxU32 i = 0; i < mMappingSize; i++) { NxU32 mappedIndex = getMapping(i); NewVertex newV; newV.parent = mappedIndex; // Find all vertices that are a parent for a newly created vertex NxArray<NewVertex>::iterator found = std::lower_bound(newVertices.begin(), newVertices.end(), newV, sortParent); while (found != NULL && found->parent == mappedIndex) { found->mappedVertices ++; if (found->mappedVertices == 1) { found->unMapParent = i; #ifdef DEBUG_WELDER printf("New Vertex Update, %d %d %d\n", found->index, found->parent, found->unMapParent); #endif } else { // several unmapped parents DifficultVertex v; v.mappedIndex = found->index; v.unMappedIndex = i; difficultVertices.push_back(v); #ifdef DEBUG_WELDER printf("Difficult Vertex %d %d\n", v.unMappedIndex, v.mappedIndex); #endif if (found->mappedVertices == 2) { v.unMappedIndex = found->unMapParent; difficultVertices.push_back(v); #ifdef DEBUG_WELDER printf("Difficult Vertex %d %d\n", v.unMappedIndex, v.mappedIndex); #endif } found->unMapParent = -2; } found++; } NxVec3& vertex = *(NxVec3*)(((char*)mWriteVerticesPtr) + mWriteVerticesStride * i); NxVec3& normal = *(NxVec3*)(((char*)mWriteNormalsPtr) + mWriteNormalsStride* i); //float* texCoord = (float*)(((char*)texCoords) + texStride * i); const NxVec3& oldVertex = *(NxVec3*)(((char*)meshData.verticesPosBegin) + meshData.verticesPosByteStride * mappedIndex); const NxVec3& oldNormal = *(NxVec3*)(((char*)meshData.verticesNormalBegin) + meshData.verticesNormalByteStride * mappedIndex); vertex = oldVertex; normal = oldNormal; } // Adapt the mapping table std::sort(newVertices.begin(), newVertices.end(), sortIndex); std::sort(difficultVertices.begin(), difficultVertices.end(), sortDifficultExt); } if (updateIndices) { std::vector<bool> bitVector(mMappingSpace, false); #ifdef DEBUG_WELDER printf("updateIndices: Vertices: %d, Indices %d, gfx Vertices: %d\n", *meshData.numVerticesPtr, *meshData.numIndicesPtr, mMappingSize); #endif if (difficultVertices.size() > 0) { #ifdef DEBUG_WELDER printf(" Difficult Vertices:\n"); #endif for (NxU32 i = 0; i < difficultVertices.size(); i++) { DifficultVertex& v = difficultVertices[i]; #ifdef DEBUG_WELDER printf(" V %d %d\n", v.unMappedIndex, v.mappedIndex); #endif } } assert((meshData.flags & NX_MF_16_BIT_INDICES) == 0); assert(meshData.indicesByteStride == 4); for (NxU32 i = 0; i < numTriangles; i++) { const NxU32* simTriangle = (NxU32*)(((char*)meshData.indicesBegin) + meshData.indicesByteStride * (i*3)); NxU32* gfxTriangle = (NxU32*)(((char*)mWriteIndicesPtr) + mWriteIndicesStride* i); if (simTriangle[0] == simTriangle[1] && simTriangle[1] == simTriangle[2]) { // Face was deleted (outside valid bounds probably) gfxTriangle[0] = gfxTriangle[1] = gfxTriangle[2] = 0; continue; } for (NxU32 j = 0; j < 3; j++) { DifficultVertex v; v.mappedIndex = simTriangle[j]; v.unMappedIndex = gfxTriangle[j]; if (std::binary_search(difficultVertices.begin(), difficultVertices.end(), v, sortDifficult)) { NxArray<DifficultVertex>::iterator it = std::lower_bound(difficultVertices.begin(), difficultVertices.end(), v, sortDifficultExt); #ifdef DEBUG_WELDER printf("-- Triangle %d (%d) (%d %d %d) (%d %d %d)", i, j, simTriangle[0], simTriangle[1], simTriangle[2], gfxTriangle[0], gfxTriangle[1], gfxTriangle[2]); printf(" %d %d\n", v.unMappedIndex, v.mappedIndex); #endif if (it == NULL || it->mappedIndex != simTriangle[j]) { // element hasn't been found //insert element #ifdef DEBUG_WELDER printf("Adding Diff %d %d\n", v.unMappedIndex, v.mappedIndex); #endif difficultVertices.push_back(v); // sort now, don't know whether this could be done less often, so far the list is extremely small std::sort(difficultVertices.begin(), difficultVertices.end(), sortDifficultExt); // get the freshly created item it = std::lower_bound(difficultVertices.begin(), difficultVertices.end(), v, sortDifficultExt); // element has to exist assert(it != NULL); } if (it->newUnMappedIndex >= 0) { gfxTriangle[j] = it->newUnMappedIndex; } else if (bitVector[it->unMappedIndex]) { #ifdef DEBUG_WELDER printf("Bit %d is true\n", it->unMappedIndex); #endif // create a new gfx vertex it->newUnMappedIndex = mMappingSize; addNewVertex(gfxTriangle[j]); setMapping(it->newUnMappedIndex, simTriangle[j]); gfxTriangle[j] = it->newUnMappedIndex; bitVector[it->newUnMappedIndex] = true; } else { #ifdef DEBUG_WELDER printf("Set Bit %d to true\n", it->unMappedIndex); #endif bitVector[it->unMappedIndex] = true; it->newUnMappedIndex = it->unMappedIndex; setMapping(it->newUnMappedIndex, simTriangle[j]); } } else if (simTriangle[j] >= oldMappingDomain) // only used when not a difficult vertex { // unamp index and update for (NxU32 k = 0; k < newVertices.size(); k++) { NewVertex& v = newVertices[k]; if (v.index == simTriangle[j] && v.mappedVertices == 1) { #ifdef DEBUG_WELDER printf("- Triangle %d (%d %d %d) (%d %d %d)", i, simTriangle[0], simTriangle[1], simTriangle[2], gfxTriangle[0], gfxTriangle[1], gfxTriangle[2]); printf(" %d %d\n", v.unMapIndex, v.index); #endif if (v.unMapIndex == -1) { #ifdef DEBUG_WELDER printf("Add Simple\n"); #endif v.unMapIndex = mMappingSize; //addNewVertex(vertices, vertexStride, normals, normalStride, texCoords, texStride, v.unMapParent); addNewVertex(gfxTriangle[j]); gfxTriangle[j] = v.unMapIndex; setMapping(v.unMapIndex, v.index); } else { #ifdef DEBUG_WELDER printf("Use Simple\n"); #endif gfxTriangle[j] = v.unMapIndex; } break; // for (k) } } } } } } if (updateVertices) { mMappingDomain = *meshData.numVerticesPtr; #ifdef DEBUG_WELDER static bool sanityCheck = true; if (sanityCheck) { // sanity check NxU32 temp = 0; for (NxU32 i = 0; i < mMappingSize; i++) { temp = NxMath::max(getMapping(i), temp); } if (temp != mMappingDomain - 1) { printf("Mapping Domain not right, is %d, should be %d\n", temp, mMappingDomain-1); assert(0); } for (NxU32 i = 0; i < numTriangles; i++) { const NxU32* simTriangle = (NxU32*)(((char*)meshData.indicesBegin) + meshData.indicesByteStride * (i*3)); NxU32* gfxTriangle = (NxU32*)(((char*)mWriteIndicesPtr) + mWriteIndicesStride * i); for (NxU32 j = 0; j < 3; j++) { if (simTriangle[j] != getMapping(gfxTriangle[j])) { printf("Triangle %d (%d) not correct (%d %d %d) -> (%d %d %d) != (%d %d %d)\n", i, 3*i+j, gfxTriangle[0], gfxTriangle[1], gfxTriangle[2], getMapping(gfxTriangle[0]), getMapping(gfxTriangle[1]), getMapping(gfxTriangle[2]), simTriangle[0], simTriangle[1], simTriangle[2]); assert(0); } } } } #endif } return; }
bool NxuPhysicsExport::Write(NxJoint *j,const char *userProperties,const char *id) { bool ret = false; NxSceneDesc *current = getCurrentScene(); CustomCopy cc(mCollection,current); NxJointDesc *joint = 0; switch ( j->getType() ) { case NX_JOINT_PRISMATIC: if ( 1 ) { ::NxPrismaticJointDesc d1; NxPrismaticJoint *sj = j->isPrismaticJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxPrismaticJointDesc *desc = new NxPrismaticJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_REVOLUTE: if ( 1 ) { ::NxRevoluteJointDesc d1; NxRevoluteJoint *sj = j->isRevoluteJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxRevoluteJointDesc *desc = new NxRevoluteJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_CYLINDRICAL: if ( 1 ) { ::NxCylindricalJointDesc d1; NxCylindricalJoint *sj = j->isCylindricalJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxCylindricalJointDesc *desc = new NxCylindricalJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_SPHERICAL: if ( 1 ) { ::NxSphericalJointDesc d1; NxSphericalJoint *sj = j->isSphericalJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxSphericalJointDesc *desc = new NxSphericalJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_POINT_ON_LINE: if ( 1 ) { ::NxPointOnLineJointDesc d1; NxPointOnLineJoint *sj = j->isPointOnLineJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxPointOnLineJointDesc *desc = new NxPointOnLineJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_POINT_IN_PLANE: if ( 1 ) { ::NxPointInPlaneJointDesc d1; NxPointInPlaneJoint *sj = j->isPointInPlaneJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxPointInPlaneJointDesc *desc = new NxPointInPlaneJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_DISTANCE: if ( 1 ) { ::NxDistanceJointDesc d1; NxDistanceJoint *sj = j->isDistanceJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxDistanceJointDesc *desc = new NxDistanceJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_PULLEY: if ( 1 ) { ::NxPulleyJointDesc d1; NxPulleyJoint *sj = j->isPulleyJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxPulleyJointDesc *desc = new NxPulleyJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_FIXED: if ( 1 ) { ::NxFixedJointDesc d1; NxFixedJoint *sj = j->isFixedJoint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxFixedJointDesc *desc = new NxFixedJointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; case NX_JOINT_D6: if ( 1 ) { ::NxD6JointDesc d1; NxD6Joint *sj = j->isD6Joint(); sj->saveToDesc(d1); addActor( d1.actor[0] ); addActor( d1.actor[1] ); NxD6JointDesc *desc = new NxD6JointDesc; desc->copyFrom(d1,cc); joint = static_cast<NxJointDesc *>(desc); } break; default: break; } //Add Limits // in addition, we also have to write out its limit planes! j->resetLimitPlaneIterator(); if (j->hasMoreLimitPlanes()) { // write limit point joint->mOnActor2 = j->getLimitPoint(joint->mPlaneLimitPoint); NxArray< NxPlaneInfoDesc *> plist; // write the plane normals while (j->hasMoreLimitPlanes()) { NxPlaneInfoDesc *pInfo = new NxPlaneInfoDesc(); #if NX_SDK_VERSION_NUMBER >= 272 j->getNextLimitPlane(pInfo->mPlaneNormal, pInfo->mPlaneD, &pInfo->restitution); #else j->getNextLimitPlane(pInfo->mPlaneNormal, pInfo->mPlaneD); #endif plist.push_back(pInfo); } if ( plist.size() ) { for (int i=plist.size()-1; i>=0; i--) { NxPlaneInfoDesc *p = plist[i]; joint->mPlaneInfo.pushBack(p); } } } if ( joint ) { if ( id ) { joint->mId = id; } else { char scratch[512]; sprintf(scratch,"Joint_%d", current->mJoints.size()); joint->mId = getGlobalString(scratch); joint->mUserProperties = getGlobalString(userProperties); } current->mJoints.push_back(joint); ret = true; } return ret; }