static void DoNotifyNodeUnHide(void *param, NotifyInfo *info) { int code = info->intcode; INode *node = (INode*)info->callParam; if (Object* obj = node->GetObjectRef()) { // Look for messages in network\Max.log // MAXScript_interface->Log()->LogEntry(SYSLOG_DEBUG, NO_DIALOG, "NifTools Max Plugin", // "Entered DoNotifyNodeUnHide; node is -%s- and class ID is %ld\n", node->GetName(), obj->ClassID().PartA()); if (obj->ClassID() == BHKLISTOBJECT_CLASS_ID) { const int PB_MESHLIST = 1; IParamBlock2* pblock2 = obj->GetParamBlockByID(0); int nBlocks = pblock2->Count(PB_MESHLIST); for (int i = 0;i < pblock2->Count(PB_MESHLIST); i++) { INode *tnode = nullptr; pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); if (tnode != nullptr) { tnode->Hide(FALSE); } } } } }
bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, vector<Triangle>& tris) { Mesh& mesh = o->GetMesh(); INode *tnode = node->GetINode(); Matrix44 baseTM = (importBones) ? triGeom->GetLocalTransform() : triGeom->GetWorldTransform(); node->SetTransform(0,TOMATRIX3(baseTM)); // Vertex info { int nVertices = triGeomData->GetVertexCount(); vector<Vector3> vertices = triGeomData->GetVertices(); mesh.setNumVerts(nVertices); for (int i=0; i < nVertices; ++i){ Vector3 &v = vertices[i]; mesh.verts[i].Set(v.x, v.y, v.z); } } // uv texture info { int nUVSet = triGeomData->GetUVSetCount(); mesh.setNumMaps(nUVSet + 1, TRUE); int n = 0, j = 0; for (int j=0; j<nUVSet; j++){ vector<TexCoord> texCoords = triGeomData->GetUVSet(j); n = texCoords.size(); if (j == 0) { mesh.setNumTVerts(n, TRUE); for (int i=0; i<n; ++i) { TexCoord& texCoord = texCoords[i]; mesh.tVerts[i].Set(texCoord.u, (flipUVTextures) ? 1.0f-texCoord.v : texCoord.v, 0); } } mesh.setMapSupport (j + 1, TRUE); mesh.setNumMapVerts(j + 1, n, TRUE); if ( UVVert *tVerts = mesh.mapVerts(j+1) ) { for (int i=0; i<n; ++i) { TexCoord& texCoord = texCoords[i]; tVerts[i].Set(texCoord.u, (flipUVTextures) ? 1.0f-texCoord.v : texCoord.v, 0); } } } } // Triangles and texture vertices SetTriangles(mesh, tris); SetNormals(mesh, tris, triGeomData->GetNormals() ); vector<Color4> cv = triGeomData->GetColors(); ImportVertexColor(tnode, o, tris, cv, 0); if (Mtl* m = ImportMaterialAndTextures(node, triGeom)) { gi->GetMaterialLibrary().Add(m); node->GetINode()->SetMtl(m); } if (removeDegenerateFaces) mesh.RemoveDegenerateFaces(); if (removeIllegalFaces) mesh.RemoveIllegalFaces(); if (enableSkinSupport) ImportSkin(node, triGeom); if (weldVertices) WeldVertices(mesh); i->AddNodeToScene(node); INode *inode = node->GetINode(); inode->EvalWorldState(0); // attach child if (INode *parent = GetNode(triGeom->GetParent())) parent->AttachChild(inode, 1); inode->Hide(triGeom->GetVisibility() ? FALSE : TRUE); if (enableAutoSmooth){ mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); } RegisterNode(triGeom, inode); return true; }
bool NifImporter::ImportMultipleGeometry(NiNodeRef parent, vector<NiTriBasedGeomRef>& glist) { bool ok = true; if (glist.empty()) return false; ImpNode *node = i->CreateNode(); if(!node) return false; INode *inode = node->GetINode(); TriObject *triObject = CreateNewTriObject(); node->Reference(triObject); string name = parent->GetName(); node->SetName(wide(name).c_str()); // Texture Mesh& mesh = triObject->GetMesh(); vector< pair<int, int> > vert_range, tri_range; vector<Triangle> tris; vector<Vector3> verts; int submats = glist.size(); // Build list of vertices and triangles. Optional components like normals will be handled later. for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr) { NiTriBasedGeomDataRef triGeomData = StaticCast<NiTriBasedGeomData>((*itr)->GetData()); // Get verts and collapse local transform into them int nVertices = triGeomData->GetVertexCount(); vector<Vector3> subverts = triGeomData->GetVertices(); Matrix44 transform = (*itr)->GetLocalTransform(); //Apply the transformations if (transform != Matrix44::IDENTITY) { for ( unsigned int i = 0; i < subverts.size(); ++i ) subverts[i] = transform * subverts[i]; } vert_range.push_back( pair<int,int>( verts.size(), verts.size() + subverts.size()) ); verts.insert(verts.end(), subverts.begin(), subverts.end()); vector<Triangle> subtris = triGeomData->GetTriangles(); for (vector<Triangle>::iterator itr = subtris.begin(), end = subtris.end(); itr != end; ++itr) { (*itr).v1 += nVertices, (*itr).v2 += nVertices, (*itr).v3 += nVertices; } tri_range.push_back( pair<int,int>( tris.size(), tris.size() + subtris.size()) ); tris.insert(tris.end(), subtris.begin(), subtris.end()); } // Transform up-to-parent Matrix44 baseTM = (importBones) ? Matrix44::IDENTITY : parent->GetWorldTransform(); node->SetTransform(0,TOMATRIX3(baseTM)); // Set vertices and triangles mesh.setNumVerts(verts.size()); mesh.setNumTVerts(verts.size(), TRUE); for (int i=0, n=verts.size(); i < n; ++i){ Vector3 &v = verts[i]; mesh.verts[i].Set(v.x, v.y, v.z); } mesh.setNumFaces(tris.size()); mesh.setNumTVFaces(tris.size()); for (int submat=0; submat<submats; ++submat) { int t_start = tri_range[submat].first, t_end = tri_range[submat].second; for (int i=t_start; i<t_end; ++i) { Triangle& t = tris[i]; Face& f = mesh.faces[i]; f.setVerts(t.v1, t.v2, t.v3); f.Show(); f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS); f.setMatID(-1); TVFace& tf = mesh.tvFace[i]; tf.setTVerts(t.v1, t.v2, t.v3); } } mesh.buildNormals(); bool bSpecNorms = false; MultiMtl *mtl = NULL; int igeom = 0; for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr, ++igeom) { NiTriBasedGeomDataRef triGeomData = StaticCast<NiTriBasedGeomData>((*itr)->GetData()); int v_start = vert_range[igeom].first, v_end = vert_range[igeom].second; int t_start = tri_range[igeom].first, t_end = tri_range[igeom].second; // Normals vector<Vector3> subnorms = triGeomData->GetNormals(); Matrix44 rotation = (*itr)->GetLocalTransform().GetRotation(); if (rotation != Matrix44::IDENTITY) { for ( unsigned int i = 0; i < subnorms.size(); ++i ) subnorms[i] = rotation * subnorms[i]; } if (!subnorms.empty()) { #if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 5 // Initialize normals if necessary if (!bSpecNorms) { bSpecNorms = true; mesh.SpecifyNormals(); MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals(); if (NULL != specNorms) { specNorms->BuildNormals(); //specNorms->ClearAndFree(); //specNorms->SetNumFaces(tris.size()); //specNorms->SetNumNormals(n.size()); } } MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals(); if (NULL != specNorms) { Point3* norms = specNorms->GetNormalArray(); for (int i=0, n=subnorms.size(); i<n; i++){ Vector3& v = subnorms[i]; norms[i+v_start] = Point3(v.x, v.y, v.z); } //MeshNormalFace* pFaces = specNorms->GetFaceArray(); //for (int i=0; i<tris.size(); i++){ // Triangle& tri = tris[i]; // MeshNormalFace& face = pFaces[i+t_start]; // face.SpecifyNormalID(0, tri.v1); // face.SpecifyNormalID(1, tri.v2); // face.SpecifyNormalID(2, tri.v3); //} #if VERSION_3DSMAX > ((7000<<16)+(15<<8)+0) // Version 7+ specNorms->SetAllExplicit(true); #endif specNorms->CheckNormals(); } #endif } // uv texture info if (triGeomData->GetUVSetCount() > 0) { vector<TexCoord> texCoords = triGeomData->GetUVSet(0); for (int i=0, n = texCoords.size(); i<n; ++i) { TexCoord& texCoord = texCoords[i]; mesh.tVerts[i+v_start].Set(texCoord.u, (flipUVTextures) ? 1.0f-texCoord.v : texCoord.v, 0); } } vector<Color4> cv = triGeomData->GetColors(); ImportVertexColor(inode, triObject, tris, cv, v_start); if ( StdMat2* submtl = ImportMaterialAndTextures(node, (*itr)) ) { if (mtl == NULL) { mtl = NewDefaultMultiMtl(); gi->GetMaterialLibrary().Add(mtl); inode->SetMtl(mtl); } // SubMatIDs do not have to be contiguous so we just use the offset mtl->SetSubMtlAndName(igeom, submtl, submtl->GetName()); for (int i=t_start; i<t_end; ++i) mesh.faces[i].setMatID(igeom); } if (enableSkinSupport) ImportSkin(node, (*itr)); } this->i->AddNodeToScene(node); inode = node->GetINode(); inode->EvalWorldState(0); for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr) { // attach child if (INode *parent = GetNode((*itr)->GetParent())) parent->AttachChild(inode, 1); inode->Hide((*itr)->GetVisibility() ? FALSE : TRUE); } if (removeDegenerateFaces) mesh.RemoveDegenerateFaces(); if (removeIllegalFaces) mesh.RemoveIllegalFaces(); if (weldVertices) WeldVertices(mesh); if (enableAutoSmooth) mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); return ok; }
BOOL plComponentDlg::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: fhDlg = hDlg; IAddComponentsRecur(GetDlgItem(hDlg, IDC_TREE), (plMaxNode*)GetCOREInterface()->GetRootNode()); ICreateMenu(); ICreateRightClickMenu(); return TRUE; case WM_SIZING: IPositionControls((RECT*)lParam, wParam); return TRUE; case WM_ACTIVATE: if (LOWORD(wParam) == WA_INACTIVE) plMaxAccelerators::Enable(); else plMaxAccelerators::Disable(); return TRUE; case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) { ShowWindow(hDlg, SW_HIDE); fInterface->UnRegisterDlgWnd(hDlg); return TRUE; } else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ATTACH) { IAttachTreeSelection(); return TRUE; } else if (HIWORD(wParam) == EN_KILLFOCUS && LOWORD(wParam) == IDC_COMMENTS) { IGetComment(); return TRUE; } // "Refresh" menu item else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_REFRESH) { IRefreshTree(); return TRUE; } // "Remove unused components" menu item else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_REMOVE_UNUSED) { IRemoveUnusedComps(); return TRUE; } // Item selected from 'New' menu else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) >= MENU_ID_START) { ClassDesc *desc = plComponentMgr::Inst().Get(LOWORD(wParam)-MENU_ID_START); // If this is a component type (not a category) if (desc) { // Create an object of that type and a node to reference it Object *obj = (Object*)GetCOREInterface()->CreateInstance(desc->SuperClassID(), desc->ClassID()); INode *node = GetCOREInterface()->CreateObjectNode(obj); plComponentBase *comp = (plComponentBase*)obj; node->Hide(!comp->AllowUnhide()); node->Freeze(TRUE); // Add the new component to the tree HWND hTree = GetDlgItem(hDlg, IDC_TREE); HTREEITEM item = IAddComponent(hTree, (plMaxNode*)node); TreeView_SelectItem(hTree, item); TreeView_EnsureVisible(hTree, item); } } break; case WM_NOTIFY: NMHDR *nmhdr = (NMHDR*)lParam; if (nmhdr->idFrom == IDC_TREE) { switch (nmhdr->code) { case TVN_SELCHANGED: { NMTREEVIEW *tv = (NMTREEVIEW*)lParam; IGetComment(); bool isComponent = IIsComponent(tv->itemNew.lParam); // If the new selection is a component, enable the attach button and comment field EnableWindow(GetDlgItem(hDlg, IDC_ATTACH), isComponent); SendDlgItemMessage(hDlg, IDC_COMMENTS, EM_SETREADONLY, !isComponent, 0); if (isComponent) { fCommentNode = (plMaxNode*)tv->itemNew.lParam; TSTR buf; fCommentNode->GetUserPropBuffer(buf); SetDlgItemText(hDlg, IDC_COMMENTS, buf); } else { fCommentNode = nil; SetDlgItemText(hDlg, IDC_COMMENTS, ""); } return TRUE; } break; case TVN_BEGINLABELEDIT: // If this isn't a component, don't allow the edit if (!IIsComponent(((NMTVDISPINFO*)lParam)->item.lParam)) { SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); return TRUE; } // The edit box this creates kills the focus on our window, causing // accelerators to be enabled. Add an extra disable to counteract that. plMaxAccelerators::Disable(); return TRUE; // Finishing changing the name of a component case TVN_ENDLABELEDIT: { NMTVDISPINFO *di = (NMTVDISPINFO*)lParam; char* text = di->item.pszText; // If the name was changed... if (text && *text != '\0') { // Update the name of the node plMaxNode *node = IGetTreeSelection(); node->SetName(text); // Update the name in the panel too if (plComponentUtil::Instance().IsOpen()) plComponentUtil::Instance().IUpdateNodeName(node); // Make sure Max knows the file was changed SetSaveRequiredFlag(); // Return true to keep the changes SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); } plMaxAccelerators::Enable(); } return TRUE; // User double-clicked. Select the objects the selected component is attached to. case NM_DBLCLK: ISelectTreeSelection(); return TRUE; case NM_RCLICK: IOpenRightClickMenu(); return TRUE; case TVN_KEYDOWN: // User pressed delete if (((NMTVKEYDOWN*)lParam)->wVKey == VK_DELETE) { IDeleteComponent(IGetTreeSelection()); return TRUE; } break; } } break; } return FALSE; }
void NifImporter::ImportBones(NiNodeRef node, bool recurse) { try { if (uncontrolledDummies) BuildControllerRefList(node, ctrlCount); string name = node->GetName(); vector<NiAVObjectRef> children = node->GetChildren(); vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children); NiAVObject::CollisionType cType = node->GetCollisionMode(); if (children.empty() && name == "Bounding Box") return; // Do all node manipulations here NiNodeRef parent = node->GetParent(); string parentname = (parent ? parent->GetName() : ""); Matrix44 m4 = node->GetWorldTransform(); // Check for Prn strings and change parent if necessary if (supportPrnStrings) { list<NiStringExtraDataRef> strings = DynamicCast<NiStringExtraData>(node->GetExtraData()); for (list<NiStringExtraDataRef>::iterator itr = strings.begin(); itr != strings.end(); ++itr){ if (strmatch((*itr)->GetName(), "Prn")) { parentname = (*itr)->GetData(); if (INode *pn = gi->GetINodeByName(parentname.c_str())){ // Apparently Heads tend to need to be rotated 90 degrees on import for if (!rotate90Degrees.empty() && wildmatch(rotate90Degrees, parentname)) { m4 *= TOMATRIX4(RotateYMatrix(TORAD(90))); } m4 *= TOMATRIX4(pn->GetObjTMAfterWSM(0, NULL)); } } } } float len = node->GetLocalTranslation().Magnitude(); // Remove NonAccum nodes and merge into primary bone if (mergeNonAccum && wildmatch("* NonAccum", name) && parent) { string realname = name.substr(0, name.length() - 9); if (strmatch(realname, parent->GetName())) { Matrix44 tm = parent->GetLocalTransform() * node->GetLocalTransform(); name = realname; len += tm.GetTranslation().Magnitude(); parent = parent->GetParent(); } } PosRotScale prs = prsDefault; Vector3 pos; Matrix33 rot; float scale; m4.Decompose(pos, rot, scale); Matrix3 im = TOMATRIX3(m4); Point3 p = im.GetTrans(); Quat q(im); //q.Normalize(); Vector3 ppos; Point3 zAxis(0,0,0); bool hasChildren = !children.empty(); if (hasChildren) { float len = 0.0f; for (vector<NiAVObjectRef>::iterator itr=children.begin(), end = children.end(); itr != end; ++itr) { len += GetObjectLength(*itr); } len /= float(children.size()); ppos = pos + Vector3(len, 0.0f, 0.0f); // just really need magnitude as rotation will take care of positioning } else if (parent) { ppos = pos + Vector3(len/3.0f, 0.0f, 0.0f); } Point3 pp(ppos.x, ppos.y, ppos.z); Point3 qp = TORAD(TOEULER(im)); INode *bone = NULL; if (!doNotReuseExistingBones) // Games like BC3 reuse the same bone names { bone = FindNode(node); if (bone == NULL) bone = gi->GetINodeByName(name.c_str()); } if (bone) { // Is there a better way of "Affect Pivot Only" behaviors? INode *pinode = bone->GetParentNode(); if (pinode) bone->Detach(0,1); PosRotScaleNode(bone, p, q, scale, prs); if (pinode) pinode->AttachChild(bone, 1); } else { bool isDummy = ( (uncontrolledDummies && !HasControllerRef(ctrlCount, name)) || (!dummyNodeMatches.empty() && wildmatch(dummyNodeMatches, name)) || (convertBillboardsToDummyNodes && node->IsDerivedType(NiBillboardNode::TYPE)) ); if (wildmatch("Camera*", name)) { if (enableCameras) { if (bone = CreateCamera(name)) { PosRotScaleNode(bone, p, q, scale, prs); bone->Hide(node->GetVisibility() ? FALSE : TRUE); } } }else if (isDummy && createNubsForBones) bone = CreateHelper(name, p); else if (bone = CreateBone(name, p, pp, zAxis)) { PosRotScaleNode(bone, p, q, scale, prs); bone->Hide(node->GetVisibility() ? FALSE : TRUE); } if (bone) { if (!parentname.empty()) { if (mergeNonAccum && wildmatch("* NonAccum", parentname)) { parentname = parentname.substr(0, parentname.length() - 9); } if (INode *pn = gi->GetINodeByName(parentname.c_str())) pn->AttachChild(bone, 1); } RegisterNode(node, bone); } } // Import UPB if (bone) ImportUPB(bone, node); // Import Havok Collision Data surrounding node, // unfortunately this causes double import of collision so I'm disabling it for now. if (enableCollision && node->GetParent()) { ImportCollision(node); } if (bone && recurse) { ImportBones(childNodes); } } catch( exception & e ) { e=e; } catch( ... ) { } }