526 lines
20 KiB
C++
Raw Permalink Normal View History

/*
** 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;
}