|
|
|
/*==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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
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 <vector>
|
|
|
|
#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_t 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<ComponentRefs> 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_t idx = -1;
|
|
|
|
for (uint32_t 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<plComponentBase*> &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<plMaxNode*> 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<plComponentBase*> 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_t 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
|
|
|
|
}
|