//---------------------------------------------------------------------------- void GeoObjFactory::UpdateCtrlColor1(Movable *mov, Float4 color) { PX2::Node *node = DynamicCast<Node>(mov); if (!node) return; for (int i = 0; i < node->GetNumChildren(); i++) { PX2::Movable *child = node->GetChild(i); PX2::TriMesh *mesh = DynamicCast<TriMesh>(child); if (mesh) { VertexBuffer *vBuffer = mesh->GetVertexBuffer(); VertexFormat *vf = PX2_GR.GetVertexFormat(GraphicsRoot::VFT_PC); VertexBufferAccessor vba(vf, vBuffer); for (int i = 0; i < vBuffer->GetNumElements(); i++) { vba.Color<Float4>(0, i) = color; } Renderer::UpdateAll(vBuffer); } } }
//---------------------------------------------------------------------------- PX2::Movable *SceneBuilder::BuildMesh(INode *maxNode, PX2::Node *relatParentOrEqualNode) { // 将Max的三角形网格数据转换到一个或者更多的等价的Phoenix2三角形网格。 // // maxNode: // Max场景图中的Mesh节点。 // relatParentOrEqualNode: // 在Phoenix2场景图系统中最新创建的父亲节点。 // 返回在Phoenix2场景中指向新的孩子节点的指针,这个指针直接指向TriMesh物体; // 或者是一个“link”节点,“link”的多个孩子TriMesh代表Max中的多个孩子mesh。 bool needDel = false; TriObject *triObject = GetTriObject(maxNode, &needDel); if (!triObject) { return 0; } Mesh *maxMesh = &triObject->GetMesh(); Mtl *mtl = maxNode->GetMtl(); int mtlIndex = mMtls.GetIndex(mtl); // 判断这个Max的几何图形节点是否有“子几何图形节点”,如果有子几何图形节点 // isEqualNode为真,反之为假。 // 如果名称相等,就不是relatParentOrEqualNode了,而是equalNode PX2::Movable *link = 0; bool isEqualNode = (relatParentOrEqualNode->GetName().length()>0 && strcmp(maxNode->GetName(), relatParentOrEqualNode->GetName().c_str()) == 0); // maxName char *maxName = maxNode->GetName(); // 如果只需要一个Phoenix的Mesh表示Max的Mesh,直接将Phoenix的Mesh链接到 // Phoenix的场景图中;否则,创建一个"link"节点,将按照材质分割的子Mesh // 放在"link"下。 int i; std::vector<UniMaterialMesh*> uMeshs; SplitGeometry(maxMesh, mtlIndex, uMeshs); if ((int)uMeshs.size() > 1) { if (!isEqualNode) { link = BuildNode(maxNode, relatParentOrEqualNode); } else { link = relatParentOrEqualNode; } assertion(link->IsDerived(PX2::Node::TYPE), "link must be a Node."); for (i=0; i<(int)uMeshs.size(); i++) { PX2::TriMesh *triMesh = uMeshs[i]->ToTriMesh(); if (triMesh) { char meshNumber[6]; sprintf_s(meshNumber, 6, "_%d", i+1); size_t size = strlen(maxName) + strlen(meshNumber) + 1; char *tdName = new1<char>((int)size); strcpy_s(tdName, size, maxName); strcat_s(tdName, size, meshNumber); triMesh->SetName(tdName); delete1(tdName); ((PX2::Node*)link)->AttachChild(triMesh); } } } else if ((int)uMeshs.size() == 1) { PX2::TriMesh *triMesh = uMeshs[0]->ToTriMesh(); if (triMesh) { if (!isEqualNode) { triMesh->SetName(maxName); triMesh->LocalTransform = GetLocalTransform(maxNode, mTimeStart); } else { size_t size = strlen(maxName) + 3; char *tdName = new1<char>((int)size); strcpy_s(tdName, size, maxName); strcat_s(tdName, size, "_1"); triMesh->SetName(tdName); delete1(tdName); } assertion(relatParentOrEqualNode->IsDerived(PX2::Node::TYPE), "relatParentOrEqualNode must be a Node."); relatParentOrEqualNode->AttachChild(triMesh); link = triMesh; } } for (i=0; i<(int)uMeshs.size(); i++) { delete0(uMeshs[i]); } if (needDel) { delete0(triObject); } return link; }
//---------------------------------------------------------------------------- void SceneBuilder::ProcessSkin(INode *node, Modifier *skinMod) { // 构造皮肤控制器。如果Max的网格被按照材质细分,每一个网格都需要自己的蒙皮 // 信息控制器。蒙皮信息中的offset,在动画起始时被计算,是骨骼的世界变换。 // // node: // 指向蒙皮修改器指向的Max中的节点。 // skinMod: // 指向蒙皮修改器 // 1. 获得max蒙皮信息中的骨骼,对应的在Phoenix的骨骼节点列表 // 2. 获得maxNode影响的Phoenix网格 // 3. 获得max中每个骨骼所影响的Phoenix网格中的顶点的数量,忽略不受蒙皮信息 // 影响的网格 // 4. 计算Phoenix mesh的蒙皮信息,生成SkinControl,AttachController到 // Phoenix mesh上。 // 1 bool needDel; TriObject *triObj = GetTriObject(node, &needDel); Mesh *maxMesh = &triObj->GetMesh(); // Max皮肤控制器接口 ISkin *skin = (ISkin*)skinMod->GetInterface(I_SKIN); ISkinContextData *skinData = skin->GetContextInterface(node); // max Skin Bones -> Phoenix2 Skin Bones int b, numSkinBone = skin->GetNumBones(); PX2::Node **bones = new1<PX2::Node*>(numSkinBone); for (b=0; b<numSkinBone; b++) { INode *boneNode = skin->GetBone(b); const std::string &boneName = boneNode->GetName(); PX2::Node *node = PX2::StaticCast<PX2::Node>(mScene->GetObjectByName(boneName)); bones[b] = node; } // 1 // 获得maxNode相关联的Phoenix mesh std::vector<PX2::TriMesh*> meshes; PX2::Object *object = mScene->GetObjectByName(node->GetName()); if (object->IsExactly(PX2::TriMesh::TYPE)) { meshes.push_back(PX2::StaticCast<PX2::TriMesh>(object)); } else { PX2::Node *node = PX2::StaticCast<PX2::Node>(object); const char *nName = node->GetName().c_str(); for (int c=0; c<node->GetNumChildren(); c++) { PX2::Movable *child = node->GetChild(c); const char *cName = child->GetName().c_str(); if (strncmp(cName, nName, strlen(nName)) == 0) // 这里必须是strlen(nName),因为子节点有_1,_2 { meshes.push_back(PX2::StaticCast<PX2::TriMesh>(child)); } } } // 为Phoenix2的每个网格建立相关的皮肤控制器 int *boneInfuseNumVert = new1<int>(numSkinBone); for (int m=0; m<(int)meshes.size(); m++) { PX2::TriMesh *mesh = meshes[m]; // Phoenix顶点在max顶点中的索引 PX2::VertexBuffer *vb = mesh->GetVertexBuffer(); int px2MeshVertexNum = vb->GetNumElements(); std::vector<int> MaxVertexIndex; // i->max索引 int v, i, j, k; PX2::VertexBufferAccessor vba(mesh->GetVertexFormat(), vb); // 3 for (int v=0; v<px2MeshVertexNum; ++v) { Float3 &position = vba.Position<Float3>(v); for (i=0; i<maxMesh->getNumVerts(); i++) { if (position[0] == maxMesh->verts[i].x && position[1] == maxMesh->verts[i].y && position[2] == maxMesh->verts[i].z) { MaxVertexIndex.push_back(i); break; } } } // 确定每个骨骼所影响的顶点数量 int maxVertexSize = (int)MaxVertexIndex.size(); memset(boneInfuseNumVert, 0, sizeof(int)*numSkinBone); for (i=0; i<maxVertexSize; i++) { v = MaxVertexIndex[i]; for (j=0; j<skinData->GetNumAssignedBones(v); j++) { // 这个max中的顶点受到几个骨骼影响啊?! b = skinData->GetAssignedBone(v, j); // 获得这个影响的骨骼索引(这个j是第几个) boneInfuseNumVert[b]++; // 这个骨骼影响的顶点数量++ } } // (通过PX2中的顶点找到Max中的顶点,找到Max中影响该顶点的骨骼) // 如果Max的网格是被按照材质分割的,可能一些骨骼对当前Phoenix2网格没有 // 影响 int bQuantity = 0; // 影响当前Phoenix2网格的骨骼数量 for (b=0; b<numSkinBone; b++) { if (boneInfuseNumVert[b] > 0) bQuantity++; } if (bQuantity == 0) { // Phoenix网格不被任何骨骼影响,进入下一个网格 continue; } // 4 PX2::Node **theBones = new1<PX2::Node*>(bQuantity); float **weight = new2<float>(bQuantity, maxVertexSize); memset(weight[0],0,bQuantity*maxVertexSize*sizeof(float)); PX2::APoint **offset = new2<PX2::APoint>(bQuantity, maxVertexSize); memset(offset[0],0,bQuantity*maxVertexSize*sizeof(PX2::APoint)); PX2::HMatrix *mats = new1<PX2::HMatrix>(bQuantity); // 计算max骨骼到Phoenix骨骼对应的索引(k) std::vector<int> bIArray(numSkinBone); for (b=0, k=0; b<numSkinBone; b++) { if (boneInfuseNumVert[b] > 0) { theBones[k] = bones[b]; // 获取对Mesh有影响的骨骼 bIArray[b] = k; // max bone index -> px2 可用bone index HMatrix boneWorldMat = theBones[k]->WorldTransform.Matrix(); HMatrix meshWorldMat = mesh->WorldTransform.Matrix(); mats[k] = boneWorldMat.Inverse() * meshWorldMat; k++; } } // 遍历顶点,计算顶点权重和offset for (i=0; i<maxVertexSize; i++) { v = MaxVertexIndex[i]; for (j=0; j<skinData->GetNumAssignedBones(v); j++) { // 遍历影响该Max顶点的骨骼 b = skinData->GetAssignedBone(v, j); k = bIArray[b]; float wit = skinData->GetBoneWeight(v, j); // 第j个骨骼的影响权重 weight[i][k] = wit; Float3 &position = vba.Position<Float3>(i); APoint point = theBones[k]->WorldTransform.Inverse() * (mesh->WorldTransform * APoint(position)); offset[i][k] = Float3(point.X(), point.Y(), point.Z()); // 在所受影响骨骼中的位置 } } PX2::SkinController *skinCtrl = new0 PX2::SkinController (maxVertexSize, bQuantity); skinCtrl->SetName("SkinController"); for (int i=0; i<bQuantity; i++) { skinCtrl->GetBones()[i] = theBones[i]; skinCtrl->GetTMMatrixs()[i] = mats[i]; } // offset for (int i=0; i<maxVertexSize; i++) { for (int j=0; j<bQuantity; j++) { skinCtrl->GetWeights()[i][j] = weight[i][j]; skinCtrl->GetOffsets()[i][j] = offset[i][j]; } } // index weights for (int i=0; i<maxVertexSize; i++) { Float4 inds = Float4(0.0f, 0.0f, 0.0f, 0.0f); Float4 wights = Float4(0.0f, 0.0f, 0.0f, 0.0f); In *pSortBone = new In[bQuantity]; for (int j=0; j<bQuantity; j++) { pSortBone[j].index = j; pSortBone[j].data = weight[i][j]; } qsort(pSortBone, bQuantity, sizeof(pSortBone[0]), cmp); int useBoneNum = bQuantity; if (useBoneNum > 4) useBoneNum = 4; float allWeight = 0.0f; for (int useBoneIndex=0; useBoneIndex<useBoneNum; useBoneIndex++) { allWeight += pSortBone[useBoneIndex].data; } if (allWeight <= 0.0f) allWeight = 1.0f; for (int useBoneIndex=0; useBoneIndex<useBoneNum; useBoneIndex++) { inds[useBoneIndex] = (float)(pSortBone[useBoneIndex].index); wights[useBoneIndex] = pSortBone[useBoneIndex].data/allWeight; } vba.TCoord<Float4>(1, i) = inds; vba.TCoord<Float4>(2, i) = wights; delete [] pSortBone; } skinCtrl->Repeat = Controller::RT_WRAP; skinCtrl->MinTime = 0.0f; skinCtrl->MaxTime = TicksToSec(mTimeEnd - mTimeStart); mesh->AttachController(skinCtrl); delete1(theBones); delete2(weight); delete2(offset); delete1(mats); } if (needDel) { delete0(triObj); } delete1(bones); delete1(boneInfuseNumVert); }