This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/htree.cpp

1024 lines
39 KiB
C++

/*
** Command & Conquer Renegade(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/>.
*/
/* $Header: /Commando/Code/ww3d2/htree.cpp 14 10/01/01 6:07p Patrick $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/htree.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 10/01/01 6:06p $*
* *
* $Revision:: 14 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* HTreeClass::HTreeClass -- constructor *
* HTreeClass::~HTreeClass -- destructor *
* HTreeClass::Load -- loads a hierarchy tree from a file *
* HTreeClass::read_pivots -- reads the pivots out of a file *
* HTreeClass::Free -- de-allocate all memory in use *
* HTreeClass::Base_Update -- Computes the base pose transform for each pivot *
* HTreeClass::Anim_Update -- Computes the transform for each pivot with motion *
* HTreeClass::Blend_Update -- computes each pivot as a blend of two anims *
* HTreeClass::Combo_Update -- compute each pivot's transform using an anim combo *
* HTreeClass::Get_Transform -- returns the transformation for the desired pivot *
* HTreeClass::Find_Bone -- Find a bone by name *
* HTreeClass::Get_Bone_Name -- get the name of a bone from its index *
* HTreeClass::Update_Parent_Need_Bits -- all "needed" children force their parents to be nee*
* HTreeClass::HTreeClass -- copy constructor *
* HTreeClass::Get_Parent_Index -- returns index of the parent of the given bone *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "htree.h"
#include "hanim.h"
#include "hcanim.h"
#include <string.h>
#include <assert.h>
#include "wwmath.h"
#include "chunkio.h"
#include "w3d_file.h"
#include "wwmemlog.h"
/***********************************************************************************************
* HTreeClass::HTreeClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HTreeClass::HTreeClass(void) :
NumPivots(0),
Pivot(NULL),
ScaleFactor(1.0f)
{
}
void HTreeClass::Init_Default(void)
{
Free ();
NumPivots = 1;
Pivot = new PivotClass[NumPivots];
Pivot[0].Index = 0;
Pivot[0].Parent = NULL;
Pivot[0].BaseTransform.Make_Identity();
Pivot[0].Transform.Make_Identity();
Pivot[0].IsVisible = true;
strcpy(Pivot[0].Name,"RootTransform");
//::strcpy (Name, "Default");
Name[0] = 0;
return ;
}
/***********************************************************************************************
* HTreeClass::~HTreeClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HTreeClass::~HTreeClass(void)
{
Free();
}
/***********************************************************************************************
* HTreeClass::HTreeClass -- copy constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/4/98 GTH : Created. *
*=============================================================================================*/
HTreeClass::HTreeClass(const HTreeClass & src) :
NumPivots(0),
Pivot(NULL),
ScaleFactor(1.0f)
{
memcpy(&Name,&src.Name,sizeof(Name));
NumPivots = src.NumPivots;
if (NumPivots > 0) {
Pivot = new PivotClass[NumPivots];
}
for (int pi = 0; pi < NumPivots; pi++) {
Pivot[pi] = src.Pivot[pi];
if (src.Pivot[pi].Parent != NULL) {
Pivot[pi].Parent = &(Pivot[src.Pivot[pi].Parent->Index]);
} else {
Pivot[pi].Parent = NULL;
}
}
ScaleFactor = src.ScaleFactor;
}
/***********************************************************************************************
* HTreeClass::Load -- loads a hierarchy tree from a file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
int HTreeClass::Load_W3D(ChunkLoadClass & cload)
{
Free();
/*
** Read the first chunk, it should be the hierarchy header
*/
if (!cload.Open_Chunk()) return LOAD_ERROR;
if (cload.Cur_Chunk_ID() != W3D_CHUNK_HIERARCHY_HEADER) {
// ERROR: Expected Hierarchy Header
return LOAD_ERROR;
}
W3dHierarchyStruct header;
if (cload.Read(&header,sizeof(W3dHierarchyStruct)) != sizeof(W3dHierarchyStruct)) {
return LOAD_ERROR;
}
cload.Close_Chunk();
/*
** Check the version, if < 3.0 add a root node for everything
** to attach to. The load_pivots function will also have to be
** notified of this.
*/
bool pre30 = false;
if (header.Version < W3D_MAKE_VERSION(3,0)) {
header.NumPivots ++;
pre30 = true;
}
/*
** Allocate the array of pivots
*/
memcpy(Name,header.Name,W3D_NAME_LEN);
NumPivots = header.NumPivots;
if (NumPivots > 0) {
Pivot = new PivotClass[NumPivots];
}
/*
** Now, read in all of the other chunks for this hierarchy.
*/
while (cload.Open_Chunk()) {
switch (cload.Cur_Chunk_ID()) {
case W3D_CHUNK_PIVOTS:
if (!read_pivots(cload,pre30)) {
goto Error;
}
break;
default:
// ERROR: expected W3D_CHUNK_PIVOTS!
break;
}
cload.Close_Chunk();
}
return OK;
Error:
Free();
return LOAD_ERROR;
}
/***********************************************************************************************
* HTreeClass::read_pivots -- reads the pivots out of a file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
bool HTreeClass::read_pivots(ChunkLoadClass & cload,bool pre30)
{
W3dPivotStruct piv;
int first_piv = 0;
/*
** At (w3d file format) version 3.0, I added a node for the root. Pre-3.0 htrees didn't have
** this so we just put one in.
*/
if (pre30) {
Pivot[0].Index = 0;
Pivot[0].Parent = NULL;
Pivot[0].BaseTransform.Make_Identity();
Pivot[0].Transform.Make_Identity();
Pivot[0].IsVisible = true;
strcpy(Pivot[0].Name,"RootTransform");
first_piv++;
}
for (int pidx=first_piv; pidx < NumPivots; pidx++) {
if (cload.Read(&piv,sizeof(W3dPivotStruct)) != sizeof(W3dPivotStruct)) {
return false;
}
memcpy(Pivot[pidx].Name,piv.Name,W3D_NAME_LEN);
Pivot[pidx].Index = pidx;
Pivot[pidx].BaseTransform.Make_Identity();
Pivot[pidx].BaseTransform.Translate(Vector3(piv.Translation.X,piv.Translation.Y,piv.Translation.Z));
Pivot[pidx].BaseTransform =
Pivot[pidx].BaseTransform *
Build_Matrix3D(
Quaternion(
piv.Rotation.Q[0],
piv.Rotation.Q[1],
piv.Rotation.Q[2],
piv.Rotation.Q[3]
)
);
/*
** At version 3.0 a root node was added, this "fixes up" pre-3.0 files
** to have that root node
*/
if (pre30) {
piv.ParentIdx += 1;
}
/*
** Set the parent pointer. The first pivot will have a parent index
** of -1 (in post-3.0 files) so set its parent to NULL.
*/
if (piv.ParentIdx == -1) {
Pivot[pidx].Parent = NULL;
assert(pidx == 0);
} else {
Pivot[pidx].Parent = &(Pivot[piv.ParentIdx]);
}
}
Pivot[0].Transform.Make_Identity();
Pivot[0].IsVisible = true;
return true;
}
/***********************************************************************************************
* HTreeClass::Free -- de-allocate all memory in use *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HTreeClass::Free(void)
{
if (Pivot != NULL) {
delete[] Pivot;
Pivot = NULL;
}
NumPivots = 0;
// Also clean up other members:
ScaleFactor = 1.0f;
}
/***********************************************************************************************
* HTreeClass::Simple_Evaluate_Pivot -- Returns the transform of a pivot at the given frame. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 04/13/2000 PDS : Created. *
*=============================================================================================*/
bool HTreeClass::Simple_Evaluate_Pivot
(
HAnimClass * motion,
int pivot_index,
float frame,
const Matrix3D & obj_tm,
Matrix3D * end_tm
) const
{
bool retval = false;
end_tm->Make_Identity ();
if ( motion != NULL &&
end_tm != NULL &&
pivot_index >= 0 &&
pivot_index < NumPivots)
{
//
// Loop over the hierarchy of pivots that this pivot is
// attached to and transform each.
//
for ( PivotClass *pivot = &Pivot[pivot_index];
pivot != NULL && pivot->Parent != NULL;
pivot = pivot->Parent)
{
//
// Build a matrix that represents the animation for this pivot
//
Matrix3D anim_tm;
motion->Get_Transform(anim_tm, pivot->Index, frame);
// Quaternion q;
// motion->Get_Orientation (q, pivot->Index, frame);
// Matrix3D anim_tm = ::Build_Matrix3D(q);
Vector3 trans;
anim_tm.Get_Translation (&trans);
anim_tm.Set_Translation (trans * ScaleFactor);
//
// Transform the animation transform by the 'relative-to-parent' transform.
//
Matrix3D curr_tm;
Matrix3D::Multiply (pivot->BaseTransform, anim_tm, &curr_tm);
//
// Transform the return value by this transform
//
Matrix3D::Multiply (curr_tm, *end_tm, end_tm);
}
//
// Transform the return value by the object's transform
//
Matrix3D::Multiply (obj_tm, *end_tm, end_tm);
// Success!
retval = true;
}
return retval;
}
/***********************************************************************************************
* HTreeClass::Simple_Evaluate_Pivot -- Returns the transform of a pivot at the given frame. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 04/13/2000 PDS : Created. *
*=============================================================================================*/
bool HTreeClass::Simple_Evaluate_Pivot
(
int pivot_index,
const Matrix3D & obj_tm,
Matrix3D * end_tm
) const
{
bool retval = false;
end_tm->Make_Identity ();
if ( end_tm != NULL &&
pivot_index >= 0 &&
pivot_index < NumPivots)
{
//
// Loop over the hierarchy of pivots that this pivot is
// attached to and transform each.
//
for ( PivotClass *pivot = &Pivot[pivot_index];
pivot != NULL && pivot->Parent != NULL;
pivot = pivot->Parent)
{
//
// Build a matrix that represents the animation for this pivot
//
Matrix3D anim_tm (1);
//
// Transform the animation transform by the 'relative-to-parent' transform.
//
Matrix3D curr_tm;
Matrix3D::Multiply (pivot->BaseTransform, anim_tm, &curr_tm);
//
// Transform the return value by this transform
//
Matrix3D::Multiply (curr_tm, *end_tm, end_tm);
}
//
// Transform the return value by the object's transform
//
Matrix3D::Multiply (obj_tm, *end_tm, end_tm);
retval = true;
}
return retval;
}
/***********************************************************************************************
* HTreeClass::Base_Update -- Computes the base pose transform for each pivot *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HTreeClass::Base_Update(const Matrix3D & root)
{
PivotClass *pivot;
Pivot[0].Transform = root;
Pivot[0].IsVisible = true;
for (int piv_idx=1; piv_idx < NumPivots; piv_idx++) {
pivot = &Pivot[piv_idx];
assert(pivot->Parent != NULL);
Matrix3D::Multiply(pivot->Parent->Transform,pivot->BaseTransform,&(pivot->Transform));
pivot->IsVisible = 1;
if (pivot->IsCaptured) pivot->Capture_Update();
}
}
/***********************************************************************************************
* HTreeClass::Anim_Update -- Computes the transform for each pivot with motion *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HTreeClass::Anim_Update(const Matrix3D & root,HAnimClass * motion,float frame)
{
PivotClass *pivot;
Pivot[0].Transform = root;
Pivot[0].IsVisible = true;
int num_anim_pivots = motion->Get_Num_Pivots ();
for (int piv_idx=1; piv_idx < NumPivots; piv_idx++) {
pivot = &Pivot[piv_idx];
// base pose
assert(pivot->Parent != NULL);
Matrix3D::Multiply(pivot->Parent->Transform,pivot->BaseTransform,&(pivot->Transform));
// Don't update this pivot if the HTree doesn't have animation data for it...
if (piv_idx < num_anim_pivots) {
// animation
Vector3 trans;
motion->Get_Translation(trans,piv_idx,frame);
pivot->Transform.Translate(trans * ScaleFactor);
Quaternion q;
motion->Get_Orientation(q,piv_idx,frame);
Matrix3D mtx=::Build_Matrix3D(q);
pivot->Transform = pivot->Transform * mtx;
// visibility
pivot->IsVisible = motion->Get_Visibility(piv_idx,frame);
}
if (pivot->IsCaptured) {
pivot->Capture_Update();
pivot->IsVisible = true;
}
}
}
/***********************************************************************************************
* HTreeClass::Blend_Update -- computes each pivot as a blend of two anims *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/4/98 GTH : Created. *
*=============================================================================================*/
void HTreeClass::Blend_Update
(
const Matrix3D & root,
HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage // 0.0 = motion0. 1.0 = motion1
)
{
PivotClass *pivot;
Pivot[0].Transform = root;
Pivot[0].IsVisible = true;
int num_anim_pivots = MIN( motion0->Get_Num_Pivots (), motion1->Get_Num_Pivots () );
for (int piv_idx=1; piv_idx < NumPivots; piv_idx++) {
pivot = &Pivot[piv_idx];
assert(pivot->Parent != NULL);
Matrix3D::Multiply(pivot->Parent->Transform,pivot->BaseTransform,&(pivot->Transform));
if (piv_idx < num_anim_pivots) {
// interpolated translation
Vector3 trans0;
motion0->Get_Translation(trans0,piv_idx,frame0);
Vector3 trans1;
motion1->Get_Translation(trans1,piv_idx,frame1);
Vector3 lerped = (1.0 - percentage) * trans0 + (percentage) * trans1;
pivot->Transform.Translate(lerped * ScaleFactor);
// interpolated rotation
Quaternion q0;
motion0->Get_Orientation(q0,piv_idx,frame0);
Quaternion q1;
motion1->Get_Orientation(q1,piv_idx,frame1);
Quaternion q;
Fast_Slerp(q,q0,q1,percentage);
pivot->Transform = pivot->Transform * Build_Matrix3D(q);
pivot->IsVisible = (motion0->Get_Visibility(piv_idx,frame0) || motion1->Get_Visibility(piv_idx,frame1));
}
if (pivot->IsCaptured) {
pivot->Capture_Update();
pivot->IsVisible = true;
}
}
}
/***********************************************************************************************
* HTreeClass::Combo_Update -- compute each pivot's transform using an anim combo *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/4/98 GTH : Created. *
*=============================================================================================*/
void HTreeClass::Combo_Update
(
const Matrix3D & root,
HAnimComboClass *anim
)
{
PivotClass *pivot;
Pivot[0].Transform = root;
Pivot[0].IsVisible = true;
int num_anim_pivots = 100000;
for ( int anim_num = 0; anim_num < anim->Get_Num_Anims(); anim_num++ ) {
num_anim_pivots = MIN( num_anim_pivots, anim->Peek_Motion( anim_num )->Get_Num_Pivots() );
}
if ( num_anim_pivots == 100000 ) {
num_anim_pivots = 0;
}
for (int piv_idx=1; piv_idx < NumPivots; piv_idx++) {
pivot = &Pivot[piv_idx];
assert(pivot->Parent != NULL);
Matrix3D::Multiply(pivot->Parent->Transform,pivot->BaseTransform,&(pivot->Transform));
if (piv_idx < num_anim_pivots) {
#define ASSUME_NORMALIZED_ANIM_COMBO_WEIGHTS
Vector3 trans(0,0,0);
Quaternion q0;
Quaternion q1;
#ifndef ASSUME_NORMALIZED_ANIM_COMBO_WEIGHTS
float last_weight = 0;
#endif
float weight_total = 0;
int wcount = 0;
for ( int anim_num = 0; anim_num < anim->Get_Num_Anims(); anim_num++ ) {
HAnimClass *motion = anim->Get_Motion( anim_num );
if ( motion != NULL ) {
float frame_num = anim->Get_Frame( anim_num );
PivotMapClass * pivot_map = anim->Get_Pivot_Weight_Map( anim_num );
//float *pivot_map = anim->Get_Pivot_Weight_Map( anim_num );
float weight = anim->Get_Weight( anim_num );
if ( pivot_map != NULL ) {
weight *= (*pivot_map)[piv_idx];
// GREG - Pivot maps are ref counted so shouldn't we
// release the rivot map here?
pivot_map->Release_Ref();
}
if ( weight != 0.0 ) {
wcount++;
Vector3 temp_trans;
motion->Get_Translation( temp_trans, piv_idx, frame_num );
trans += weight * ScaleFactor * temp_trans;
weight_total += weight;
#ifdef ASSUME_NORMALIZED_ANIM_COMBO_WEIGHTS
motion->Get_Orientation(q1,piv_idx, frame_num );
if ( wcount == 1 ) {
q0 = q1;
} else {
Fast_Slerp(q0, q0, q1, weight / weight_total );
}
#else
q0 = q1;
motion->Get_Orientation(q1, piv_idx, frame_num );
last_weight = weight;
#endif
}
motion->Release_Ref();
}
}
#ifdef ASSUME_NORMALIZED_ANIM_COMBO_WEIGHTS
if (weight_total != 0.0f ) {
// SKB: Removed assert because I have a case where I don't want normalization.
// One anim moves X, the other moves Y. Assert was just in to warn programmers.
// WWASSERT(WWMath::Fabs( weight_total - 1.0 ) < WWMATH_EPSILON);
pivot->Transform.Translate(trans);
pivot->Transform = pivot->Transform * Build_Matrix3D(q0);
}
#else
if (( weight_total != 0.0f ) && (wcount >= 2)) {
pivot->Transform.Translate( trans / weight_total );
Quaternion q = Slerp_( q0, q1, last_weight / weight_total );
pivot->Transform = pivot->Transform * Build_Matrix3D(q);
} else if (weight_total != 0.0f) {
pivot->Transform.Translate( trans / weight_total );
pivot->Transform = pivot->Transform * Build_Matrix3D(q1);
}
#endif
pivot->IsVisible = false;
for ( anim_num = 0; (anim_num < anim->Get_Num_Anims()) && (!pivot->IsVisible); anim_num++ ) {
HAnimClass *motion = anim->Get_Motion( anim_num );
if ( motion != NULL ) {
float frame_num = anim->Get_Frame( anim_num );
pivot->IsVisible |= motion->Get_Visibility(piv_idx,frame_num);
motion->Release_Ref();
}
}
}
if (pivot->IsCaptured) {
pivot->Capture_Update();
pivot->IsVisible = true;
}
}
}
/***********************************************************************************************
* HTreeClass::Find_Bone -- Find a bone by name *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/4/97 GTH : Created. *
*=============================================================================================*/
int HTreeClass::Get_Bone_Index(const char * name) const
{
for (int i=0; i < NumPivots; i++) {
if (stricmp(Pivot[i].Name,name) == 0) {
return i;
}
}
return 0;
}
/***********************************************************************************************
* HTreeClass::Get_Bone_Name -- get the name of a bone from its index *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/4/97 GTH : Created. *
*=============================================================================================*/
const char * HTreeClass::Get_Bone_Name(int boneidx) const
{
assert(boneidx >= 0);
assert(boneidx < NumPivots);
return Pivot[boneidx].Name;
}
/***********************************************************************************************
* HTreeClass::Get_Parent_Index -- returns index of the parent of the given bone *
* *
* INPUT: *
* boneidx - the bone you are interested in *
* *
* OUTPUT: *
* the index of that bone's parent *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/12/2000 gth : Created. *
*=============================================================================================*/
int HTreeClass::Get_Parent_Index(int boneidx) const
{
assert(boneidx >= 0);
assert(boneidx < NumPivots);
if (Pivot[boneidx].Parent != NULL) {
return Pivot[boneidx].Parent->Index;
} else {
return 0;
}
}
// Scale this HTree by a constant factor:
void HTreeClass::Scale(float factor)
{
if (factor == 1.0f) return;
// Scale pivot translations
for (int i = 0; i < NumPivots; i++) {
Matrix3D &pivot_transform = Pivot[i].BaseTransform;
Vector3 pivot_translation;
pivot_transform.Get_Translation(&pivot_translation);
pivot_translation *= factor;
pivot_transform.Set_Translation(pivot_translation);
}
// Set state used later to scale animations:
ScaleFactor *= factor;
}
void HTreeClass::Capture_Bone(int boneindex)
{
assert(boneindex >= 0);
assert(boneindex < NumPivots);
Pivot[boneindex].IsCaptured = true;
}
void HTreeClass::Release_Bone(int boneindex)
{
assert(boneindex >= 0);
assert(boneindex < NumPivots);
Pivot[boneindex].IsCaptured = false;
}
bool HTreeClass::Is_Bone_Captured(int boneindex) const
{
assert(boneindex >= 0);
assert(boneindex < NumPivots);
return Pivot[boneindex].IsCaptured;
}
void HTreeClass::Control_Bone(int boneindex,const Matrix3D & relative_tm,bool world_space_translation)
{
assert(boneindex >= 0);
assert(boneindex < NumPivots);
assert(Pivot[boneindex].IsCaptured);
Pivot[boneindex].WorldSpaceTranslation = world_space_translation;
Pivot[boneindex].CapTransform = relative_tm;
}
void HTreeClass::Get_Bone_Control(int boneindex, Matrix3D & relative_tm) const
{
assert(boneindex >= 0);
assert(boneindex < NumPivots);
//
// Return the bone's control transform to the caller
//
if (Pivot[boneindex].IsCaptured) {
relative_tm = Pivot[boneindex].CapTransform;
} else {
relative_tm.Make_Identity ();
}
return ;
}
// Morph the bones on the HTree using weights from a number of other HTrees
HTreeClass * HTreeClass::Create_Morphed( int num_morph_sources,
const float morph_weights[],
const HTreeClass *tree_array[] )
{
int i;
assert(num_morph_sources>0);
for(i=0;i<num_morph_sources;i++) {
assert( tree_array[i] );
assert( morph_weights[i]>=0.0f && morph_weights[i]<=1.0f );
}
for(i=0;i<num_morph_sources-1;i++) {
assert( tree_array[i]->NumPivots == tree_array[i+1]->NumPivots );
}
// Clone the first one,
HTreeClass * new_tree = new HTreeClass( *tree_array[0] );
// Then interpolate all the pivots translations
for (int pi = 0; pi < new_tree->NumPivots; pi++) {
Vector3 pos(0.0f,0.0f,0.0f);
for(int nm = 0; nm < num_morph_sources; nm++) {
pos.X += tree_array[nm]->Pivot[pi].BaseTransform.Get_Translation().X*morph_weights[nm];
pos.Y += tree_array[nm]->Pivot[pi].BaseTransform.Get_Translation().Y*morph_weights[nm];
pos.Z += tree_array[nm]->Pivot[pi].BaseTransform.Get_Translation().Z*morph_weights[nm];
}
new_tree->Pivot[pi].BaseTransform.Set_Translation( pos );
}
return new_tree;
}
// Create an HTree by Interpolating between others
HTreeClass * HTreeClass::Create_Interpolated( const HTreeClass * tree_a0_b0,
const HTreeClass * tree_a0_b1,
const HTreeClass * tree_a1_b0,
const HTreeClass * tree_a1_b1,
float lerp_a, float lerp_b )
{
assert( tree_a0_b0->NumPivots == tree_a0_b1->NumPivots );
assert( tree_a0_b0->NumPivots == tree_a1_b0->NumPivots );
assert( tree_a0_b0->NumPivots == tree_a1_b1->NumPivots );
// Clone the first one,
HTreeClass * new_tree = new HTreeClass( *tree_a0_b0 );
// Then interpolate all the pivots translations
for (int pi = 0; pi < new_tree->NumPivots; pi++) {
Vector3 pos_a0 = Lerp( tree_a0_b0->Pivot[pi].BaseTransform.Get_Translation(),
tree_a0_b1->Pivot[pi].BaseTransform.Get_Translation(),
lerp_b );
Vector3 pos_a1 = Lerp( tree_a1_b0->Pivot[pi].BaseTransform.Get_Translation(),
tree_a1_b1->Pivot[pi].BaseTransform.Get_Translation(),
lerp_b );
Vector3 pos = Lerp( pos_a0, pos_a1, lerp_a );
new_tree->Pivot[pi].BaseTransform.Set_Translation( pos );
}
return new_tree;
}
// Create an HTree by Interpolating between others
HTreeClass * HTreeClass::Create_Interpolated(const HTreeClass * tree_base,
const HTreeClass * tree_a,
const HTreeClass * tree_b,
float a_scale, float b_scale )
{
WWMEMLOG(MEM_ANIMATION);
assert( tree_base->NumPivots == tree_a->NumPivots );
assert( tree_base->NumPivots == tree_b->NumPivots );
// Clone the first one,
HTreeClass * new_tree = new HTreeClass( *tree_base );
float a_scale_abs = WWMath::Fabs( a_scale );
float b_scale_abs = WWMath::Fabs( b_scale );
if ( a_scale_abs + b_scale_abs > 0 ) {
// Then interpolate all the pivots translations
for (int pi = 0; pi < new_tree->NumPivots; pi++) {
Vector3 pos_a = Lerp( tree_base->Pivot[pi].BaseTransform.Get_Translation(),
tree_a->Pivot[pi].BaseTransform.Get_Translation(),
a_scale );
Vector3 pos_b = Lerp( tree_base->Pivot[pi].BaseTransform.Get_Translation(),
tree_b->Pivot[pi].BaseTransform.Get_Translation(),
b_scale );
Vector3 pos = (pos_a * a_scale_abs + pos_b * b_scale_abs ) / ( a_scale_abs + b_scale_abs );
new_tree->Pivot[pi].BaseTransform.Set_Translation( pos );
}
}
return new_tree;
}