You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
8.4 KiB

/*==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/>.
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 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 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<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 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
}