/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "HeadSpin.h" #include "max.h" #include "notify.h" #include "../MaxComponent/plComponent.h" #include #include "plMaxNode.h" bool IIsNodeInTab(INodeTab &tab, INode *node) { for (int i = 0; i < tab.Count(); i++) if (tab[i] == node) return true; return false; } class ComponentRefs { public: plComponentBase *comp; INodeTab refs; }; void plSaveSelected() { Interface *ip = GetCOREInterface(); // Get the Max filename to save to char buf[256]; memset(&buf, 0, sizeof(buf)); OPENFILENAME ofn = {0}; ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = ip->GetMAXHWnd(); ofn.lpstrFilter = "3ds max (*.max)\0*.max\0\0"; ofn.nFilterIndex = 1; ofn.lpstrFile = buf; ofn.nMaxFile = sizeof(buf); // ofn.lpstrInitialDir = ip->GetDir(APP_SCENE_DIR); ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; ofn.lpstrDefExt = "max"; if (GetSaveFileName(&ofn)) { int count = ip->GetSelNodeCount(); int i; // Put all the selected nodes in a list INodeTab selected; for (i = 0; i < count; i++) { plMaxNode *node = (plMaxNode*)ip->GetSelNode(i); selected.Append(1, (INode**)&node); } // Put all the components attached to those nodes in a list INodeTab components; for (i = 0; i < count; i++) { plMaxNode *node = (plMaxNode*)ip->GetSelNode(i); UInt32 compCount = node->NumAttachedComponents(); for (int j = 0; j < compCount; j++) { INode *compNode = node->GetAttachedComponent(j)->GetINode();//Node(j); if (!IIsNodeInTab(components, compNode)) components.Append(1, &compNode); } } // Find the objects that the components are reffing that are not part of the selection. // Back them up, and delete them out of the components ref list so they won't be saved too. std::vector out; for (i = 0; i < components.Count(); i++) { plComponentBase *comp = ((plMaxNode*)components[i])->ConvertToComponent(); for (int j = comp->NumTargets() - 1; j >= 0; --j) { plMaxNodeBase *node = comp->GetTarget(j); if (!node) continue; const char *t1 = components[i]->GetName(); const char *t2 = node->GetName(); if (!IIsNodeInTab(selected, node)) { UInt32 idx = -1; for (UInt32 k = 0; k < out.size(); k++) { if (out[k].comp == comp) idx = k; } if (idx == -1) { idx = out.size(); out.resize(idx+1); out[idx].comp = comp; } out[idx].refs.Append(1, (INode**)&node); comp->DeleteTarget(node); } } } // Save the selected objects and their components INodeTab allNodes; allNodes.Append(selected.Count(), &selected[0]); allNodes.Append(components.Count(), &components[0]); ip->FileSaveNodes(&allNodes, buf); // Restore the component refs to objects that weren't selected for (i = 0; i < out.size(); i++) { for (int j = 0; j < out[i].refs.Count(); j++) out[i].comp->AddTarget((plMaxNode*)out[i].refs[j]); } } } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// void IFindComponentsRecur(plMaxNode *node, std::vector &components) { if (node->IsComponent()) components.push_back(node->ConvertToComponent()); for (int i = 0; i < node->NumberOfChildren(); i++) IFindComponentsRecur((plMaxNode*)node->GetChildNode(i), components); } void IMergeComponents(plComponentBase *to, plComponentBase *from) { for (int i = 0; i < from->NumTargets(); i++) to->AddTarget(from->GetTarget(i)); // Delete the component from the scene theHold.Begin(); GetCOREInterface()->DeleteNode(from->GetINode()); theHold.Accept(_T("Delete Component")); } plMaxNode *IFindComponentRecur(plMaxNode *node, const char *name) { if (!strcmp(node->GetName(), name)) { if (node->IsComponent()) return node; } for (int i = 0; i < node->NumberOfChildren(); i++) { plMaxNode *ret = IFindComponentRecur((plMaxNode*)node->GetChildNode(i), name); if (ret) return ret; } return nil; } void plMerge() { Interface *ip = GetCOREInterface(); // Get the Max filename to merge char file[MAX_PATH]; memset(&file, 0, sizeof(file)); OPENFILENAME ofn = {0}; ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = ip->GetMAXHWnd(); ofn.lpstrFilter = "3ds max (*.max)\0*.max\0\0"; ofn.nFilterIndex = 1; ofn.lpstrFile = file; ofn.nMaxFile = sizeof(file); // ofn.lpstrInitialDir = ip->GetDir(APP_SCENE_DIR); ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; ofn.lpstrTitle = "Merge"; if (!GetOpenFileName(&ofn)) return; // Don't actually merge yet, just get the names of every node in the file NameTab nodeNames; ip->MergeFromFile(file, TRUE, FALSE, FALSE, MERGE_LIST_NAMES, &nodeNames); // For each node name, search the current scene for a component with the same name. // If one is found, append "Merged" to it so its name won't cause a conflict during // the actual merge. std::vector renamedNodes; int i; for (i = 0; i < nodeNames.Count(); i++) { plMaxNode *node = IFindComponentRecur((plMaxNode*)ip->GetRootNode(), nodeNames[i]); if (node) { char buf[256]; strcpy(buf, node->GetName()); strcat(buf, "Merged"); node->SetName(buf); renamedNodes.push_back(node); } } // Do the merge ip->MergeFromFile(file); // Rename the components back to their original names for (i = 0; i < renamedNodes.size(); i++) { char buf[256]; strcpy(buf, renamedNodes[i]->GetName()); buf[strlen(buf)-6] = '\0'; renamedNodes[i]->SetName(buf); } // Put all the components in the scene in a list std::vector components; IFindComponentsRecur((plMaxNode*)ip->GetRootNode(), components); nodeNames.ZeroCount(); // For each component, search the scene for any other components with the same // name and type. If there are any, merge their target lists and delete one. for (i = 0; i < renamedNodes.size(); i++) { if (!renamedNodes[i]) continue; plComponentBase *oldComp = renamedNodes[i]->ConvertToComponent(); char *oldCompName = oldComp->GetINode()->GetName(); for (int j = 0; j < components.size(); j++) { plComponentBase *comp = components[j]; if (oldComp == comp) components[j] = nil; else if (comp) { const char *temp = comp->GetINode()->GetName(); if (!strcmp(oldCompName, comp->GetINode()->GetName()) && comp->ClassID() == comp->ClassID()) { IMergeComponents(comp, oldComp); nodeNames.AddName(oldCompName); continue; } } } } // Send out merge notifications again, so that the component dialog will be updated BroadcastNotification(NOTIFY_FILE_PRE_MERGE); BroadcastNotification(NOTIFY_FILE_POST_MERGE); #if 0 if (nodeNames.Count() == 0) return; // Actually calculate the size of all the merged component names, because // a static buffer could be too small in large merges UInt32 size = 0; for (i = 0; i < nodeNames.Count(); i++) size += strlen(nodeNames[i]) + 1; // Put all the component names in a list and show it to the user char *buf = TRACKED_NEW char[size+25]; strcpy(buf, "Components Merged:\n\n"); for (i = 0; i < nodeNames.Count(); i++) { strcat(buf, nodeNames[i]); strcat(buf, "\n"); } MessageBox(ip->GetMAXHWnd(), buf, "Components Merged", MB_OK); delete [] buf; #endif }