Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
util.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/* $Header: /Commando/Code/Tools/max2w3d/util.cpp 28 10/27/00 4:12p Greg_h $ */
20/***********************************************************************************************
21 *** Confidential - Westwood Studios ***
22 ***********************************************************************************************
23 * *
24 * Project Name : Commando Tools - W3D export *
25 * *
26 * $Archive:: /Commando/Code/Tools/max2w3d/util.cpp $*
27 * *
28 * $Author:: Greg_h $*
29 * *
30 * $Modtime:: 10/27/00 1:13p $*
31 * *
32 * $Revision:: 28 $*
33 * *
34 *---------------------------------------------------------------------------------------------*
35 * Functions: *
36 * Cleanup_Orthogonal_Matrix -- removes very small numbers from the matrix *
37 * Set_W3D_Name -- set a W3D name *
38 * Split_Node_Name -- break a node name into the base and extension *
39 * Is_Max_Tri_Mesh -- Is this node a triangle mesh? *
40 * -- checks if the node is the origin of a model *
41 * -- Checks if the node is the origin for the base obect (non-LOD'd). *
42 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
43
44#include "util.h"
45#include "w3dutil.h"
46#include "skin.h"
47#include "skindata.h"
48#include "modstack.h"
49
50
51#define MAX_NODE_NAME_LEN 256 // max name size we can handle
52
53const float EPSILON = 0.00001f;
54static char _string[256];
55
56
57
58static int get_geometry_type(INode * node)
59{
60 assert(node != NULL);
62
63 //return (get_w3d_bits(node) & GEO_TYPE_MASK);
64}
65
66
67/***********************************************************************************************
68 * Cleanup_Orthogonal_Matrix -- removes very small numbers from the matrix *
69 * *
70 * INPUT: *
71 * *
72 * OUTPUT: *
73 * *
74 * WARNINGS: *
75 * *
76 * HISTORY: *
77 * 10/26/1997 GH : Created. *
78 *=============================================================================================*/
80{
81 Matrix3 newmat = mat;
82
83 for (int j=0; j<3; j++) {
84 Point3 row = newmat.GetRow(j);
85
86 if (fabs(row.x) < EPSILON) row.x = 0.0f;
87 if (fabs(row.y) < EPSILON) row.y = 0.0f;
88 if (fabs(row.z) < EPSILON) row.z = 0.0f;
89
90 row = Normalize(row);
91
92 newmat.SetRow(j,row);
93 }
94
95 return newmat;
96}
97
98/***********************************************************************************************
99 * Set_W3D_Name -- set a W3D name *
100 * *
101 * INPUT: *
102 * *
103 * OUTPUT: *
104 * *
105 * WARNINGS: *
106 * *
107 * HISTORY: *
108 * 10/26/1997 GH : Created. *
109 * 9/13/1999 AJA : Strip off the trailing ".digits" since this is a convention we've set in *
110 * MAX to help artists manage LODs. *
111 *=============================================================================================*/
112void Set_W3D_Name(char * set_name,const char * src)
113{
114 memset(set_name,0,W3D_NAME_LEN);
115 strncpy(set_name,src,W3D_NAME_LEN-1);
116 char *dot = strrchr(set_name, '.');
117 if (dot)
118 {
119 // If a number comes after the dot, strip it off
120 int value;
121 if (sscanf(dot+1, "%d", &value) == 1)
122 *dot = 0;
123 // If nothing comes after the dot, strip it off
124 else if (*(dot+1) == 0)
125 *dot = 0;
126 }
127 strupr(set_name);
128}
129
130/***********************************************************************************************
131 * Split_Node_Name -- break a node name into the base and extension *
132 * *
133 * INPUT: *
134 * *
135 * OUTPUT: *
136 * *
137 * WARNINGS: *
138 * *
139 * HISTORY: *
140 * 10/26/1997 GH : Created. *
141 *=============================================================================================*/
142void Split_Node_Name(const char * name,char * set_base,char * set_exten,int * set_exten_index)
143{
144 // Nodes are assumed to be named in the following way:
145 // <name>.<exten character><exten digits>
146 // for example: mesh.d1
147
148 char buf[MAX_NODE_NAME_LEN];
149 char * ptr;
150
151 assert(strlen(name) < MAX_NODE_NAME_LEN);
152
153 // Initialize
154 if (set_base != NULL) set_base[0] = 0;
155 if (set_exten != NULL) set_exten[0] = 0;
156 if (set_exten_index != NULL) *set_exten_index = 0;
157
158 // Get the base name
159 strncpy(buf,name,MAX_NODE_NAME_LEN);
160 ptr = buf;
161 while ((*ptr != 0) && (*ptr != '.')) {
162 ptr++;
163 }
164
165 if (*ptr == '.') {
166
167 // copy what we have so far into set_base
168 *ptr = 0;
169 if (set_base != NULL) strncpy(set_base,buf,MAX_NODE_NAME_LEN);
170
171 // copy the rest back into the extension
172 ptr++;
173 if (set_exten != NULL) strncpy(set_exten,ptr,MAX_NODE_NAME_LEN);
174
175 // now get the extension index
176 ptr++;
177 if (set_exten_index != NULL) *set_exten_index = atoi(ptr);
178
179 } else {
180
181 // no extension, just copy the base name
182 if (set_base != NULL) strncpy(set_base,buf,MAX_NODE_NAME_LEN);
183 return;
184 }
185}
186
187
188bool Append_Lod_Character (char *meshname, int lod_level, INodeListClass *origin_list)
189{
190 if (meshname == NULL || lod_level < 0)
191 return false;
192
193 if (!origin_list)
194 return false;
195
196 int num_lods = origin_list->Num_Nodes();
197
198 /*
199 ** Search the other LODs to see if there is a mesh with the same name.
200 ** If there is, we will append the current LOD level digit to the name.
201 ** If there is not, the name will not be modified.
202 */
203 INode *conflict = NULL, *cur_origin = NULL;
204 int i, lod;
205 for (i = 0; i < num_lods; i++)
206 {
207 // Don't bother searching the current LOD.
208 lod = Get_Lod_Level((*origin_list)[i]);
209 if (lod == lod_level)
210 continue;
211
212 // Search this lod for a node of the same name.
213 conflict = Find_Named_Node(meshname, (*origin_list)[i]);
214 if (conflict)
215 {
216 // Name length is a worry here, because the name plus the number
217 // must be less than W3D_NAME_LEN (which is fairly small!).
218 int length = strlen(meshname);
219 if ( (lod_level < 10) && (length < W3D_NAME_LEN - 1) )
220 {
221 // Append a number corresponding to the LOD level to the mesh name.
222 // Highest-detail LOD is '0' (convention).
223 char *insert = meshname + length;
224 *insert++ = '0' + lod_level;
225 *insert = '\0';
226 }
227 else if ( (lod_level < 100) && (length < W3D_NAME_LEN - 2) )
228 {
229 // Append a number corresponding to the LOD level to the mesh name.
230 // Highest-detail LOD is '0' (convention).
231 char buf[3];
232 sprintf(buf, "%d", lod_level);
233 strcat(meshname, buf);
234 }
235 else
236 {
237 // Replace the last character of the mesh name with the lod character (as above).
238 meshname[W3D_NAME_LEN-2] = '0' + lod_level;
239 }
240
241 // Name mangling finished (conflict with other LODs is solved on their pass).
242 break;
243 }
244 }
245
246 return true;
247}
248
249
250
251void Create_Full_Path(char *full_path, const char *curr, const char *rel_path)
252{
253 // Copy current dir to full path. If it doesn't end with a slash, add one.
254 strcpy(full_path, curr);
255 int curr_len = strlen(curr);
256 char *full_p = full_path + curr_len; // Point at the terminating NULL
257 if (curr_len == 0 ||(*(full_p - 1) != '/' && *(full_p - 1) != '\\')) {
258 *full_p = '\\';
259 *(++full_p) = '\000'; // Point at the terminating NULL
260 }
261
262 // Scan "..\"s at the beginning of the rel path, scan backwards on the
263 // full path (current dir):
264 const char *rel_p;
265 for ( rel_p = rel_path;
266 *rel_p == '.' && *(rel_p+1) == '.' && ( *(rel_p+2) == '/' || *(rel_p+2) == '\\' );
267 rel_p += 3)
268 {
269 full_p--;
270 for (; full_p > full_path && *(full_p-1) != '/' && *(full_p-1) != '\\'; full_p--);
271 *full_p = '\000';
272 }
273
274 // Copy the remainder of the relative path to the full path:
275 strcpy(full_p, rel_p);
276}
277
278// This enum is used inside Create_Relative_Path:
284
285void Create_Relative_Path(char *rel_path, const char *curr, const char *full_path)
286{
287 // Copy both constant strings and convert them to uppercase:
288 int curr_len = strlen(curr);
289 char *up_curr = (char *)malloc(curr_len + 1);
290 strcpy(up_curr, curr);
291 _strupr(up_curr);
292
293 int full_len = strlen(full_path);
294 char *up_full = (char *)malloc(full_len + 1);
295 strcpy(up_full, full_path);
296 _strupr(up_full);
297
298 char *rel_p = rel_path;
299
300 // Find shared prefix of curr and full path
301 const char *full_p = up_full;
302 const char *curr_p = up_curr;
303 for ( ;
304 *full_p && *full_p == *curr_p || (*full_p == '/' && *curr_p == '\\') || (*full_p == '\\' && *curr_p == '/');
305 full_p++, curr_p++
306 );
307
308 // If no shared prefix at this point set the relative path to 0
309 // This will force the code to use the absolute path.
310 if (full_p == up_full) {
311 rel_path[0] = 0;
312 goto end;
313 }
314
315 // The first different character for each string can be: a NULL, a slash,
316 // or an ordinary character.
317 PathCharType full_type, curr_type;
318 if (*full_p == '\000') {
319 full_type = NULL_CHAR;
320 } else {
321 if (*full_p == '/' || *full_p == '\\') {
322 full_type = SLASH_CHAR;
323 } else {
324 full_type = PLAIN_CHAR;
325 }
326 }
327 if (*curr_p == '\000') {
328 curr_type = NULL_CHAR;
329 } else {
330 if (*curr_p == '/' || *curr_p == '\\') {
331 curr_type = SLASH_CHAR;
332 } else {
333 curr_type = PLAIN_CHAR;
334 }
335 }
336 // If the last fullpath char is a NULL or both are slashes, we have an
337 // error - return full path
338 if (full_type == NULL_CHAR || (full_type == SLASH_CHAR && curr_type == SLASH_CHAR)) {
339 strcpy(rel_path, up_full);
340 goto end;
341 }
342
343 // If the current path has ended (last char is a NULL) and the full path's
344 // last char is a slash, then just copy the remainder of the full path
345 // (w/o the slash) to the relative path, and exit.
346 if (curr_type == NULL_CHAR && full_type == SLASH_CHAR) {
347 full_p++; // skip slash
348 strcpy(rel_path, full_p);
349 goto end;
350 }
351
352 // If one of following holds:
353 // 1) One of the last chars is a slash and the other is a plain char
354 // 2) The current path has ended (last char is NULL) and the last char
355 // of the full path is a plain char
356 // 3) The last char of both are plain chars and the previous char is not a
357 // slash
358 // Then we must backtrack both pointers until the characters before them
359 // are slashes. If there are no previous slashes, we have an error.
360 if ( (full_type == SLASH_CHAR && curr_type == PLAIN_CHAR) ||
361 (curr_type == SLASH_CHAR && full_type == PLAIN_CHAR) ||
362 (curr_type == NULL_CHAR && full_type == PLAIN_CHAR) ||
363 (curr_type == PLAIN_CHAR && full_type == PLAIN_CHAR &&
364 *(full_p-1) != '/' && *(full_p-1) != '\\') )
365 {
366 for (;
367 full_p > up_full &&
368 ( (*(full_p - 1) != '/' && *(full_p - 1) != '\\') ||
369 (*(curr_p - 1) != '/' && *(curr_p - 1) != '\\') );
370 full_p--, curr_p--);
371 }
372
373 // If no shared prefix at this point (not even a drive letter) return the
374 // full path
375 if (full_p == up_full) {
376 strcpy(rel_path, up_full);
377 goto end;
378 }
379
380 // Scan all directories levels in current path from shared point to end -
381 // for each one add a "../" to the relative path. Note that at this point
382 // we know we have to add at least one.
383 *rel_p++ = '.';
384 *rel_p++ = '.';
385 *rel_p++ = '\\';
386 // Go over remaining current path, for each slash we find add one "../" to
387 // the relative path
388 for (; *curr_p; curr_p++) {
389 if (*curr_p == '/' || *curr_p == '\\') {
390 *rel_p++ = '.';
391 *rel_p++ = '.';
392 *rel_p++ = '\\';
393 }
394 }
395
396 // If the last char of the current path is a slash remove a "../" from the
397 // relative path.
398 if (*(curr_p - 1) == '/' || *(curr_p - 1) == '\\') {
399 rel_p -= 3;
400 }
401
402 // Copy remaining full path (from shared point to end) to relative path
403 strcpy(rel_p, full_p);
404
405end:
406 free(up_curr);
407 free(up_full);
408}
409
410bool Is_Full_Path(char * path)
411{
412 // first scan for a drive letter (scan for a colon)
413 if (strchr(path,':') != NULL) {
414 return true;
415 }
416
417 // now scan for a "network" path (starts with "//")
418 if ((path[0] == '/') && (path[1] == '/')) {
419 return true;
420 }
421 if ((path[0] == '\\') && (path[1] == '\\')) {
422 return true;
423 }
424
425 return false;
426}
427
428
429/***********************************************************************************************
430 * Is_Max_Tri_Mesh -- Is this node a triangle mesh? *
431 * *
432 * INPUT: *
433 * *
434 * OUTPUT: *
435 * *
436 * WARNINGS: *
437 * *
438 * HISTORY: *
439 * 2/2/98 GTH : Created. *
440 *=============================================================================================*/
441bool Is_Max_Tri_Mesh(INode * node)
442{
443 Object *obj = node->EvalWorldState(0).obj;
444
445 if (obj && obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) {
446 return true;
447 }
448 return false;
449}
450
451
452
453bool Is_Damage_Root(INode *node)
454{
455 if (node == NULL)
456 return false;
457
458 // Is the node's parent the scene root?
459 INode *parent = node->GetParentNode();
460 if (!parent || !parent->IsRootNode())
461 return false;
462
463 // Is the node's name in the form "damage.*"?
464 char *name = node->GetName();
465 if (strnicmp(name, "damage.", strlen("damage.")) != 0)
466 return false;
467
468/* This won't pick up references to a dummy object for some reason.
469 // Does the node point to a dummy object?
470 Object *obj = node->GetObjectRef();
471 if (!obj || !obj->CanConvertToType(Class_ID(DUMMY_CLASS_ID, 0)))
472 return false;
473*/
474
475 return true;
476}
477
478
479/***********************************************************************************************
480 * Is_Origin -- checks if the node is the origin of a model *
481 * *
482 * A node is an origin if its parent is the scene root, it is a dummy object, and its name *
483 * is of the form "origin.*" (case insensitive). All descendants of the origin will be *
484 * expressed in coordinates relative to the origin object. *
485 * *
486 * INPUT: *
487 * *
488 * OUTPUT: *
489 * *
490 * WARNINGS: *
491 * *
492 * HISTORY: *
493 * 9/13/99 AJA : Created.
494 *=============================================================================================*/
495bool Is_Origin(INode * node)
496{
497 if (!node) return false;
498 if (node->IsRootNode()) return true;
499 if (node->IsHidden()) return false;
500
501 // Is the node's parent the scene root?
502 INode *parent = node->GetParentNode();
503 if (!parent || !parent->IsRootNode())
504 return false;
505
506 // Is the node's name in the form "origin.*"?
507 char *name = node->GetName();
508 if (strnicmp(name, "origin.", strlen("origin.")) != 0)
509 return false;
510
511/* This won't pick up references to a dummy object for some reason.
512 // Does the node point to a dummy object?
513 Object *obj = node->GetObjectRef();
514 if (!obj || !obj->CanConvertToType(Class_ID(DUMMY_CLASS_ID, 0)))
515 return false;
516*/
517
518 // This is an origin.
519 return true;
520}
521
522
523/***********************************************************************************************
524 * Is_Base_Origin -- Checks if the node is the origin for the base obect (non-LOD'd). *
525 * *
526 * INPUT: *
527 * *
528 * OUTPUT: *
529 * *
530 * WARNINGS: *
531 * *
532 * HISTORY: *
533 * 9/13/1999 AJA : Created. *
534 *=============================================================================================*/
535bool Is_Base_Origin(INode * node)
536{
537 if (!node) return false;
538
539 if (node->IsRootNode()) return true;
540
541 if (!Is_Origin(node)) return false;
542
543 // An origin is the base object origin if it's name is "origin." or
544 // "origin.0" (a numeric value evaluating to zero as scanned by sscanf
545 // which would include "origin.00" "origin.000", etc.).
546 bool is_base_origin = false;
547 char *name = node->GetName();
548 if (stricmp(name, "origin.") == 0)
549 is_base_origin = true;
550 else if (strlen(name) > strlen("origin."))
551 {
552 // We know the first 7 characters are "origin." because that
553 // was tested in Is_Origin(). Is it "origin.0"?
554 int idx;
555 if ((sscanf(name+strlen("origin."), "%d", &idx) == 1) && (idx == 0))
556 is_base_origin = true;
557 }
558
559 return is_base_origin;
560}
561
562
563int Get_Lod_Level(INode *node)
564{
565 if (node->IsRootNode()) return 0;
566 if (!Is_Origin(node)) return -1;
567
568 char *name = node->GetName();
569 char *dot = strrchr(name, '.');
570 assert(dot);
571 return atoi(dot+1);
572}
573
574int Get_Damage_State(INode *node)
575{
576 if (!Is_Damage_Root(node)) return -1;
577
578 char *name = node->GetName();
579 char *dot = strrchr(name, '.');
580 assert(dot);
581 return atoi(dot+1);
582}
583
584INode *Find_Named_Node(char *nodename, INode *root)
585{
586 if (!root || !nodename)
587 return NULL;
588
589 // Perform a breadth-first search of the tree for a node
590 // of the given name.
591 INode *child = NULL;
592 int i;
593 char cur_name[W3D_NAME_LEN];
594
595 // Is this the node we're looking for?
596 Set_W3D_Name(cur_name, root->GetName());
597 if (strcmp(cur_name, nodename) == 0)
598 return root;
599
600 // Check the children against the given name.
601 for (i = 0; i < root->NumChildren(); i++)
602 {
603 // Is it this child?
604 child = root->GetChildNode(i);
605 Set_W3D_Name(cur_name, child->GetName());
606 if (strcmp(nodename, cur_name) == 0)
607 return child;
608 }
609
610 // Wasn't any children. Check each child's descendants.
611 for (i = 0; i < root->NumChildren(); i++)
612 {
613 child = root->GetChildNode(i);
614 INode *found = Find_Named_Node(nodename, child);
615 if (found)
616 return found;
617 }
618
619 // Didn't find the node anywhere.
620 return NULL;
621}
622
#define NULL
Definition BaseType.h:92
void const char * value
Real Normalize(Real valueToNormalize, Real minRange, Real maxRange)
const float EPSILON
Definition meshbuild.cpp:68
#define W3D_NAME_LEN
Definition w3d_file.h:319
unsigned Num_Nodes(void) const
Definition nodelist.h:75
int Get_Geometry_Type(void) const
Definition w3dappdata.h:281
static W3DAppData2Struct * Get_App_Data(INode *node, bool create_if_missing=true)
bool Is_Base_Origin(INode *node)
Definition util.cpp:535
int Get_Lod_Level(INode *node)
Definition util.cpp:563
PathCharType
Definition util.cpp:279
@ PLAIN_CHAR
Definition util.cpp:282
@ NULL_CHAR
Definition util.cpp:280
@ SLASH_CHAR
Definition util.cpp:281
void Split_Node_Name(const char *name, char *set_base, char *set_exten, int *set_exten_index)
Definition util.cpp:142
void Create_Relative_Path(char *rel_path, const char *curr, const char *full_path)
Definition util.cpp:285
Matrix3 Cleanup_Orthogonal_Matrix(Matrix3 &mat)
Definition util.cpp:79
void Set_W3D_Name(char *set_name, const char *src)
Definition util.cpp:112
bool Is_Full_Path(char *path)
Definition util.cpp:410
#define MAX_NODE_NAME_LEN
Definition util.cpp:51
INode * Find_Named_Node(char *nodename, INode *root)
Definition util.cpp:584
bool Is_Damage_Root(INode *node)
Definition util.cpp:453
void Create_Full_Path(char *full_path, const char *curr, const char *rel_path)
Definition util.cpp:251
bool Is_Origin(INode *node)
Definition util.cpp:495
bool Is_Max_Tri_Mesh(INode *node)
Definition util.cpp:441
bool Append_Lod_Character(char *meshname, int lod_level, INodeListClass *origin_list)
Definition util.cpp:188
int Get_Damage_State(INode *node)
Definition util.cpp:574