Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
hmorphanim.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/***********************************************************************************************
20 *** 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 ***
21 ***********************************************************************************************
22 * *
23 * Project Name : WW3D *
24 * *
25 * $Archive:: /Commando/Code/ww3d2/hmorphanim.cpp $*
26 * *
27 * Original Author:: Greg Hjelstrom *
28 * *
29 * $Author:: Byon_g $*
30 * *
31 * $Modtime:: 1/16/02 6:39p $*
32 * *
33 * $Revision:: 7 $*
34 * *
35 *---------------------------------------------------------------------------------------------*
36 * Functions: *
37 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
38
39#include "hmorphanim.h"
40#include "w3d_file.h"
41#include "chunkio.h"
42#include "assetmgr.h"
43#include "htree.h"
44#include "wwstring.h"
45#include "textfile.h"
46#include "simplevec.h"
47
48
49
51 : CachedIdx (0)
52{
53}
54
59
60void TimeCodedMorphKeysClass::Free(void)
61{
62 Keys.Delete_All ();
63 CachedIdx = 0;
64}
65
67{
68 Free();
69 uint32 key_count = cload.Cur_Chunk_Length() / sizeof(W3dMorphAnimKeyStruct);
70
72 for (uint32 i=0; i<key_count; i++) {
73 cload.Read(&w3dkey,sizeof(w3dkey));
74 Keys.Add (MorphKeyStruct (w3dkey.MorphFrame, w3dkey.PoseFrame));
75 }
76 CachedIdx = 0;
77 return true;
78}
79
81{
83 for (int i=0; i<Keys.Count (); i++) {
84 w3dkey.MorphFrame = Keys[i].MorphFrame;
85 w3dkey.PoseFrame = Keys[i].PoseFrame;
86 csave.Write(&w3dkey,sizeof(w3dkey));
87 }
88 return true;
89}
90
91void TimeCodedMorphKeysClass::Add_Key (uint32 morph_frame, uint32 pose_frame)
92{
93 Keys.Add (MorphKeyStruct (morph_frame, pose_frame));
94 return ;
95}
96
97void TimeCodedMorphKeysClass::Get_Morph_Info(float morph_frame,int * pose_frame0,int * pose_frame1,float * fraction)
98{
99 if (morph_frame < 0.0f) {
100 *pose_frame0 = *pose_frame1 = Keys[0].PoseFrame;
101 *fraction = 0.0f;
102 return;
103 }
104
105 if (morph_frame >= Keys[Keys.Count ()-1].MorphFrame) {
106 *pose_frame0 = *pose_frame1 = Keys[Keys.Count ()-1].PoseFrame;
107 *fraction = 0.0f;
108 return;
109 }
110
111 int key_index = get_index(morph_frame);
112
113 *pose_frame0 = Keys[key_index].PoseFrame;
114 *pose_frame1 = Keys[key_index+1].PoseFrame;
115 *fraction = (morph_frame - Keys[key_index].MorphFrame) / (Keys[key_index+1].MorphFrame - Keys[key_index].MorphFrame);
116}
117
118
119uint32 TimeCodedMorphKeysClass::get_index(float frame)
120{
121 assert(CachedIdx <= (uint32)Keys.Count ()-1);
122
123 float cached_frame = Keys[CachedIdx].MorphFrame;
124
125 // check if the requested time is in the cached interval or the following one
126 if (frame >= cached_frame) {
127
128 // special case for end packets
129 if (CachedIdx == (uint32)Keys.Count ()-1) return(CachedIdx);
130
131 // check if the requested time is still in the cached interval
132 if (frame < Keys[CachedIdx + 1].MorphFrame) return(CachedIdx);
133
134 // do one time look-ahead before reverting to a search
135 CachedIdx++;
136
137 // again, special case the end interval
138 if (CachedIdx == (uint32)Keys.Count ()-1) return(CachedIdx);
139
140 // check if requested time is in this interval
141 if (frame < Keys[CachedIdx + 1].MorphFrame) return(CachedIdx);
142 }
143
144 // nope, fall back to a binary search for the requested interval
145 CachedIdx = binary_search_index(frame);
146
147 return(CachedIdx);
148}
149
150uint32 TimeCodedMorphKeysClass::binary_search_index(float req_frame)
151{
152 // special case first and last packet
153 if (req_frame < Keys[0].MorphFrame) return 0;
154 if (req_frame >= Keys[Keys.Count ()-1].MorphFrame) return Keys.Count ()-1;
155
156 int leftIdx = 0;
157 int rightIdx = Keys.Count ();
158 int idx,dx;
159
160 // binary search for the desired interval
161 for (;;) {
162
163 // if we've zeroed in on the interval, return the left index
164 dx = rightIdx - leftIdx;
165 if (dx == 1) {
166 WWASSERT(req_frame >= Keys[leftIdx].MorphFrame);
167 WWASSERT(req_frame < Keys[rightIdx].MorphFrame);
168 return leftIdx;
169 }
170
171 // otherwise, split our interval in half and descend into one of them
172 dx>>=1;
173 idx = leftIdx + dx;
174
175 if (req_frame < Keys[idx].MorphFrame) {
176 rightIdx = idx;
177 } else {
178 leftIdx = idx;
179 }
180 }
181
182 assert(0);
183 return(0);
184}
185
186
187/*********************************************************************************************
188**
189** HMorphAnimClass Implementation
190**
191*********************************************************************************************/
192
194 FrameCount(0),
195 FrameRate(0.0f),
196 ChannelCount(0),
197 NumNodes(0),
198 PoseData(NULL),
201{
202 memset(Name,0,sizeof(Name));
203 memset(AnimName,0,sizeof(AnimName));
204 memset(HierarchyName,0,sizeof(HierarchyName));
205}
206
211
213{
214 if (PoseData != NULL) {
215 for (int i=0; i<ChannelCount; i++) {
217 }
218 delete[] PoseData;
219 PoseData = NULL;
220 }
221
222 if (MorphKeyData != NULL) {
223 delete[] MorphKeyData;
225 }
226
227 if (PivotChannel != NULL) {
228 delete[] PivotChannel;
230 }
231}
232
233
234static int Build_List_From_String
235(
236 const char * buffer,
237 const char * delimiter,
238 StringClass ** string_list
239)
240{
241 int count = 0;
242
243 WWASSERT (buffer != NULL);
244 WWASSERT (delimiter != NULL);
245 WWASSERT (string_list != NULL);
246 if ((buffer != NULL) &&
247 (delimiter != NULL) &&
248 (string_list != NULL))
249 {
250 int delim_len = ::strlen (delimiter);
251
252 //
253 // Determine how many entries there will be in the list
254 //
255 for (const char *entry = buffer;
256 (entry != NULL) && (entry[1] != 0);
257 entry = ::strstr (entry, delimiter))
258 {
259
260 //
261 // Move past the current delimiter (if necessary)
262 //
263 if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
264 entry += delim_len;
265 }
266
267 // Increment the count of entries
268 count ++;
269 }
270
271 if (count > 0) {
272
273 //
274 // Allocate enough StringClass objects to hold all the strings in the list
275 //
276 (*string_list) = W3DNEWARRAY StringClass[count];
277
278 //
279 // Parse the string and pull out its entries.
280 //
281 count = 0;
282 for (entry = buffer;
283 (entry != NULL) && (entry[1] != 0);
284 entry = ::strstr (entry, delimiter))
285 {
286
287 //
288 // Move past the current delimiter (if necessary)
289 //
290 if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
291 entry += delim_len;
292 }
293
294 //
295 // Copy this entry into its own string
296 //
297 StringClass entry_string = entry;
298 char *delim_start = ::strstr (entry_string, delimiter);
299 if (delim_start != NULL) {
300 delim_start[0] = 0;
301 }
302
303 //
304 // Add this entry to our list
305 //
306 if ((entry_string.Get_Length () > 0) || (count == 0)) {
307 (*string_list)[count++] = entry_string;
308 }
309 }
310
311 } else if (delim_len > 0) {
312 count = 1;
313 (*string_list) = W3DNEWARRAY StringClass[count];
314 (*string_list)[0] = buffer;
315 }
316
317 }
318
319 //
320 // Return the number of entries in our list
321 //
322 return count;
323}
324
325
326bool Is_Number (const char *str)
327{
328 bool retval = true;
329
330 while (retval && str[0] != NULL){
331 retval = ((str[0] >= '0' && str[0] <= '9') || str[0] == '-' || str[0] == '.');
332 str ++;
333 }
334
335 return retval;
336}
337
338
339bool HMorphAnimClass::Import(const char *hierarchy_name, TextFileClass &text_desc)
340{
341 bool retval = false;
342 Free ();
343 FrameCount = 0;
344 FrameRate = 30.0F;
345
346 //
347 // Copy the hierarchy name into a class variable
348 //
349 ::strncpy (HierarchyName, hierarchy_name, W3D_NAME_LEN);
351
352 //
353 // Attempt to load the new base pose
354 //
356 WWASSERT (base_pose != NULL);
357 NumNodes = base_pose->Num_Pivots();
358
359 //
360 // Read the header from the file
361 //
362 StringClass header;
363 bool success = text_desc.Read_Line (header);
364 if (success) {
365
366 //
367 // Get the list of comma-delimited strings from the header
368 //
369 StringClass *column_list = NULL;
370 int column_count = Build_List_From_String (header, ",", &column_list);
371
372 //
373 // The first column header should be 'Frame#', all other headers
374 // should be channel animation names.
375 //
376 ChannelCount = column_count - 1;
377
379 if (ChannelCount > 0) {
380
381 //
382 // Allocate and initialize each animation channel
383 //
386 for (int index = 0; index < ChannelCount; index ++) {
387 StringClass channel_anim_name;
388 channel_anim_name.Format ("%s.%s", HierarchyName, (const char *)column_list[index + 1]);
389 PoseData[index] = WW3DAssetManager::Get_Instance()->Get_HAnim (channel_anim_name);
390 }
391
392 //
393 // Now read the animation data for each frame
394 //
395 StringClass frame_desc;
396 while (text_desc.Read_Line (frame_desc)) {
397
398 //
399 // Get the frame descriptions from this line
400 //
401 StringClass *channel_list = NULL;
402 int list_count = Build_List_From_String (frame_desc, ",", &channel_list);
403
404 WWASSERT (list_count > 0);
405 if (list_count > 0) {
406
407 //
408 // The first column contains the morph frame number
409 //
410 int frame = ::atoi (channel_list[0]);
411
412 //
413 // Now read the animation frame number for each channel
414 //
415 for (int index = 1; index < list_count; index ++) {
416 StringClass &channel_frame = channel_list[index];
417
418 //
419 // If this channel contains a valid number, then record
420 // its animation frame
421 //
422 if (::Is_Number (channel_frame)) {
423 MorphKeyData[index - 1].Add_Key (frame, ::atoi (channel_frame));
424 }
425 }
426
427 FrameCount = frame + 1;
428 }
429
430 //
431 // Cleanup
432 //
433 if (channel_list != NULL) {
434 delete [] channel_list;
435 channel_list = NULL;
436 }
437 }
438
439 //
440 // Allocate the pivot channel list
441 //
444 }
445
446 //
447 // Cleanup
448 //
449 if (column_list != NULL) {
450 delete [] column_list;
451 column_list = NULL;
452 }
453 }
454
455 return retval;
456}
457
459{
461
462 //
463 // Loop over all the pivots in the HTree
464 //
465 for (int pivot = 0; pivot < NumNodes; pivot ++) {
466 PivotChannel[pivot] = 0;
467
468 //
469 // Find out which animation channel affects this pivot
470 //
471 for (int channel = 0; channel < ChannelCount; channel ++) {
472 if (PoseData[channel]->Is_Node_Motion_Present (pivot)) {
473 PivotChannel[pivot] = channel;
474 }
475 }
476 }
477
478 return ;
479}
480
481void HMorphAnimClass::Set_Name(const char * name)
482{
483 //
484 // Copy the full name
485 //
486 ::strcpy (Name, name);
487
488 //
489 // Try to find the separator (a period)
490 //
491 StringClass full_name = name;
492 char *separator = ::strchr (full_name, '.');
493 if (separator != NULL) {
494
495 //
496 // Null out the separator and copy the two names
497 // into our two buffers
498 //
499 separator[0] = 0;
500 ::strcpy (AnimName, separator + 1);
501 ::strcpy (HierarchyName, full_name);
502 }
503
504 return ;
505}
506
508{
509 Free();
510}
511
512int HMorphAnimClass::Create_New_Morph(const int channels, HAnimClass *anim[])
513{
514 // clean out the previous instance of this class
515 Free();
516
517 // set the number of channels
518 ChannelCount = channels;
519
520 // read in the animation header
521 if (anim == NULL) {
522 return LOAD_ERROR;
523 }
524
525 // set up info
526 // FrameCount = anim[0]->Get_Num_Frames();
527 FrameCount = 0;
528 FrameRate = anim[0]->Get_Frame_Rate();
529 NumNodes = anim[0]->Get_Num_Pivots();
530
531 // Set up the anim data for all the channels
533 for(int i=0;i<ChannelCount;i++)
534 PoseData[i] = anim[i];
535
536 // Create a timecodekey array for each channel and initialize the pivot channels
539
540 // Resolve the pivots so that they correspond to the proper morphing channels
541 memset(PivotChannel,0,NumNodes * sizeof(uint32));
543
544 // Signal successful process
545 return OK;
546}
547
549{
550 Free();
551
552 // read in the animation header
554 cload.Open_Chunk();
556 cload.Read(&header,sizeof(header));
557 cload.Close_Chunk();
558
559 strncpy(AnimName,header.Name,sizeof(AnimName));
560 strncpy(HierarchyName,header.HierarchyName,sizeof(HierarchyName));
561 strcpy(Name,HierarchyName);
562 strcat(Name,".");
563 strcat(Name,AnimName);
564
566 if (base_pose == NULL) {
567 return LOAD_ERROR;
568 }
569 NumNodes = base_pose->Num_Pivots();
570
571 FrameCount = header.FrameCount;
572 FrameRate = header.FrameRate;
573 ChannelCount = header.ChannelCount;
574
578 memset(PivotChannel,0,NumNodes * sizeof(uint32));
579
580 // read in the rest of the chunks
581 int cur_channel = 0;
582 while (cload.Open_Chunk()) {
583 switch(cload.Cur_Chunk_ID())
584 {
586 read_channel(cload,cur_channel++);
587 break;
588
590 cload.Read(PivotChannel,cload.Cur_Chunk_Length());
591 break;
592 };
593 cload.Close_Chunk();
594 }
595 return OK;
596
597}
598
600{
601 WWASSERT(channel >= 0);
602 WWASSERT(channel < ChannelCount);
603
604 StringClass anim_name;
605
606 cload.Open_Chunk();
608 cload.Read(anim_name.Get_Buffer(cload.Cur_Chunk_Length()),cload.Cur_Chunk_Length());
609 cload.Close_Chunk();
610
611 //StringClass channel_anim_name;
612 //channel_anim_name.Format ("%s.%s", HierarchyName, anim_name);
613 PoseData[channel] = WW3DAssetManager::Get_Instance()->Get_HAnim(anim_name);
614 WWASSERT(PoseData[channel] != NULL);
615
616 cload.Open_Chunk();
618 MorphKeyData[channel].Load_W3D(cload);
619 cload.Close_Chunk();
620}
621
622
624{
625 // W3D objects write their own wrapper chunks
627
628 // init the header data
630 memset(&header,0,sizeof(header));
631 strncpy(header.Name,AnimName,sizeof(header.Name));
632 strncpy(header.HierarchyName,HierarchyName,sizeof(header.HierarchyName));
633
634 header.FrameCount = FrameCount;
635 header.FrameRate = FrameRate;
636 header.ChannelCount = ChannelCount;
637
638 // write out the animation header
640 csave.Write(&header,sizeof(header));
641 csave.End_Chunk();
642
643 // write out the morph channels
644 for (int ci=0; ci<ChannelCount; ci++) {
646 write_channel(csave,ci);
647 csave.End_Chunk();
648 }
649
650 // write out the pivot attachments
652 csave.Write(PivotChannel,NumNodes * sizeof(uint32));
653 csave.End_Chunk();
654
655 csave.End_Chunk();
656 return OK;
657}
658
660{
661 WWASSERT(PoseData[channel] != NULL);
662
663 const char * pose_name = PoseData[channel]->Get_Name();
665 csave.Write(pose_name,strlen(pose_name) + 1);
666 csave.End_Chunk();
667
669 MorphKeyData[channel].Save_W3D(csave);
670 csave.End_Chunk();
671}
672
673
674void HMorphAnimClass::Get_Translation(Vector3& trans,int pividx,float frame) const
675{
676 int channel = PivotChannel[pividx];
677 int pose_frame0,pose_frame1;
678 float fraction;
679 MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
680
681 Vector3 t0;
682 PoseData[channel]->Get_Translation(t0,pividx,pose_frame0);
683 Vector3 t1;
684 PoseData[channel]->Get_Translation(t1,pividx,pose_frame1);
685 Vector3::Lerp(t0,t1,fraction,&trans);
686}
687
688void HMorphAnimClass::Get_Orientation(Quaternion& q, int pividx,float frame) const
689{
690 int channel = PivotChannel[pividx];
691 int pose_frame0,pose_frame1;
692 float fraction;
693 MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
694
695 Quaternion q0;
696 PoseData[channel]->Get_Orientation(q0,pividx,pose_frame0);
697 Quaternion q1;
698 PoseData[channel]->Get_Orientation(q1,pividx,pose_frame1);
699 ::Fast_Slerp(q,q0,q1,fraction);
700}
701
702void HMorphAnimClass::Get_Transform(Matrix3D& mtx,int pividx,float frame) const
703{
704 int channel = PivotChannel[pividx];
705 int pose_frame0,pose_frame1;
706 float fraction;
707 MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
708
709 Quaternion q0;
710 PoseData[channel]->Get_Orientation(q0,pividx,pose_frame0);
711 Quaternion q1;
712 PoseData[channel]->Get_Orientation(q1,pividx,pose_frame1);
714 ::Fast_Slerp(q,q0,q1,fraction);
715 ::Build_Matrix3D(q,mtx);
716 Vector3 t0;
717 PoseData[channel]->Get_Translation(t0,pividx,pose_frame0);
718 Vector3 t1;
719 PoseData[channel]->Get_Translation(t1,pividx,pose_frame1);
720 Vector3 trans;
721 Vector3::Lerp(t0,t1,fraction,&trans);
722 mtx.Set_Translation(trans);
723}
724
725
726void HMorphAnimClass::Insert_Morph_Key(const int channel, uint32 morph_frame, uint32 pose_frame)
727{
728 assert(channel<ChannelCount);
729 MorphKeyData[channel].Add_Key(morph_frame,pose_frame);
730
731 // update the framecount to reflect the newly added key
732 FrameCount = WWMath::Max(morph_frame,FrameCount);
733}
734
736{
737 for(int i=0;i<ChannelCount;i++)
738 MorphKeyData[i].Free();
739
740 // update the framecount as 0
741 FrameCount = 0;
742}
#define NULL
Definition BaseType.h:92
#define WWASSERT
#define W3D_NAME_LEN
Definition w3d_file.h:319
@ W3D_CHUNK_MORPHANIM_PIVOTCHANNELDATA
Definition w3d_file.h:416
@ W3D_CHUNK_MORPHANIM_HEADER
Definition w3d_file.h:412
@ W3D_CHUNK_MORPHANIM_CHANNEL
Definition w3d_file.h:413
@ W3D_CHUNK_MORPHANIM_POSENAME
Definition w3d_file.h:414
@ W3D_CHUNK_MORPH_ANIMATION
Definition w3d_file.h:411
@ W3D_CHUNK_MORPHANIM_KEYDATA
Definition w3d_file.h:415
#define W3DNEWARRAY
Definition always.h:110
unsigned long uint32
Definition bittype.h:46
bool Close_Chunk()
Definition chunkio.cpp:448
uint32 Cur_Chunk_ID()
Definition chunkio.cpp:484
uint32 Cur_Chunk_Length()
Definition chunkio.cpp:503
uint32 Read(void *buf, uint32 nbytes)
Definition chunkio.cpp:692
bool Open_Chunk()
Definition chunkio.cpp:412
uint32 Write(const void *buf, uint32 nbytes)
Definition chunkio.cpp:264
bool Begin_Chunk(uint32 id)
Definition chunkio.cpp:108
bool End_Chunk()
Definition chunkio.cpp:148
virtual float Get_Frame_Rate()=0
HAnimClass(void)
Definition hanim.h:85
virtual int Get_Num_Pivots(void) const =0
void Free(void)
void Insert_Morph_Key(const int channel, uint32 morph_frame, uint32 pose_frame)
int Save_W3D(ChunkSaveClass &csave)
void Resolve_Pivot_Channels(void)
char AnimName[W3D_NAME_LEN]
Definition hmorphanim.h:123
void Free_Morph(void)
TimeCodedMorphKeysClass * MorphKeyData
Definition hmorphanim.h:132
char HierarchyName[W3D_NAME_LEN]
Definition hmorphanim.h:124
bool Is_Node_Motion_Present(int pividx)
Definition hmorphanim.h:107
bool Import(const char *hierarchy_name, TextFileClass &text_desc)
void read_channel(ChunkLoadClass &cload, int channel)
int Load_W3D(ChunkLoadClass &cload)
char Name[2 *W3D_NAME_LEN]
Definition hmorphanim.h:122
void Get_Transform(Matrix3D &transform, int pividx, float frame) const
void Get_Translation(Vector3 &translation, int pividx, float frame) const
void write_channel(ChunkSaveClass &csave, int channel)
void Set_Name(const char *name)
uint32 * PivotChannel
Definition hmorphanim.h:133
HAnimClass ** PoseData
Definition hmorphanim.h:131
void Release_Keys(void)
void Get_Orientation(Quaternion &orientation, int pividx, float frame) const
int Create_New_Morph(const int channels, HAnimClass *anim[])
WWINLINE int Num_Pivots(void) const
Definition htree.h:93
WWINLINE void Set_Translation(const Vector3 &t)
Definition matrix3d.h:219
void Delete_All(bool allow_shrink=true)
Definition simplevec.h:547
int Count(void) const
Definition simplevec.h:263
int _cdecl Format(const TCHAR *format,...)
Definition wwstring.cpp:273
TCHAR * Get_Buffer(int new_length)
Definition wwstring.h:549
int Get_Length(void) const
Definition wwstring.h:664
bool Read_Line(StringClass &string)
Definition textfile.cpp:79
void Get_Morph_Info(float morph_frame, int *pose_frame0, int *pose_frame1, float *fraction)
void Add_Key(uint32 morph_frame, uint32 pose_frame)
bool Load_W3D(ChunkLoadClass &cload)
bool Save_W3D(ChunkSaveClass &csave)
static void Lerp(const Vector3 &a, const Vector3 &b, float alpha, Vector3 *set_result)
Definition vector3.h:535
static WW3DAssetManager * Get_Instance(void)
Definition assetmgr.h:205
virtual HAnimClass * Get_HAnim(const char *name)
Definition assetmgr.cpp:978
virtual HTreeClass * Get_HTree(const char *name)
static float Max(float a, float b)
Definition wwmath.h:264
bool Is_Number(const char *str)
else return(RetVal)
int q
Definition test1.cpp:94
void __cdecl Fast_Slerp(Quaternion &res, const Quaternion &p, const Quaternion &q, float alpha)
Definition quat.cpp:441
Matrix3D & Build_Matrix3D(const Quaternion &q, Matrix3D &out)
Definition quat.h:208
#define REF_PTR_RELEASE(x)
Definition refcount.h:80
char Name[W3D_NAME_LEN]
Definition w3d_file.h:1543
char HierarchyName[W3D_NAME_LEN]
Definition w3d_file.h:1544