526 lines
20 KiB
C++
526 lines
20 KiB
C++
/*
|
|
** Command & Conquer Generals Zero Hour(tm)
|
|
** Copyright 2025 Electronic Arts 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/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : G *
|
|
* *
|
|
* $Archive:: /Commando/Code/Tools/max2w3d/SkinCopy.cpp $*
|
|
* *
|
|
* $Author:: Andre_a $*
|
|
* *
|
|
* $Modtime:: 11/03/99 11:51a $*
|
|
* *
|
|
* $Revision:: 3 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* find_skin_binding -- Find the "WWSkin Binding" modifier on this object. *
|
|
* find_skin_wsm -- Finds the node for the WWSkin WSM used by this object. *
|
|
* get_skin_wsm_obj -- Gets the SkinWSMObjectClass from a WWSkin WSM node. *
|
|
* duplicate_wsm -- Duplicates a WWSkin WSM *
|
|
* find_equivalent_node -- Searches a hierarchy for an object equivalent to the given one. *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/*
|
|
-- Copy the skin info for this object into the target object.
|
|
-- If we haven't copied the WSM yet, it will be copied.
|
|
new_wsm = wwCopySkinInfo source_root target_root new_wsm
|
|
*/
|
|
|
|
#include <MaxScrpt.h> // Main MAXScript header
|
|
#include <MaxObj.h> // MAX* Wrapper objects
|
|
#include <definsfn.h> // def_* functions to create static function headers
|
|
|
|
#include <Max.h>
|
|
#include <modstack.h>
|
|
|
|
#include "skin.h"
|
|
#include "util.h"
|
|
#include "w3d_file.h"
|
|
|
|
|
|
/*
|
|
** Forward declarations
|
|
*/
|
|
|
|
Value *find_skin_node_in_tree (INode *root);
|
|
|
|
SkinModifierClass *find_skin_binding (INode *skinned_obj);
|
|
|
|
INode *find_skin_wsm (INode *skinned_obj);
|
|
|
|
SkinWSMObjectClass *get_skin_wsm_obj (INode *wsm_node);
|
|
|
|
INode *duplicate_wsm (INode *skinned_obj, INode *tree_root);
|
|
|
|
INode *find_equivalent_node (INode *source, INode *tree, bool name_is_valid = false);
|
|
|
|
Value *copy_skin_info (INode *source, INode *target, INode *wsm);
|
|
|
|
IDerivedObject *setup_wsm_derived_obj (INode *node);
|
|
|
|
ModContext *find_skin_mod_context (INode *node);
|
|
|
|
|
|
/*
|
|
** Let MAXScript know we're implementing a new built-in function.
|
|
*/
|
|
def_visible_primitive(find_skin_node, "wwFindSkinNode");
|
|
def_visible_primitive(copy_skin_info, "wwCopySkinInfo");
|
|
def_visible_primitive(dupe_skin_wsm, "wwDuplicateSkinWSM");
|
|
|
|
|
|
/*
|
|
**
|
|
** MAXScript Function:
|
|
** wwFindSkinNode - Usage: wwFindSkinNode tree_root_node
|
|
**
|
|
** Searches the given hierarchy tree for the node containing
|
|
** the WWSkin WSM. Returns the node if found, otherwise it
|
|
** returns undefined.
|
|
**
|
|
** Used by the SceneSetup MAXScript.
|
|
*/
|
|
Value *find_skin_node_cf (Value **arg_list, int count)
|
|
{
|
|
// Verify the number and type of the arguments.
|
|
check_arg_count("wwFindSkinNode", 1, count);
|
|
type_check(arg_list[0], MAXNode, "Tree Root INode");
|
|
|
|
// Get the INode that was passed in.
|
|
INode *tree_root = arg_list[0]->to_node();
|
|
|
|
// Search the tree for the WWSkin WSM, and return
|
|
// the node which references it.
|
|
return find_skin_node_in_tree(tree_root);
|
|
}
|
|
|
|
/*
|
|
**
|
|
** MAXScript Function:
|
|
** wwCopySkinInfo - Usage: wwCopySkinInfo from_node to_node wsm_node to_tree_root
|
|
**
|
|
** Copies the skin info for the given node to the target node.
|
|
** If wsm_node is "undefined" then we will create a new WWSkin WSM
|
|
** with the same values as the one being used by from_node.
|
|
** If from_node doesn't have a WWSkin binding, the return value
|
|
** is "undefined". If the function succeeds, it returns the
|
|
** wsm_node (the new WSM if it was created, otherwise the old wsm_node).
|
|
**
|
|
** Used by the SceneSetup MAXScript.
|
|
*/
|
|
Value * copy_skin_info_cf (Value **arg_list, int count)
|
|
{
|
|
// Verify the number and type of the arguments.
|
|
check_arg_count("wwCopySkinInfo", 4, count);
|
|
type_check(arg_list[0], MAXNode, "Source INode");
|
|
type_check(arg_list[1], MAXNode, "Target INode");
|
|
type_check(arg_list[3], MAXNode, "Target Tree Root INode");
|
|
|
|
// Get the INode pointers that were passed in.
|
|
INode *src_node = arg_list[0]->to_node();
|
|
INode *dest_node = arg_list[1]->to_node();
|
|
INode *wsm_node = NULL;
|
|
INode *tree_root = arg_list[3]->to_node();
|
|
if (arg_list[2] == &undefined)
|
|
{
|
|
// Duplicate the WSM used by src_node.
|
|
wsm_node = duplicate_wsm(find_skin_wsm(src_node), tree_root);
|
|
if (wsm_node == NULL)
|
|
return &undefined;
|
|
}
|
|
else
|
|
wsm_node = arg_list[2]->to_node();
|
|
|
|
return copy_skin_info(src_node, dest_node, wsm_node);
|
|
}
|
|
|
|
|
|
/*
|
|
**
|
|
** MAXScript Function:
|
|
** wwDuplicateSkinWSM - Usage: wwDuplicateSkinWSM wwskin_wsm_node tree_root
|
|
**
|
|
** Duplicates the given WWSkin WSM. tree_root is the root node of a hierarchy
|
|
** containing bones similar to the ones used by the given WSM. The hierarchy
|
|
** will be searched for equivalent bones (ie. names the same except their
|
|
** extension), and those bones will be used by the newly duplicated WSM.
|
|
**
|
|
** Used by the SceneSetup MAXScript.
|
|
*/
|
|
Value * dupe_skin_wsm_cf (Value **arg_list, int count)
|
|
{
|
|
// Verify the number and type of the arguments.
|
|
check_arg_count("wwDuplicateSkinWSM", 2, count);
|
|
type_check(arg_list[0], MAXNode, "WWSkin Object INode");
|
|
type_check(arg_list[1], MAXNode, "Target Tree Root INode");
|
|
|
|
// Get the INode pointers that were passed in.
|
|
INode *wsm_node = arg_list[0]->to_node();
|
|
INode *root_node = arg_list[1]->to_node();
|
|
|
|
// Return the duplicated WWSkin WSM.
|
|
INode *dupe = duplicate_wsm(wsm_node, root_node);
|
|
if (!dupe)
|
|
return &undefined;
|
|
else
|
|
{
|
|
// Return the WSM.
|
|
one_typed_value_local(Value* wsm_node);
|
|
vl.wsm_node = MAXNode::intern(dupe);
|
|
return_value(vl.wsm_node);
|
|
}
|
|
}
|
|
|
|
|
|
Value *find_skin_node_in_tree (INode *root)
|
|
{
|
|
if (root == NULL)
|
|
return &undefined;
|
|
|
|
// Is this the node we're looking for?
|
|
if (get_skin_wsm_obj(root))
|
|
{
|
|
one_typed_value_local(Value* wsm_node);
|
|
vl.wsm_node = MAXNode::intern(root);
|
|
return_value(vl.wsm_node);
|
|
}
|
|
|
|
// Search the children of this node.
|
|
for (int i = 0; i < root->NumChildren(); i++)
|
|
{
|
|
Value *retval = find_skin_node_in_tree(root->GetChildNode(i));
|
|
if (retval != &undefined)
|
|
return retval;
|
|
}
|
|
|
|
// Didn't find it anywhere!
|
|
return &undefined;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* find_skin_binding -- Find the "WWSkin Binding" modifier on this object. *
|
|
* *
|
|
* INPUT: The skinned object. *
|
|
* *
|
|
* OUTPUT: The skin modifier, or NULL if one doesn't exist. *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 10/19/1999 AJA : Created. *
|
|
*=============================================================================================*/
|
|
SkinModifierClass *find_skin_binding (INode *skinned_obj)
|
|
{
|
|
// WWSkin Binding ties us to a space warp, so search the node's
|
|
// WSM Derived Object for the WWSkin Binding modifier.
|
|
IDerivedObject *dobj = skinned_obj->GetWSMDerivedObject();
|
|
if (dobj == NULL)
|
|
return NULL; // not bound to a space warp
|
|
|
|
// Search for the WWSkin Binding modifier on this derived object.
|
|
for (int i = 0; i < dobj->NumModifiers(); i++)
|
|
{
|
|
Modifier *mod = dobj->GetModifier(i);
|
|
if (mod->ClassID() != SKIN_MOD_CLASS_ID)
|
|
continue;
|
|
|
|
// We found the skin modifier.
|
|
return (SkinModifierClass*)mod;
|
|
}
|
|
|
|
// Skin modifier not found.
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* find_skin_wsm -- Finds the node for the WWSkin WSM used by this object. *
|
|
* *
|
|
* INPUT: The node of an object with a WWSkin Binding. *
|
|
* *
|
|
* OUTPUT: The node of the WWSkin WSM referenced by the given object. *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 10/18/1999 AJA : Created. *
|
|
*=============================================================================================*/
|
|
INode *find_skin_wsm (INode *skinned_obj)
|
|
{
|
|
// Find the skin modifier on this object.
|
|
SkinModifierClass *skin_mod = find_skin_binding(skinned_obj);
|
|
if (skin_mod == NULL)
|
|
return NULL;
|
|
|
|
// Using the skin modifer, find the WSM's INode.
|
|
INode *wsm = (INode*)( skin_mod->GetReference(SkinModifierClass::NODE_REF) );
|
|
if (wsm == NULL)
|
|
{
|
|
char buf[256];
|
|
sprintf(buf, "%s has a WWSkin Binding, but I can't find its WWSkin WSM!",
|
|
skinned_obj->GetName());
|
|
throw RuntimeError(buf);
|
|
}
|
|
|
|
return wsm;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* get_skin_wsm_obj -- Gets the SkinWSMObjectClass from a WWSkin WSM node. *
|
|
* *
|
|
* INPUT: The node for the WWSkin WSM's representation in the scene. *
|
|
* *
|
|
* OUTPUT: The SkinWSMObjectClass. *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 10/18/1999 AJA : Created. *
|
|
*=============================================================================================*/
|
|
SkinWSMObjectClass *get_skin_wsm_obj (INode *wsm_node)
|
|
{
|
|
// We need a valid node.
|
|
if (!wsm_node)
|
|
return NULL;
|
|
|
|
// The node must reference an object.
|
|
Object *obj = wsm_node->GetObjectRef();
|
|
if (!obj)
|
|
return NULL;
|
|
|
|
// That BASE object must be a SkinWSMObject
|
|
while (obj)
|
|
{
|
|
// If this is a derived object, burrow deeper,
|
|
// otherwise we're at the base object.
|
|
if (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
|
|
obj = ((IDerivedObject*)obj)->GetObjRef();
|
|
else
|
|
break;
|
|
}
|
|
if (obj->ClassID() != SKIN_OBJ_CLASS_ID)
|
|
return NULL;
|
|
|
|
// Return it.
|
|
return (SkinWSMObjectClass*)obj;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* duplicate_wsm -- Duplicates a WWSkin WSM *
|
|
* *
|
|
* INPUT: wsm_node - INode of the WWSkin WSM object. *
|
|
* tree - The root of a tree containing equivalents of the bones in the WWSkin used *
|
|
* by skinned_obj. The bone names must be the same (after being processed by *
|
|
* Set_W3D_Name() *
|
|
* *
|
|
* OUTPUT: The node of the newly duplicated WWSkin WSM. *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 10/18/1999 AJA : Created. *
|
|
* 11/3/1999 AJA : Changed first argument from skinned_obj to wsm_node. *
|
|
*=============================================================================================*/
|
|
INode *duplicate_wsm (INode *wsm_node, INode *tree)
|
|
{
|
|
SkinWSMObjectClass *wsm_obj = get_skin_wsm_obj(wsm_node);
|
|
|
|
if (!wsm_node || !wsm_obj)
|
|
return NULL;
|
|
|
|
/*
|
|
** Duplicate the WSM.
|
|
*/
|
|
|
|
SkinWSMObjectClass *new_wsm_obj =
|
|
(SkinWSMObjectClass*)CreateInstance(WSM_OBJECT_CLASS_ID, SKIN_OBJ_CLASS_ID);
|
|
if (!new_wsm_obj)
|
|
return NULL;
|
|
|
|
// Create a new node in the scene that points to the new WSM object.
|
|
INode *new_wsm_node = MAXScript_interface->CreateObjectNode(new_wsm_obj);
|
|
if (!new_wsm_node)
|
|
return NULL;
|
|
|
|
// Copy the bones from one to the other.
|
|
for (int i = 0; i < wsm_obj->Num_Bones(); i++)
|
|
{
|
|
INode *src_bone = wsm_obj->Get_Bone(i);
|
|
INode *dst_bone = find_equivalent_node(src_bone, tree);
|
|
if (!src_bone || !dst_bone)
|
|
return NULL;
|
|
|
|
new_wsm_obj->Add_Bone(dst_bone);
|
|
}
|
|
|
|
// Return a pointer to the new WSM node.
|
|
return new_wsm_node;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* find_equivalent_node -- Searches a hierarchy for an object equivalent to the given one. *
|
|
* *
|
|
* INPUT: source - The node to search for an equivalent of. *
|
|
* tree - The hierarchy to search in. *
|
|
* *
|
|
* OUTPUT: The equivalent node, or NULL if none was found. *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 10/19/1999 AJA : Created. *
|
|
*=============================================================================================*/
|
|
INode *find_equivalent_node (INode *source, INode *tree, bool name_is_valid)
|
|
{
|
|
// We need a valid source and tree.
|
|
if (!source || !tree)
|
|
return NULL;
|
|
|
|
// The name of the source object. We'll only evaluate this once as an easy optimization.
|
|
static char src_name[W3D_NAME_LEN];
|
|
if (!name_is_valid)
|
|
Set_W3D_Name(src_name, source->GetName());
|
|
|
|
// The name of the current object we're examining.
|
|
char chk_name[W3D_NAME_LEN];
|
|
Set_W3D_Name(chk_name, tree->GetName());
|
|
|
|
// Is this the node we're looking for?
|
|
if (strcmp(src_name, chk_name) == 0)
|
|
return tree; // Yup, sure is.
|
|
|
|
// Nope. Check its children.
|
|
for (int i = 0; i < tree->NumberOfChildren(); i++)
|
|
{
|
|
INode *retval = find_equivalent_node(source, tree->GetChildNode(i), true);
|
|
if (retval != NULL)
|
|
return retval; // we found the node in our children
|
|
}
|
|
|
|
// No equivalent node was found.
|
|
return NULL;
|
|
}
|
|
|
|
|
|
Value *copy_skin_info (INode *source, INode *target, INode *wsm)
|
|
{
|
|
// Get the "WWSkin Binding" modifier on the source object.
|
|
SkinModifierClass *source_modifier = find_skin_binding(source);
|
|
if (source_modifier == NULL)
|
|
return &undefined;
|
|
|
|
// Get the WSMDerivedObject we can add our skin binding modifier to.
|
|
IDerivedObject *dobj = setup_wsm_derived_obj(target);
|
|
|
|
// Create a new skin modifier and copy the source modifier's settings to it.
|
|
SkinModifierClass *new_modifier = new SkinModifierClass(wsm, get_skin_wsm_obj(wsm));
|
|
if (new_modifier == NULL)
|
|
throw RuntimeError("Out of memory - Unable to allocate a new SkinModifierClass object!");
|
|
new_modifier->SubObjSelLevel = source_modifier->SubObjSelLevel;
|
|
|
|
// Dupe the mod context, especially the local mod data hanging off of it.
|
|
ModContext *source_context = find_skin_mod_context(source);
|
|
ModContext *new_context = new ModContext(source_context->tm, source_context->box,
|
|
source_context->localData);
|
|
if (new_context == NULL)
|
|
throw RuntimeError("Out of memory - Unable to allocate a new ModContext object!");
|
|
|
|
// Add a new "WWSkin Binding" modifier to the target object to associate
|
|
// the object with the skin WSM. All of the settings should be correct,
|
|
// and the target object becomes a "skinned object".
|
|
dobj->AddModifier(new_modifier, new_context);
|
|
|
|
// Return the WSM.
|
|
one_typed_value_local(Value* wsm_node);
|
|
vl.wsm_node = MAXNode::intern(wsm);
|
|
return_value(vl.wsm_node);
|
|
}
|
|
|
|
|
|
IDerivedObject *setup_wsm_derived_obj (INode *node)
|
|
{
|
|
// Check if the target object is already bound to a space warp.
|
|
IDerivedObject *dobj = node->GetWSMDerivedObject();
|
|
if (dobj != NULL)
|
|
{
|
|
// It's bound to a space warp. Check if WWSkin is one of the
|
|
// space warp bindings. If so, remove it (we don't want to
|
|
// be bound to an old skin).
|
|
for (int i = 0; i < dobj->NumModifiers(); i++)
|
|
{
|
|
Modifier *mod = dobj->GetModifier(i);
|
|
if (mod->ClassID() != SKIN_MOD_CLASS_ID)
|
|
continue;
|
|
|
|
// We found the skin modifier, remove it.
|
|
dobj->DeleteModifier(i);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This object isn't bound to a space warp. Create a
|
|
// WSMDerivedObject for the node to play with.
|
|
dobj = CreateWSDerivedObject(node->GetObjectRef());
|
|
if (dobj == NULL)
|
|
{
|
|
char msg[128];
|
|
sprintf(msg, "Error setting up the WSMDerivedObject for %s", node->GetName());
|
|
throw RuntimeError(msg);
|
|
}
|
|
node->SetObjectRef(dobj);
|
|
}
|
|
|
|
return dobj;
|
|
}
|
|
|
|
|
|
ModContext *find_skin_mod_context (INode *node)
|
|
{
|
|
// We need a valid node
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
// The node needs to be bound to a space warp (ie. must have
|
|
// a WSMDerivedObject).
|
|
IDerivedObject *dobj = node->GetWSMDerivedObject();
|
|
if (dobj == NULL)
|
|
return NULL;
|
|
|
|
// It's bound to a space warp. Find the WWSkin modifier.
|
|
for (int i = 0; i < dobj->NumModifiers(); i++)
|
|
{
|
|
Modifier *mod = dobj->GetModifier(i);
|
|
if (mod->ClassID() != SKIN_MOD_CLASS_ID)
|
|
continue;
|
|
|
|
// We found the skin modifier, return its mod context.
|
|
return dobj->GetModContext(i);
|
|
}
|
|
|
|
// We didn't find a WWSkin binding.
|
|
return NULL;
|
|
} |