1 #include "LDLModel.h"
2 #include "LDLMainModel.h"
3 #include "LDLCommentLine.h"
4 #include "LDLModelLine.h"
5 #include "LDLFindFileAlert.h"
6 #include "LDrawIni.h"
7 #include <TCFoundation/TCDictionary.h>
8 #include <TCFoundation/mystring.h>
9 #include <TCFoundation/TCStringArray.h>
10 #include <TCFoundation/TCAlertManager.h>
11 #include <TCFoundation/TCProgressAlert.h>
12 #include <TCFoundation/TCLocalStrings.h>
13 #include <TCFoundation/TCUserDefaults.h>
14 #include <TCFoundation/TCImage.h>
15 #include <math.h>
16
17 #ifdef WIN32
18 #include <direct.h>
19
20 #if defined(_MSC_VER) && _MSC_VER >= 1400 && defined(_DEBUG)
21 #define new DEBUG_CLIENTBLOCK
22 #endif // _DEBUG
23
24 #else // WIN32
25 #include <unistd.h>
26 #endif // WIN32
27
28 #define LDL_LOWRES_PREFIX "LDL-LOWRES:"
29 #define LOAD_MESSAGE TCLocalStrings::get(_UC("LDLModelLoading"))
30 #define MAIN_READ_FRACTION 0.1f
31
32 char *LDLModel::sm_systemLDrawDir = NULL;
33 char *LDLModel::sm_defaultLDrawDir = NULL;
34 LDrawIniS *LDLModel::sm_lDrawIni = NULL;
35 int LDLModel::sm_modelCount = 0;
36 LDLFileCaseCallback LDLModel::fileCaseCallback = NULL;
37 LDLModel::LDLModelCleanup LDLModel::sm_cleanup;
38 StringList LDLModel::sm_checkDirs;
39
~LDLModelCleanup(void)40 LDLModel::LDLModelCleanup::~LDLModelCleanup(void)
41 {
42 delete[] LDLModel::sm_systemLDrawDir;
43 delete[] LDLModel::sm_defaultLDrawDir;
44 LDLModel::sm_systemLDrawDir = NULL;
45 if (LDLModel::sm_lDrawIni)
46 {
47 LDrawIniFree(LDLModel::sm_lDrawIni);
48 }
49 }
50
51
LDLModel(void)52 LDLModel::LDLModel(void)
53 :m_filename(NULL),
54 m_name(NULL),
55 m_author(NULL),
56 m_description(NULL),
57 m_fileLines(NULL),
58 m_mpdTexmapModels(NULL),
59 m_mpdTexmapLines(NULL),
60 m_mpdTexmapImages(NULL),
61 m_mainModel(NULL),
62 m_activeLineCount(0),
63 m_activeMPDModel(NULL),
64 m_texmapImage(NULL),
65 m_dataLine(NULL)
66 {
67 // Initialize Private flags
68 m_flags.loadingPart = false;
69 m_flags.loadingSubPart = false;
70 m_flags.loadingPrimitive = false;
71 m_flags.loadingUnoffic = false;
72 m_flags.mainModelLoaded = false;
73 m_flags.mainModelParsed = false;
74 m_flags.started = false;
75 m_flags.bfcClip = false;
76 m_flags.bfcWindingCCW = true;
77 m_flags.bfcInvertNext = false;
78 m_flags.haveBoundingBox = false;
79 m_flags.haveMaxRadius = false;
80 m_flags.haveMaxFullRadius = false;
81 m_flags.fullRadius = false;
82 m_flags.texmapStarted = false;
83 m_flags.texmapFallback = false;
84 m_flags.texmapNext = false;
85 // Initialize Public flags
86 m_flags.part = false;
87 m_flags.subPart = false;
88 m_flags.primitive = false;
89 m_flags.mpd = false;
90 m_flags.noShrink = false;
91 m_flags.official = false;
92 m_flags.unofficial = false;
93 m_flags.hasStuds = false;
94 m_flags.bfcCertify = BFCUnknownState;
95 m_flags.bboxIgnoreOn = false;
96 m_flags.bboxIgnoreBegun = false;
97 sm_modelCount++;
98 }
99
LDLModel(const LDLModel & other)100 LDLModel::LDLModel(const LDLModel &other)
101 :m_filename(copyString(other.m_filename)),
102 m_name(copyString(other.m_name)),
103 m_author(copyString(other.m_author)),
104 m_description(copyString(other.m_description)),
105 m_fileLines(NULL),
106 m_mpdTexmapModels(NULL),
107 m_mpdTexmapLines(NULL),
108 m_mpdTexmapImages(NULL),
109 m_mainModel(other.m_mainModel),
110 m_stepIndices(other.m_stepIndices),
111 m_activeLineCount(other.m_activeLineCount),
112 m_activeMPDModel(NULL),
113 m_boundingMin(other.m_boundingMin),
114 m_boundingMax(other.m_boundingMax),
115 m_center(other.m_center),
116 m_maxRadius(other.m_maxRadius),
117 m_texmapImage(TCObject::retain(other.m_texmapImage)),
118 m_dataLine(TCObject::retain(other.m_dataLine)),
119 m_flags(other.m_flags)
120 {
121 if (other.m_fileLines)
122 {
123 m_fileLines = (LDLFileLineArray *)other.m_fileLines->copy();
124 }
125 if (other.m_mpdTexmapModels)
126 {
127 m_mpdTexmapModels = (LDLModelArray *)other.m_mpdTexmapModels->copy();
128 }
129 if (other.m_mpdTexmapLines)
130 {
131 m_mpdTexmapLines = (LDLCommentLineArray *)other.m_mpdTexmapLines->copy();
132 }
133 if (other.m_mpdTexmapImages)
134 {
135 m_mpdTexmapImages = (TCImageArray *)other.m_mpdTexmapImages->copy();
136 }
137 }
138
dealloc(void)139 void LDLModel::dealloc(void)
140 {
141 delete[] m_filename;
142 delete[] m_name;
143 delete[] m_author;
144 delete[] m_description;
145 TCObject::release(m_fileLines);
146 TCObject::release(m_mpdTexmapModels);
147 TCObject::release(m_mpdTexmapLines);
148 TCObject::release(m_mpdTexmapImages);
149 TCObject::release(m_texmapImage);
150 TCObject::release(m_dataLine);
151 sm_modelCount--;
152 TCObject::dealloc();
153 }
154
copy(void) const155 TCObject *LDLModel::copy(void) const
156 {
157 return new LDLModel(*this);
158 }
159
setFilename(const char * filename)160 void LDLModel::setFilename(const char *filename)
161 {
162 delete[] m_filename;
163 m_filename = copyString(filename);
164 }
165
setName(const char * name)166 void LDLModel::setName(const char *name)
167 {
168 delete[] m_name;
169 m_name = copyString(name);
170 }
171
getPackedRGBA(int colorNumber)172 TCULong LDLModel::getPackedRGBA(int colorNumber)
173 {
174 int r, g, b, a;
175
176 getRGBA(colorNumber, r, g, b, a);
177 return r << 24 | g << 16 | b << 8 | a;
178 }
179
hasSpecular(int colorNumber)180 bool LDLModel::hasSpecular(int colorNumber)
181 {
182 return m_mainModel->hasSpecular(colorNumber);
183 }
184
hasShininess(int colorNumber)185 bool LDLModel::hasShininess(int colorNumber)
186 {
187 return m_mainModel->hasShininess(colorNumber);
188 }
189
getSpecular(int colorNumber,float * specular)190 void LDLModel::getSpecular(int colorNumber, float *specular)
191 {
192 m_mainModel->getSpecular(colorNumber, specular);
193 }
194
getShininess(int colorNumber,float & shininess)195 void LDLModel::getShininess(int colorNumber, float &shininess)
196 {
197 m_mainModel->getShininess(colorNumber, shininess);
198 }
199
getEdgeColorNumber(int colorNumber)200 int LDLModel::getEdgeColorNumber(int colorNumber)
201 {
202 return m_mainModel->getEdgeColorNumber(colorNumber);
203 }
204
getRGBA(int colorNumber,int & r,int & g,int & b,int & a)205 void LDLModel::getRGBA(int colorNumber, int& r, int& g, int& b, int& a)
206 {
207 m_mainModel->getRGBA(colorNumber, r, g, b, a);
208 }
209
colorNumberIsTransparent(int colorNumber)210 bool LDLModel::colorNumberIsTransparent(int colorNumber)
211 {
212 return m_mainModel->colorNumberIsTransparent(colorNumber);
213 }
214
subModelNamed(const char * subModelName,bool lowRes,bool secondAttempt,const LDLModelLine * fileLine,bool knownPart)215 LDLModel *LDLModel::subModelNamed(const char *subModelName, bool lowRes,
216 bool secondAttempt,
217 const LDLModelLine *fileLine, bool knownPart)
218 {
219 TCDictionary* subModelDict = getLoadedModels();
220 LDLModel* subModel;
221 char *dictName = NULL;
222 char *adjustedName;
223 bool &ancestorCheck = m_mainModel->ancestorCheck(subModelName);
224 bool loop = false;
225
226 if (ancestorCheck)
227 {
228 // Recursion: the model named subModelName is an ancestor of the current
229 // model. Loading it will result in an infinite loop and crash after
230 // the stack overflows.
231 return NULL;
232 }
233 else
234 {
235 ancestorCheck = true;
236 }
237 if (strcasecmp(subModelName, "stud.dat") == 0)
238 {
239 m_flags.hasStuds = true;
240 }
241 adjustedName = copyString(subModelName);
242 replaceStringCharacter(adjustedName, '\\', '/');
243 if (lowRes)
244 {
245 if (stringHasCaseInsensitivePrefix(adjustedName, "stud"))
246 {
247 adjustedName[3] = '2';
248 }
249 else
250 {
251 delete[] adjustedName;
252 ancestorCheck = false;
253 return NULL;
254 }
255 dictName = new char[strlen(LDL_LOWRES_PREFIX) + strlen(adjustedName) +
256 1];
257 sprintf(dictName, "%s%s", LDL_LOWRES_PREFIX, adjustedName);
258 }
259 else
260 {
261 dictName = copyString(adjustedName);
262 }
263 subModel = (LDLModel*)(subModelDict->objectForKey(dictName));
264 if (subModel == NULL)
265 {
266 std::ifstream subModelStream;
267 std::string subModelPath;
268
269 if (openSubModelNamed(adjustedName, subModelPath, subModelStream,
270 knownPart, &loop))
271 {
272 bool clearSubModel = false;
273 replaceStringCharacter(&subModelPath[0], '\\', '/');
274 subModel = new LDLModel;
275 subModel->setFilename(subModelPath.c_str());
276
277 if (!initializeNewSubModel(subModel, dictName, subModelStream))
278 {
279 clearSubModel = true;
280 }
281 if (clearSubModel)
282 {
283 subModel = NULL;
284 }
285 m_flags.loadingPart = false;
286 m_flags.loadingSubPart = false;
287 m_flags.loadingUnoffic = false;
288 }
289 }
290 if (subModel != NULL && subModel->isUnOfficial())
291 {
292 sendUnofficialWarningIfPart(subModel, fileLine, subModelName);
293 }
294 delete[] adjustedName;
295 if (!subModel && !secondAttempt && !loop)
296 {
297 LDLFindFileAlert *alert = new LDLFindFileAlert(subModelName);
298
299 TCAlertManager::sendAlert(alert, this);
300 if (alert->getFileFound())
301 {
302 subModel = subModelNamed(alert->getFilename(), lowRes, true,
303 fileLine, alert->getPartFlag());
304 if (subModel)
305 {
306 // The following is necessary in order for primitive
307 // substitution to work.
308 subModel->setName(dictName);
309 sendUnofficialWarningIfPart(subModel, fileLine, subModelName);
310 }
311 }
312 alert->release();
313 }
314 delete[] dictName;
315 if (!loop)
316 {
317 ancestorCheck = false;
318 }
319 return subModel;
320 }
321
sendUnofficialWarningIfPart(const LDLModel * subModel,const LDLModelLine * fileLine,const char * subModelName)322 void LDLModel::sendUnofficialWarningIfPart(
323 const LDLModel *subModel,
324 const LDLModelLine *fileLine,
325 const char *subModelName)
326 {
327 if (!isPart() && subModel->isPart())
328 {
329 UCCHAR szWarning[1024];
330 UCSTR ucSubModelName = mbstoucstring(subModelName);
331
332 sucprintf(szWarning, COUNT_OF(szWarning),
333 TCLocalStrings::get(_UC("LDLModelUnofficialPart")),
334 ucSubModelName);
335 delete[] ucSubModelName;
336 reportWarning(LDLEUnofficialPart, *fileLine, szWarning);
337 }
338 }
339
340 // NOTE: static function
openStream(const char * filename,std::ifstream & stream)341 bool LDLModel::openStream(const char *filename, std::ifstream &stream)
342 {
343 // Use binary mode to work with DOS and Unix line endings and allow
344 // seeking in the file. The file parsing code will still work fine and
345 // strip out the extra data.
346 #ifdef _MSC_VER
347 ucstring ucFilename;
348 utf8toucstring(ucFilename, filename);
349 if (!ucFilename.empty())
350 {
351 // Windows STL has a non-standard extension to support wide-character
352 // filenames
353 stream.open(ucFilename.c_str(), std::ios_base::binary);
354 }
355 else
356 {
357 // If the filename isn't valid UTF-8, try to open it using
358 // the original non-wide version.
359 stream.open(filename, std::ios_base::binary);
360 }
361 #else // _MSC_VER
362 stream.open(filename, std::ios_base::binary);
363 #endif // !_MSC_VER
364 return stream.is_open() && !stream.fail();
365 }
366
367 // NOTE: static function
openFile(const char * filename,std::ifstream & modelStream)368 bool LDLModel::openFile(const char *filename, std::ifstream &modelStream)
369 {
370 char *newFilename = copyString(filename);
371
372 convertStringToLower(newFilename);
373 if (fileCaseCallback)
374 {
375 if (openStream(newFilename, modelStream))
376 {
377 delete[] newFilename;
378 return true;
379 }
380 convertStringToUpper(newFilename);
381 if (openStream(newFilename, modelStream))
382 {
383 delete[] newFilename;
384 return true;
385 }
386 strcpy(newFilename, filename);
387 if (openStream(newFilename, modelStream))
388 {
389 delete[] newFilename;
390 return true;
391 }
392 if (fileCaseCallback(newFilename))
393 {
394 openStream(newFilename, modelStream);
395 }
396 }
397 else
398 {
399 openStream(newFilename, modelStream);
400 }
401 delete[] newFilename;
402 return modelStream.is_open();
403 }
404
openModelFile(const char * filename,std::ifstream & modelStream,bool isText,bool knownPart)405 bool LDLModel::openModelFile(
406 const char *filename,
407 std::ifstream &modelStream,
408 bool isText,
409 bool knownPart /*= false*/)
410 {
411 if (openFile(filename, modelStream))
412 {
413 if (knownPart)
414 {
415 m_flags.loadingPart = true;
416 }
417 if (isText)
418 {
419 // Check for UTF-8 Byte order mark (BOM), and skip over it if
420 // present. Only do this on text files. (Right now, texture maps
421 // are the only binary files that get opened by this function.)
422 skipUtf8BomIfPresent(modelStream);
423 }
424 }
425 return modelStream.is_open() && !modelStream.fail();
426 }
427
isSubPart(const char * subModelName)428 bool LDLModel::isSubPart(const char *subModelName)
429 {
430 return stringHasCaseInsensitivePrefix(subModelName, "s/") ||
431 stringHasCaseInsensitivePrefix(subModelName, "s\\");
432 }
433
isAbsolutePath(const char * path)434 bool LDLModel::isAbsolutePath(const char *path)
435 {
436 #ifdef WIN32
437 return strlen(path) >= 2 && path[1] == ':' || path[0] == '/';
438 #else
439 return path[0] == '/';
440 #endif
441 }
442
443 // NOTE: static function.
combinePathParts(std::string & path,const std::string & left,const std::string & middle,const std::string & right)444 void LDLModel::combinePathParts(
445 std::string &path,
446 const std::string &left,
447 const std::string& middle,
448 const std::string &right)
449 {
450 path = left;
451 path += middle;
452 path += right;
453 }
454
openSubModelNamed(const char * subModelName,std::string & subModelPath,std::ifstream & subModelStream,bool knownPart,bool * pLoop,bool isText)455 bool LDLModel::openSubModelNamed(
456 const char* subModelName,
457 std::string &subModelPath,
458 std::ifstream &subModelStream,
459 bool knownPart,
460 bool *pLoop /*= NULL*/,
461 bool isText /*= true*/)
462 {
463 TCStringArray *extraSearchDirs = m_mainModel->getExtraSearchDirs();
464
465 if (pLoop != NULL)
466 {
467 *pLoop = false;
468 }
469 subModelPath = subModelName;
470 if (isAbsolutePath(subModelPath.c_str()))
471 {
472 return openModelFile(subModelPath.c_str(), subModelStream, isText,
473 knownPart);
474 }
475 else if (sm_lDrawIni && sm_lDrawIni->nSearchDirs > 0)
476 {
477 int i;
478
479 for (i = 0; i < sm_lDrawIni->nSearchDirs; i++)
480 {
481 LDrawSearchDirS *searchDir = &sm_lDrawIni->SearchDirs[i];
482 bool skip = false;
483
484 if (searchDir->Flags & LDSDF_UNOFFIC)
485 {
486 if (m_mainModel->getCheckPartTracker())
487 {
488 skip = true;
489 }
490 else
491 {
492 m_flags.loadingUnoffic = true;
493 }
494 }
495 if ((searchDir->Flags & LDSDF_SKIP) == 0 && !skip)
496 {
497 combinePathParts(subModelPath, searchDir->Dir, "/",
498 subModelName);
499 if (openModelFile(subModelPath.c_str(), subModelStream, isText))
500 {
501 char *mainModelPath = copyString(m_mainModel->getFilename());
502 #ifdef WIN32
503 replaceStringCharacter(mainModelPath, '\\', '/');
504 replaceStringCharacter(&subModelPath[0], '\\', '/');
505 #endif // WIN32
506 if (strcasecmp(mainModelPath, subModelPath.c_str()) == 0)
507 {
508 // Recursive call to main model.
509 delete[] mainModelPath;
510 subModelStream.close();
511 if (pLoop != NULL)
512 {
513 *pLoop = true;
514 }
515 return false;
516 }
517 delete[] mainModelPath;
518 if (searchDir->Flags & LDSDF_DEFPRIM)
519 {
520 m_flags.loadingPrimitive = true;
521 }
522 else if (searchDir->Flags & LDSDF_DEFPART)
523 {
524 if (isSubPart(subModelName))
525 {
526 m_flags.loadingSubPart = true;
527 }
528 else
529 {
530 m_flags.loadingPart = true;
531 }
532 }
533 return true;
534 }
535 }
536 }
537 }
538 else
539 {
540 if (openModelFile(subModelPath.c_str(), subModelStream, isText))
541 {
542 return true;
543 }
544 combinePathParts(subModelPath, lDrawDir(), "/P/", subModelName);
545 if (openModelFile(subModelPath.c_str(), subModelStream, isText))
546 {
547 m_flags.loadingPrimitive = true;
548 return true;
549 }
550 combinePathParts(subModelPath, lDrawDir(), "/PARTS/", subModelName);
551 if (openModelFile(subModelPath.c_str(), subModelStream, isText))
552 {
553 if (isSubPart(subModelName))
554 {
555 m_flags.loadingSubPart = true;
556 }
557 else
558 {
559 m_flags.loadingPart = true;
560 }
561 return true;
562 }
563 combinePathParts(subModelPath, lDrawDir(), "/MODELS/", subModelName);
564 if (openModelFile(subModelPath.c_str(), subModelStream, isText))
565 {
566 return true;
567 }
568 }
569 if (extraSearchDirs)
570 {
571 int i;
572 int count = extraSearchDirs->getCount();
573
574 for (i = 0; i < count; i++)
575 {
576 combinePathParts(subModelPath, (*extraSearchDirs)[i], "/",
577 subModelName);
578 if (openModelFile(subModelPath.c_str(), subModelStream, isText))
579 {
580 return true;
581 }
582 }
583 }
584 return false;
585 }
586
initializeNewSubModel(LDLModel * subModel,const char * dictName)587 bool LDLModel::initializeNewSubModel(LDLModel *subModel, const char *dictName)
588 {
589 std::ifstream closedStream;
590 return initializeNewSubModel(subModel, dictName, closedStream);
591 }
592
initializeNewSubModel(LDLModel * subModel,const char * dictName,std::ifstream & subModelStream)593 bool LDLModel::initializeNewSubModel(
594 LDLModel *subModel,
595 const char *dictName,
596 std::ifstream &subModelStream)
597 {
598 TCDictionary* subModelDict = getLoadedModels();
599
600 subModelDict->setObjectForKey(subModel, dictName);
601 subModel->release();
602 subModel->m_mainModel = m_mainModel;
603 if (m_flags.loadingPart)
604 {
605 subModel->m_flags.part = true;
606 subModel->m_flags.subPart = false;
607 // subModel->m_flags.bfcCertify = BFCForcedOnState;
608 }
609 if (m_flags.loadingSubPart)
610 {
611 subModel->m_flags.part = false;
612 subModel->m_flags.subPart = true;
613 }
614 if (m_flags.loadingPrimitive)
615 {
616 subModel->m_flags.primitive = true;
617 }
618 if (m_flags.loadingUnoffic)
619 {
620 subModel->m_flags.unofficial = true;
621 }
622 if (subModelStream.is_open() && !subModel->load(subModelStream))
623 {
624 subModelDict->removeObjectForKey(dictName);
625 return false;
626 }
627 subModel->setName(dictName);
628 return true;
629 }
630
631 // NOTE: static function.
verifyLDrawDir(const char * value)632 bool LDLModel::verifyLDrawDir(const char *value)
633 {
634 char currentDir[1024];
635 bool retValue = false;
636
637 if (value && getcwd(currentDir, sizeof(currentDir)))
638 {
639 if (chdir(value) == 0)
640 {
641 if (chdir("parts") == 0 && chdir("..") == 0 && chdir("p") == 0)
642 {
643 retValue = true;
644 }
645 }
646 if (chdir(currentDir) != 0)
647 {
648 debugPrintf("Error going back to original directory.\n");
649 debugPrintf("currentDir before: <%s>\n", currentDir);
650 if (getcwd(currentDir, sizeof(currentDir)) != NULL)
651 {
652 debugPrintf("currentDir after: <%s>\n", currentDir);
653 }
654 }
655 }
656 return retValue;
657 }
658
659 // NOTE: static function.
setFileCaseCallback(LDLFileCaseCallback value)660 void LDLModel::setFileCaseCallback(LDLFileCaseCallback value)
661 {
662 fileCaseCallback = value;
663 // If bool isn't 1 byte, then we can't support the case callback, so don't
664 // even try. This will trigger an error in LDLMainModel if the P and/or
665 // PARTS directory inside the LDraw directory aren't capitalized.
666 if (sizeof(bool) == sizeof(char))
667 {
668 if (sm_lDrawIni)
669 {
670 LDrawIniSetFileCaseCallback(fileCaseCallback);
671 }
672 }
673 }
674
675 // NOTE: static function.
setLDrawDir(const char * value)676 void LDLModel::setLDrawDir(const char *value)
677 {
678 if (value != sm_systemLDrawDir || !value)
679 {
680 delete[] sm_systemLDrawDir;
681 if (value)
682 {
683 sm_systemLDrawDir = cleanedUpPath(value);
684 }
685 else
686 {
687 sm_systemLDrawDir = NULL;
688 }
689 if (sm_lDrawIni)
690 {
691 LDrawIniFree(sm_lDrawIni);
692 }
693 sm_lDrawIni = LDrawIniGet(sm_systemLDrawDir, NULL, NULL);
694 if (sm_lDrawIni)
695 {
696 if (fileCaseCallback)
697 {
698 LDrawIniSetFileCaseCallback(fileCaseCallback);
699 }
700 if (!sm_systemLDrawDir)
701 {
702 sm_systemLDrawDir = copyString(sm_lDrawIni->LDrawDir);
703 }
704 if (sm_systemLDrawDir)
705 {
706 stripTrailingPathSeparators(sm_systemLDrawDir);
707 LDrawIniComputeRealDirs(sm_lDrawIni, 1, 0, NULL);
708 }
709 }
710 }
711 }
712
initCheckDirs()713 void LDLModel::initCheckDirs()
714 {
715 const char *value = getenv("LDRAWDIR");
716
717 if (value)
718 {
719 sm_checkDirs.push_back(value);
720 }
721 #ifdef WIN32
722 char buf[1024];
723
724 if (GetPrivateProfileString("LDraw", "BaseDirectory", "", buf, 1024,
725 "ldraw.ini"))
726 {
727 buf[1023] = 0;
728 }
729 if (buf[0])
730 {
731 sm_checkDirs.push_back(buf);
732 }
733 sm_checkDirs.push_back("C:\\ldraw");
734 #else // WIN32
735 #ifdef __APPLE__
736 const char *libDir = "/Library/ldraw";
737 const char *homeDir = getenv("HOME");
738
739 if (homeDir != NULL)
740 {
741 char *homeLib = copyString(homeDir, strlen(libDir));
742
743 stripTrailingPathSeparators(homeLib);
744 strcat(homeLib, libDir);
745 sm_checkDirs.push_back(homeLib);
746 delete[] homeLib;
747 }
748 sm_checkDirs.push_back(libDir);
749 sm_checkDirs.push_back("/Applications/Bricksmith/LDraw");
750 #else // __APPLE__
751 sm_checkDirs.push_back("/usr/share/ldraw");
752 const char *homeDir = getenv("HOME");
753 if (homeDir != NULL)
754 {
755 char *cleanHome = cleanedUpPath(homeDir);
756 std::string homeLDraw;
757
758 stripTrailingPathSeparators(cleanHome);
759 homeLDraw = cleanHome;
760 delete[] cleanHome;
761 homeLDraw += "/ldraw";
762 sm_checkDirs.push_back(homeLDraw);
763 }
764 #endif // __APPLE__
765 #endif // WIN32
766 char *ldviewDir = copyString(TCUserDefaults::getAppPath());
767 stripTrailingPathSeparators(ldviewDir);
768 char *ldviewLDrawDir = copyString(ldviewDir, 10);
769
770 // LDView Dir/ldraw
771 strcat(ldviewLDrawDir, "/ldraw");
772 sm_checkDirs.push_back(ldviewLDrawDir);
773 #ifndef COCOA
774 delete[] ldviewLDrawDir;
775 char *ldviewParentDir = directoryFromPath(ldviewDir);
776 stripTrailingPathSeparators(ldviewParentDir);
777 ldviewLDrawDir = copyString(ldviewParentDir, 10);
778 delete[] ldviewParentDir;
779 // LDView Dir/../ldraw
780 strcat(ldviewLDrawDir, "/ldraw");
781 sm_checkDirs.push_back(ldviewLDrawDir);
782 #endif // COCOA
783 delete[] ldviewDir;
784 delete[] ldviewLDrawDir;
785 }
786
787 // NOTE: static function.
lDrawDir(bool defaultValue)788 const char* LDLModel::lDrawDir(bool defaultValue /*= false*/)
789 {
790 char *origValue = NULL;
791
792 if (defaultValue)
793 {
794 if (sm_defaultLDrawDir)
795 {
796 return sm_defaultLDrawDir;
797 }
798 origValue = copyString(sm_systemLDrawDir);
799 if (sm_systemLDrawDir)
800 {
801 setLDrawDir(NULL);
802 }
803 }
804 if (!sm_systemLDrawDir)
805 {
806 bool found = false;
807
808 if (sm_checkDirs.size() == 0)
809 {
810 initCheckDirs();
811 }
812 for (StringList::const_iterator it = sm_checkDirs.begin(); !found &&
813 it != sm_checkDirs.end(); ++it)
814 {
815 const char *dir = it->c_str();
816
817 if (verifyLDrawDir(dir))
818 {
819 setLDrawDir(dir);
820 found = true;
821 }
822 }
823 if (!found)
824 {
825 sm_systemLDrawDir = copyString("");
826 }
827 }
828 if (defaultValue)
829 {
830 sm_defaultLDrawDir = copyString(sm_systemLDrawDir);
831 setLDrawDir(origValue);
832 delete[] origValue;
833 return sm_defaultLDrawDir;
834 }
835 else
836 {
837 return sm_systemLDrawDir;
838 }
839 }
840
readComment(LDLCommentLine * commentLine)841 void LDLModel::readComment(LDLCommentLine *commentLine)
842 {
843 std::string filename;
844 std::string author;
845
846 if (commentLine->getMPDFilename(&filename))
847 {
848 replaceStringCharacter(&filename[0], '\\', '/');
849 if (m_flags.mainModelLoaded)
850 {
851 if (m_activeLineCount == 0)
852 {
853 m_activeLineCount = commentLine->getLineNumber() - 1;
854 }
855 if (!getLoadedModels()->objectForKey(filename.c_str()))
856 {
857 LDLModel *subModel = new LDLModel;
858
859 if (commentLine->isDataMeta())
860 {
861 subModel->m_dataLine = TCObject::retain(commentLine);
862 }
863 subModel->setFilename(m_filename);
864 initializeNewSubModel(subModel, filename.c_str());
865 m_activeMPDModel = subModel;
866 if (this == getMainModel())
867 {
868 getMainModel()->addMpdModel(subModel);
869 }
870 }
871 }
872 else
873 {
874 m_flags.mainModelLoaded = true;
875 m_flags.mpd = true;
876 if (this == getMainModel())
877 {
878 getMainModel()->addMpdModel(this);
879 if (m_name == NULL)
880 {
881 setName(filename.c_str());
882 }
883 }
884 }
885 }
886 else if (commentLine->isPartMeta())
887 {
888 if (commentLine->isOfficialPartMeta(true))
889 {
890 // Note that even if we decide it's a primitive, it can still be an
891 // official file.
892 m_flags.official = true;
893 }
894 // No matter what the comment says, remember that a primitive cannot
895 // be a part, so if we found the file in the P directory, then by
896 // definition it isn't a part.
897 if (m_flags.mainModelLoaded)
898 {
899 if (m_activeMPDModel && !m_activeMPDModel->isPrimitive())
900 {
901 if (m_flags.loadingSubPart)
902 {
903 m_activeMPDModel->m_flags.subPart = true;
904 }
905 else if (!m_activeMPDModel->m_flags.subPart)
906 {
907 m_activeMPDModel->m_flags.part = true;
908 }
909 }
910 }
911 else if (!isPrimitive())
912 {
913 if (m_flags.loadingSubPart)
914 {
915 m_flags.subPart = true;
916 }
917 else if (!m_flags.subPart)
918 {
919 m_flags.part = true;
920 }
921 // This is now a part, so if we've already hit a BFC CERTIFY line
922 // switch it to forced certify.
923 if (m_flags.bfcCertify == BFCOnState)
924 {
925 m_flags.bfcCertify = BFCForcedOnState;
926 }
927 }
928 }
929 else if (commentLine->isPrimitiveMeta())
930 {
931 if (m_flags.mainModelLoaded)
932 {
933 m_activeMPDModel->m_flags.part = false;
934 m_activeMPDModel->m_flags.subPart = false;
935 m_activeMPDModel->m_flags.primitive = true;
936 }
937 else
938 {
939 m_flags.part = false;
940 m_flags.subPart = false;
941 m_flags.primitive = true;
942 }
943 }
944 else if (commentLine->isNoShrinkMeta())
945 {
946 if (m_flags.mainModelLoaded)
947 {
948 if (m_activeMPDModel)
949 {
950 m_activeMPDModel->m_flags.noShrink = true;
951 }
952 }
953 else
954 {
955 m_flags.noShrink = true;
956 }
957 }
958 else if (commentLine->getAuthor(author))
959 {
960 if (!m_author)
961 {
962 m_author = copyString(author.c_str());
963 }
964 }
965 }
966
967 /*
968 static char *myFgets(char *buf, int bufSize, FILE *file)
969 {
970 int i;
971
972 for (i = 0; i < bufSize - 1; i++)
973 {
974 int char1 = fgetc(file);
975
976 if (feof(file))
977 {
978 buf[i] = 0;
979 if (i > 0)
980 {
981 return buf;
982 }
983 else
984 {
985 return NULL;
986 }
987 }
988 if (char1 == '\r' || char1 == '\n')
989 {
990 int char2 = fgetc(file);
991
992 buf[i] = '\n';
993 buf[i + 1] = 0;
994 if (!feof(file))
995 {
996 if (!(char1 == '\r' && char2 == '\n' ||
997 char1 == '\n' && char2 == '\r'))
998 {
999 ungetc(char2, file);
1000 }
1001 }
1002 return buf;
1003 }
1004 buf[i] = (char)char1;
1005 }
1006 buf[bufSize - 1] = 0;
1007 return buf;
1008 }
1009 */
1010
read(std::ifstream & stream)1011 bool LDLModel::read(std::ifstream &stream)
1012 {
1013 std::string line;
1014 int lineNumber = 1;
1015 bool done = false;
1016 bool retValue = true;
1017
1018 m_fileLines = new LDLFileLineArray;
1019 while (!done && !getLoadCanceled())
1020 {
1021 if (std::getline(stream, line))
1022 {
1023 LDLFileLine *fileLine;
1024
1025 stripCRLF(&line[0]);
1026 fileLine = LDLFileLine::initFileLine(this, line.c_str(), lineNumber);
1027 lineNumber++;
1028 m_fileLines->addObject(fileLine);
1029 fileLine->release();
1030 if (fileLine->getLineType() == LDLLineTypeComment)
1031 {
1032 // To a certain extent, this will actually parse the comment, but
1033 // we really need to do some parsing prior to parsing the rest
1034 // of the file.
1035 readComment((LDLCommentLine *)fileLine);
1036 }
1037 }
1038 else
1039 {
1040 if (m_activeLineCount == 0)
1041 {
1042 m_activeLineCount = m_fileLines->getCount();
1043 }
1044 done = true;
1045 }
1046 }
1047 stream.close();
1048 m_activeMPDModel = NULL;
1049 return retValue && !getLoadCanceled();
1050 }
1051
reportProgress(const char * message,float progress,bool mainOnly)1052 void LDLModel::reportProgress(const char *message, float progress,
1053 bool mainOnly)
1054 {
1055 if (!mainOnly || this == m_mainModel)
1056 {
1057 bool loadCanceled;
1058
1059 TCProgressAlert::send("LDLModel", message, progress, &loadCanceled,
1060 this);
1061 if (loadCanceled)
1062 {
1063 cancelLoad();
1064 }
1065 }
1066 }
1067
reportProgress(const wchar_t * message,float progress,bool mainOnly)1068 void LDLModel::reportProgress(const wchar_t *message, float progress,
1069 bool mainOnly)
1070 {
1071 if (!mainOnly || this == m_mainModel)
1072 {
1073 bool loadCanceled;
1074
1075 TCProgressAlert::send("LDLModel", message, progress, &loadCanceled,
1076 this);
1077 if (loadCanceled)
1078 {
1079 cancelLoad();
1080 }
1081 }
1082 }
1083
load(std::ifstream & stream,bool trackProgress)1084 bool LDLModel::load(std::ifstream &stream, bool trackProgress)
1085 {
1086 bool retValue;
1087
1088 if (trackProgress)
1089 {
1090 reportProgress(LOAD_MESSAGE, 0.0f);
1091 }
1092 if (!read(stream))
1093 {
1094 if (trackProgress)
1095 {
1096 reportProgress(LOAD_MESSAGE, 1.0f);
1097 }
1098 return false;
1099 }
1100 if (trackProgress)
1101 {
1102 reportProgress(LOAD_MESSAGE, MAIN_READ_FRACTION);
1103 }
1104 retValue = parse();
1105 if (trackProgress)
1106 {
1107 reportProgress(LOAD_MESSAGE, 1.0f);
1108 }
1109 return retValue;
1110 }
1111
parseMPDMeta(int index,const char * filename)1112 int LDLModel::parseMPDMeta(int index, const char *filename)
1113 {
1114 int i = index + 1;
1115 int count = m_fileLines->getCount();
1116
1117 if (m_flags.mainModelParsed)
1118 {
1119 LDLModel *subModel;
1120
1121 for (i = index + 1; i < count; i++)
1122 {
1123 LDLFileLine *fileLine = (*m_fileLines)[i];
1124
1125 if (fileLine->getLineType() == LDLLineTypeComment &&
1126 ((LDLCommentLine *)fileLine)->getMPDFilename())
1127 {
1128 break;
1129 }
1130 }
1131 count = i;
1132 subModel = (LDLModel *)getLoadedModels()->objectForKey(filename);
1133 if (subModel)
1134 {
1135 if (!subModel->m_fileLines)
1136 {
1137 subModel->m_fileLines = new LDLFileLineArray(count - index - 1);
1138 for (i = index + 1; i < count; i++)
1139 {
1140 LDLFileLine *fileLine = (*m_fileLines)[i];
1141
1142 subModel->m_fileLines->addObject(fileLine);
1143 }
1144 subModel->m_activeLineCount = subModel->m_fileLines->getCount();
1145 LDLModel *oldActiveMpd = m_mainModel->m_activeMPDModel;
1146 m_mainModel->m_activeMPDModel = this;
1147 if (!subModel->parse())
1148 {
1149 m_mainModel->m_activeMPDModel = oldActiveMpd;
1150 return -1;
1151 }
1152 m_mainModel->m_activeMPDModel = oldActiveMpd;
1153 }
1154 else
1155 {
1156 reportError(LDLEMPDError, *(*m_fileLines)[index],
1157 TCLocalStrings::get(_UC("LDLModelMpdAlreadyLoaded")));
1158 }
1159 }
1160 }
1161 else
1162 {
1163 m_flags.mainModelParsed = true;
1164 }
1165 return i - index - 1;
1166 }
1167
parseBBoxIgnoreMeta(LDLCommentLine * commentLine)1168 int LDLModel::parseBBoxIgnoreMeta(LDLCommentLine *commentLine)
1169 {
1170 if (commentLine->containsBBoxIgnoreCommand("BEGIN"))
1171 {
1172 m_flags.bboxIgnoreOn = true;
1173 m_flags.bboxIgnoreBegun = true;
1174 }
1175 else if (commentLine->containsBBoxIgnoreCommand("NEXT"))
1176 {
1177 m_flags.bboxIgnoreOn = true;
1178 }
1179 else if (commentLine->containsBBoxIgnoreCommand("END"))
1180 {
1181 if (!m_flags.bboxIgnoreBegun)
1182 {
1183 reportWarning(LDLEMetaCommand, *commentLine,
1184 TCLocalStrings::get(_UC("LDLModelBBoxEndUnexpected")));
1185 }
1186 m_flags.bboxIgnoreOn = false;
1187 m_flags.bboxIgnoreBegun = false;
1188 }
1189 else
1190 {
1191 reportWarning(LDLEMetaCommand, *commentLine,
1192 TCLocalStrings::get(_UC("LDLModelBBoxCommand")));
1193 }
1194 return 0;
1195 }
1196
endTexmap(void)1197 void LDLModel::endTexmap(void)
1198 {
1199 m_flags.texmapStarted = false;
1200 m_flags.texmapNext = false;
1201 TCObject::release(m_texmapImage);
1202 m_texmapImage = NULL;
1203 }
1204
openTexmap(const char * filename,std::ifstream & texmapStream,std::string & path)1205 bool LDLModel::openTexmap(
1206 const char *filename,
1207 std::ifstream &texmapStream,
1208 std::string &path)
1209 {
1210 if (!openSubModelNamed(filename, path, texmapStream, false, NULL, false))
1211 {
1212 LDLFindFileAlert *alert = new LDLFindFileAlert(filename);
1213
1214 TCAlertManager::sendAlert(alert, this);
1215 if (alert->getFileFound())
1216 {
1217 path = alert->getFilename();
1218 openStream(path.c_str(), texmapStream);
1219 }
1220 alert->release();
1221 }
1222 return texmapStream.is_open() && !texmapStream.fail();
1223 }
1224
extractData()1225 void LDLModel::extractData()
1226 {
1227 std::string base64Text;
1228 int lineCount = m_fileLines->getCount();
1229 for (int i = 0; i < lineCount; ++i)
1230 {
1231 LDLFileLine *fileLine = (*m_fileLines)[i];
1232 if (fileLine->getLineType() == LDLLineTypeComment)
1233 {
1234 LDLCommentLine *commentLine = (LDLCommentLine *)fileLine;
1235 if (commentLine->isDataRowMeta())
1236 {
1237 const char *line = commentLine->getLine();
1238 std::string base64Line;
1239 base64Line.reserve(strlen(line));
1240 for (const char *lineChar = strchr(line, ':') + 1; *lineChar; ++lineChar)
1241 {
1242 if (isInBase64Charset(*lineChar))
1243 {
1244 base64Line += *lineChar;
1245 }
1246 }
1247 base64Text += base64Line;
1248 }
1249 }
1250 }
1251 if (!base64Decode(base64Text, m_data))
1252 {
1253 reportError(LDLEMetaCommand, *m_dataLine,
1254 TCLocalStrings::get(_UC("LDLModelDataDecodeError")));
1255 }
1256 }
1257
parseTexmapMeta(LDLCommentLine * commentLine)1258 int LDLModel::parseTexmapMeta(LDLCommentLine *commentLine)
1259 {
1260 if (m_flags.texmapStarted && m_flags.texmapNext)
1261 {
1262 reportError(LDLEGeneral, *commentLine,
1263 TCLocalStrings::get(_UC("LDLModelTexmapCommandAfterNext")));
1264 endTexmap();
1265 }
1266 if (m_flags.texmapStarted)
1267 {
1268 if (commentLine->containsTexmapCommand("FALLBACK"))
1269 {
1270 if (m_flags.texmapFallback)
1271 {
1272 reportError(LDLEGeneral, *commentLine,
1273 TCLocalStrings::get(_UC("LDLModelTexmapMultipleFallback")));
1274 }
1275 else
1276 {
1277 if (m_texmapFilename.size() > 0)
1278 {
1279 // If the texmap image load failed, we need to display the
1280 // fallback geometry, so don't go into fallback mode.
1281 m_flags.texmapFallback = true;
1282 }
1283 }
1284 }
1285 else if (commentLine->containsTexmapCommand("END"))
1286 {
1287 endTexmap();
1288 if (!m_flags.texmapValid)
1289 {
1290 commentLine->setValid(false);
1291 }
1292 }
1293 else
1294 {
1295 reportError(LDLEMetaCommand, *commentLine,
1296 TCLocalStrings::get(_UC("LDLModelTexmapUnexpectedCommand")));
1297 }
1298 }
1299 else
1300 {
1301 bool isStart = commentLine->containsTexmapCommand("START");
1302 bool isNext = commentLine->containsTexmapCommand("NEXT");
1303
1304 if (isStart || isNext)
1305 {
1306 const char *typeName = commentLine->getWord(2);
1307 int extraParams = 0;
1308
1309 if (typeName == NULL) {
1310 reportError(LDLEParse, *commentLine,
1311 TCLocalStrings::get(_UC("LDLModelTexmapParseError")));
1312 commentLine->setValid(false);
1313 return -1;
1314 }
1315 else if (strcmp(typeName, "PLANAR") == 0)
1316 {
1317 m_texmapType = LDLFileLine::TTPlanar;
1318 }
1319 else if (strcmp(typeName, "CYLINDRICAL") == 0)
1320 {
1321 m_texmapType = LDLFileLine::TTCylindrical;
1322 extraParams = 1;
1323 }
1324 else if (strcmp(typeName, "SPHERICAL") == 0)
1325 {
1326 m_texmapType = LDLFileLine::TTSpherical;
1327 extraParams = 2;
1328 }
1329 else
1330 {
1331 reportError(LDLEGeneral, *commentLine,
1332 TCLocalStrings::get(_UC("LDLModelTexmapUnknownMethod")));
1333 commentLine->setValid(false);
1334 return -1;
1335 }
1336 if (commentLine->getNumWords() < 13 + extraParams)
1337 {
1338 reportError(LDLEParse, *commentLine,
1339 TCLocalStrings::get(_UC("LDLModelTexmapParseError")));
1340 commentLine->setValid(false);
1341 return -1;
1342 }
1343 for (int i = 0; i < 3; i++)
1344 {
1345 for (int j = 0; j < 3; j++)
1346 {
1347 m_texmapPoints[i][j] =
1348 (TCFloat)atof(commentLine->getWord(3 + i * 3 + j));
1349 }
1350 }
1351 for (int i = 0; i < extraParams; ++i)
1352 {
1353 m_texmapExtra[i] = (TCFloat)atof(commentLine->getWord(12 + i));
1354 }
1355 m_flags.texmapStarted = true;
1356 m_flags.texmapFallback = false;
1357 m_flags.texmapValid = true;
1358 if (isNext)
1359 {
1360 m_flags.texmapNext = true;
1361 }
1362 // Only load the texture map file if textures are enabled. Still
1363 // perform the parsing of everything else either way, but don't
1364 // load the file when they're disabled.
1365 if (m_mainModel->getTexmaps())
1366 {
1367 std::string filename = commentLine->getWord(12 + extraParams);
1368 std::string pathFilename = std::string("textures/") + filename;
1369 bool delayedLoad = false;
1370 std::string path;
1371 TCDictionary* subModelDict = getLoadedModels();
1372 LDLModel *texmapModel = (LDLModel*)subModelDict->objectForKey(filename.c_str());
1373 if (texmapModel != NULL)
1374 {
1375 LDLModel *activeMpd = m_mainModel->m_activeMPDModel;
1376 if (activeMpd != NULL && activeMpd->m_filename != NULL)
1377 {
1378 char *baseName = filenameFromPath(activeMpd->m_filename);
1379 combinePathParts(path, baseName, ":", filename);
1380 delete[] baseName;
1381 }
1382 else
1383 {
1384 combinePathParts(path, "MPD:", filename);
1385 }
1386 if (texmapModel->m_data.empty())
1387 {
1388 if (m_mpdTexmapModels == NULL)
1389 {
1390 m_mpdTexmapModels = new LDLModelArray;
1391 m_mpdTexmapLines = new LDLCommentLineArray;
1392 m_mpdTexmapImages = new TCImageArray;
1393 }
1394 m_mpdTexmapModels->addObject(texmapModel);
1395 m_mpdTexmapLines->addObject(commentLine);
1396 m_mainModel->setHaveMpdTexmaps();
1397 delayedLoad = true;
1398 }
1399 }
1400 std::ifstream texmapStream;
1401 if (texmapModel == NULL)
1402 {
1403 if (!openTexmap(pathFilename.c_str(), texmapStream, path))
1404 {
1405 openTexmap(filename.c_str(), texmapStream, path);
1406 }
1407 }
1408 if ((texmapStream.is_open() && !texmapStream.fail()) ||
1409 texmapModel != NULL)
1410 {
1411 TCImage *image = new TCImage;
1412 bool loaded = false;
1413
1414 image->setLineAlignment(4);
1415 if (delayedLoad)
1416 {
1417 m_mpdTexmapImages->addObject(image);
1418 }
1419 else if (texmapModel != NULL)
1420 {
1421 loaded = image->loadData(&texmapModel->m_data[0],
1422 texmapModel->m_data.size());
1423 }
1424 else
1425 {
1426 texmapStream.close();
1427 // Image loading from a stream would require loading the
1428 // entire image into memory and then doing an in-memory
1429 // load. So close the stream and use the full path that
1430 // was used to open the stream to load the image.
1431 loaded = image->loadFile(path.c_str());
1432 }
1433 if (loaded || delayedLoad)
1434 {
1435 char *cleanPath = cleanedUpPath(path.c_str());
1436
1437 m_texmapImage = image;
1438 convertStringToLower(cleanPath);
1439 // Since the path is going to be used as a key in a map,
1440 // we want it to be consistent. Hence, cleaning it up
1441 // and making it all lower case. Files in LDraw cannot
1442 // have case-sensitive filenames, so this should be
1443 // kosher.
1444 m_texmapFilename = cleanPath;
1445 delete[] cleanPath;
1446 }
1447 else
1448 {
1449 image->release();
1450 reportError(LDLEMetaCommand, *commentLine,
1451 TCLocalStrings::get(_UC("LDLModelTexmapImageLoadError")));
1452 }
1453 }
1454 else
1455 {
1456 reportError(LDLEMetaCommand, *commentLine,
1457 TCLocalStrings::get(_UC("LDLModelTexmapFileNotFound")));
1458 }
1459 if (m_texmapImage == NULL)
1460 {
1461 commentLine->setValid(false);
1462 m_flags.texmapValid = false;
1463 }
1464 }
1465 }
1466 else
1467 {
1468 reportError(LDLEMetaCommand, *commentLine,
1469 TCLocalStrings::get(_UC("LDLModelTexmapUnexpectedCommand")));
1470 }
1471 }
1472 return 0;
1473 }
1474
loadMpdTexmaps(void)1475 int LDLModel::loadMpdTexmaps(void)
1476 {
1477 if (m_mpdTexmapModels == NULL)
1478 {
1479 return 0;
1480 }
1481 int count = m_mpdTexmapModels->getCount();
1482 for (int i = 0; i < count; ++i)
1483 {
1484 LDLModel *texmapModel = (*m_mpdTexmapModels)[i];
1485 TCImage *image = (*m_mpdTexmapImages)[i];
1486 if (!image->loadData(&texmapModel->m_data[0],
1487 texmapModel->m_data.size()))
1488 {
1489 LDLCommentLine *commentLine = (*m_mpdTexmapLines)[i];
1490 reportError(LDLEMetaCommand, *commentLine,
1491 TCLocalStrings::get(_UC("LDLModelTexmapImageLoadError")));
1492 }
1493 }
1494 m_mpdTexmapModels->release();
1495 m_mpdTexmapModels = NULL;
1496 m_mpdTexmapLines->release();
1497 m_mpdTexmapLines = NULL;
1498 m_mpdTexmapImages->release();
1499 m_mpdTexmapImages = NULL;
1500 return 0;
1501 }
1502
parseBFCMeta(LDLCommentLine * commentLine)1503 int LDLModel::parseBFCMeta(LDLCommentLine *commentLine)
1504 {
1505 if (m_flags.bfcInvertNext)
1506 {
1507 reportError(LDLEBFCError, *commentLine,
1508 TCLocalStrings::get(_UC("LDLModelBfcInvert")));
1509 m_flags.bfcInvertNext = false;
1510 }
1511 if (m_flags.bfcCertify == BFCUnknownState)
1512 {
1513 if (commentLine->containsBFCCommand("NOCERTIFY"))
1514 {
1515 m_flags.bfcCertify = BFCOffState;
1516 if (m_flags.started)
1517 {
1518 reportError(LDLEBFCError, *commentLine,
1519 TCLocalStrings::get(_UC("LDLModelBfcNoCertFirst")));
1520 }
1521 }
1522 else
1523 {
1524 if (m_flags.started)
1525 {
1526 m_flags.bfcCertify = BFCOffState;
1527 reportError(LDLEBFCError, *commentLine,
1528 TCLocalStrings::get(_UC("LDLModelBfcFirst")));
1529 }
1530 else
1531 {
1532 // Unless a NOCERTIFY is present, CERTIFY gets turned on by
1533 // default for any BFC command.
1534 if (m_flags.part || m_flags.subPart)
1535 {
1536 // BFC certified parts force BFC to be on, even if their
1537 // parent models don't have BFC certification.
1538 m_flags.bfcCertify = BFCForcedOnState;
1539 }
1540 else
1541 {
1542 m_flags.bfcCertify = BFCOnState;
1543 }
1544 m_flags.bfcClip = true;
1545 }
1546 }
1547 }
1548 else
1549 {
1550 if (commentLine->containsBFCCommand("CERTIFY"))
1551 {
1552 if (getBFCOn())
1553 {
1554 reportWarning(LDLEBFCWarning, *commentLine,
1555 TCLocalStrings::get(_UC("LDLModelBfcCertNotFirst")));
1556 }
1557 else
1558 {
1559 reportError(LDLEBFCError, *commentLine,
1560 TCLocalStrings::get(_UC("LDLModelBfcCertNoCert")));
1561 }
1562 }
1563 // Not else if below, because each BFC command could potentially
1564 // have both certify AND nocertify.
1565 if (commentLine->containsBFCCommand("NOCERTIFY"))
1566 {
1567 if (getBFCOn())
1568 {
1569 reportError(LDLEBFCError, *commentLine,
1570 TCLocalStrings::get(_UC("LDLModelBfcNoCertCert")));
1571 }
1572 else
1573 {
1574 reportWarning(LDLEBFCWarning, *commentLine,
1575 TCLocalStrings::get(_UC("LDLModelBfcNoCertMulti")));
1576 }
1577 }
1578 }
1579 if (getBFCOn())
1580 {
1581 if (commentLine->containsBFCCommand("CLIP"))
1582 {
1583 if (commentLine->containsBFCCommand("NOCLIP"))
1584 {
1585 reportError(LDLEBFCError, *commentLine,
1586 TCLocalStrings::get(_UC("LDLModelBfcClipNoClip")));
1587 }
1588 else
1589 {
1590 m_flags.bfcClip = true;
1591 }
1592 }
1593 else if (commentLine->containsBFCCommand("NOCLIP"))
1594 {
1595 m_flags.bfcClip = false;
1596 }
1597 if (commentLine->containsBFCCommand("CCW"))
1598 {
1599 if (commentLine->containsBFCCommand("CW"))
1600 {
1601 reportError(LDLEBFCError, *commentLine,
1602 TCLocalStrings::get(_UC("LDLModelBfcCwCcw")));
1603 }
1604 else
1605 {
1606 m_flags.bfcWindingCCW = true;
1607 }
1608 }
1609 else if (commentLine->containsBFCCommand("CW"))
1610 {
1611 m_flags.bfcWindingCCW = false;
1612 }
1613 if (commentLine->containsBFCCommand("INVERTNEXT"))
1614 {
1615 m_flags.bfcInvertNext = true;
1616 }
1617 }
1618 else
1619 {
1620 if (commentLine->containsBFCCommand("CLIP") ||
1621 commentLine->containsBFCCommand("NOCLIP") ||
1622 commentLine->containsBFCCommand("CW") ||
1623 commentLine->containsBFCCommand("CCW") ||
1624 commentLine->containsBFCCommand("INVERTNEXT"))
1625 {
1626 reportError(LDLEBFCError, *commentLine,
1627 TCLocalStrings::get(_UC("LDLModelBfcAfterNoCert")));
1628 }
1629 }
1630 return 0;
1631 }
1632
parseComment(int index,LDLCommentLine * commentLine)1633 int LDLModel::parseComment(int index, LDLCommentLine *commentLine)
1634 {
1635 std::string filename;
1636
1637 if (commentLine->getMPDFilename(&filename))
1638 {
1639 replaceStringCharacter(&filename[0], '\\', '/');
1640 return parseMPDMeta(index, filename.c_str());
1641 }
1642 else if (commentLine->isBFCMeta())
1643 {
1644 return parseBFCMeta(commentLine);
1645 }
1646 else if (commentLine->isPartMeta())
1647 {
1648 m_stepIndices.push_back(index);
1649 return 0;
1650 }
1651 else if (commentLine->isLDViewMeta())
1652 {
1653 if (commentLine->isBBoxIgnoreMeta())
1654 {
1655 return parseBBoxIgnoreMeta(commentLine);
1656 }
1657 else
1658 {
1659 reportWarning(LDLEMetaCommand, *commentLine,
1660 TCLocalStrings::get(_UC("LDLModelUnknownLDViewMeta")));
1661 }
1662 }
1663 else if (commentLine->isTexmapMeta())
1664 {
1665 return parseTexmapMeta(commentLine);
1666 }
1667 else if (index == 0)
1668 {
1669 delete[] m_description;
1670 m_description = copyString(&commentLine->getLine()[1]);
1671 stripLeadingWhitespace(m_description);
1672 stripTrailingWhitespace(m_description);
1673 }
1674 return 0;
1675 }
1676
1677 /*
1678 void LDLModel::processModelLine(LDLModelLine *modelLine)
1679 {
1680 m_flags.started = true;
1681 }
1682 */
1683
parse(void)1684 bool LDLModel::parse(void)
1685 {
1686 if (m_fileLines)
1687 {
1688 if (m_dataLine != NULL)
1689 {
1690 extractData();
1691 // Note: even if extractData fails, that does NOT mean that we
1692 // failed to parse the file.
1693 return true;
1694 }
1695 int i;
1696 int count = m_fileLines->getCount();
1697
1698 // ********************************************************************
1699 // NOTE: This for loop does a number of things that aren't normally
1700 // done (at least by me). In one place (when a line needs to be
1701 // replaced by new ones), it inserts new items in the array just after
1702 // the current spot, changes count, and uses continue. In another place
1703 // (when it sees an MPD secondary file), it increases i to skip over all
1704 // the lines in that secondary file (they get parsed separately).
1705 // ********************************************************************
1706 for (i = 0; i < count && !getLoadCanceled(); i++)
1707 {
1708 LDLFileLine *fileLine = (*m_fileLines)[i];
1709 bool checkInvertNext = true;
1710
1711 if (fileLine->isActionLine())
1712 {
1713 LDLActionLine *actionLine = (LDLActionLine *)fileLine;
1714
1715 m_flags.started = true;
1716 actionLine->setBFCSettings(m_flags.bfcCertify, m_flags.bfcClip,
1717 m_flags.bfcWindingCCW, m_flags.bfcInvertNext);
1718 if (m_flags.bboxIgnoreOn)
1719 {
1720 actionLine->setBBoxIgnore(true);
1721 m_mainModel->setBBoxIgnoreUsed(true);
1722 }
1723 if (!m_flags.bboxIgnoreBegun)
1724 {
1725 m_flags.bboxIgnoreOn = false;
1726 }
1727 if (m_flags.texmapStarted)
1728 {
1729 if (m_flags.texmapFallback)
1730 {
1731 actionLine->setTexmapFallback();
1732 }
1733 else
1734 {
1735 actionLine->setTexmapSettings(m_texmapType,
1736 m_texmapFilename, m_texmapImage, m_texmapPoints,
1737 m_texmapExtra);
1738 if (m_flags.texmapNext)
1739 {
1740 endTexmap();
1741 }
1742 }
1743 }
1744 }
1745 else
1746 {
1747 checkInvertNext = false;
1748 if (fileLine->getLineType() == LDLLineTypeComment &&
1749 m_flags.texmapStarted && !m_flags.texmapFallback)
1750 {
1751 ((LDLCommentLine *)fileLine)->setTexmapSettings(
1752 m_texmapType, m_texmapFilename, m_texmapImage,
1753 m_texmapPoints, m_texmapExtra);
1754 }
1755 }
1756 if (fileLine->parse())
1757 {
1758 if (fileLine->isValid())
1759 {
1760 if (fileLine->getError())
1761 {
1762 sendAlert(fileLine->getError());
1763 }
1764 }
1765 else
1766 {
1767 LDLFileLineArray *replacementLines =
1768 fileLine->getReplacementLines();
1769
1770 if (replacementLines)
1771 {
1772 int replacementCount = replacementLines->getCount();
1773 int j;
1774
1775 fileLine->setReplaced(true);
1776 for (j = 0; j < replacementCount; j++)
1777 {
1778 m_fileLines->insertObject((*replacementLines)[j],
1779 i + 1);
1780 }
1781 // Note that if we do get here, we haven't gotten past
1782 // m_activeLineCount, because that parsing gets done in
1783 // the secondary files themselves.
1784 m_activeLineCount += replacementCount;
1785 count += replacementCount;
1786 if (fileLine->getError())
1787 {
1788 sendAlert(fileLine->getError());
1789 }
1790 replacementLines->release();
1791 // ****************************************************
1792 // Note the use of continue below. I really shy away
1793 // from using it, but I'm goint to do so here.
1794 // ****************************************************
1795 continue;
1796 // ****************************************************
1797 // ****************************************************
1798 }
1799 else
1800 {
1801 sendAlert(fileLine->getError());
1802 }
1803 }
1804 }
1805 else
1806 {
1807 sendAlert(fileLine->getError());
1808 }
1809 switch (fileLine->getLineType())
1810 {
1811 case LDLLineTypeComment:
1812 {
1813 int skippedLines = parseComment(i,
1814 (LDLCommentLine *)fileLine);
1815
1816 checkInvertNext = false;
1817 if (skippedLines >= 0)
1818 {
1819 // ****************************************************
1820 // Note that I increment i below to skip over the lines
1821 // in the MPD secondary file I just encountered. (The
1822 // parseComment function will only return a number
1823 // greater than 0 if it found an MPD secondary file.
1824 // ****************************************************
1825 i += skippedLines;
1826 // ****************************************************
1827 // ****************************************************
1828 }
1829 }
1830 break;
1831 case LDLLineTypeModel:
1832 {
1833 LDLModel *model = ((LDLModelLine *)fileLine)->getModel();
1834
1835 if (model)
1836 {
1837 model->calcBoundingBox();
1838 }
1839 if (model != NULL && model->hasStuds())
1840 {
1841 m_flags.hasStuds = true;
1842 }
1843 m_flags.bfcInvertNext = false;
1844 }
1845 break;
1846 default:
1847 break;
1848 }
1849 if (checkInvertNext && m_flags.bfcInvertNext)
1850 {
1851 reportError(LDLEBFCError, *fileLine,
1852 TCLocalStrings::get(_UC("LDLModelBfcInvert")));
1853 m_flags.bfcInvertNext = false;
1854 }
1855 fileLine->setStepIndex((int)m_stepIndices.size());
1856 reportProgress(LOAD_MESSAGE, (float)i / (float)m_activeLineCount *
1857 (1.0f - MAIN_READ_FRACTION) + MAIN_READ_FRACTION);
1858 }
1859 return !getLoadCanceled();
1860 }
1861 else
1862 {
1863 // This is in an MPD, and has not yet been fully initialized.
1864 return true;
1865 }
1866 }
1867
cancelLoad(void)1868 void LDLModel::cancelLoad(void)
1869 {
1870 m_mainModel->cancelLoad();
1871 }
1872
getLoadCanceled(void)1873 bool LDLModel::getLoadCanceled(void)
1874 {
1875 return m_mainModel->getLoadCanceled();
1876 }
1877
sendAlert(LDLError * alert)1878 void LDLModel::sendAlert(LDLError *alert)
1879 {
1880 if (alert)
1881 {
1882 TCAlertManager::sendAlert(alert, this);
1883 if (alert->getLoadCanceled())
1884 {
1885 cancelLoad();
1886 }
1887 }
1888 }
1889
1890 /*
1891 void LDLModel::sendAlert(LDLErrorType type, LDLAlertLevel level,
1892 const char* format, va_list argPtr)
1893 {
1894 LDLError *alert;
1895
1896 alert = newError(type, format, argPtr);
1897 alert->setLevel(level);
1898 sendAlert(alert);
1899 alert->release();
1900 }
1901 */
1902
sendAlert(LDLErrorType type,LDLAlertLevel level,CUCSTR format,va_list argPtr)1903 void LDLModel::sendAlert(LDLErrorType type, LDLAlertLevel level, CUCSTR format,
1904 va_list argPtr)
1905 {
1906 LDLError *alert;
1907
1908 alert = newError(type, format, argPtr);
1909 alert->setLevel(level);
1910 sendAlert(alert);
1911 alert->release();
1912 }
1913
1914 /*
1915 void LDLModel::sendAlert(LDLErrorType type, LDLAlertLevel level,
1916 const LDLFileLine &fileLine, const char* format,
1917 va_list argPtr)
1918 {
1919 LDLError *alert;
1920
1921 alert = newError(type, fileLine, format, argPtr);
1922 alert->setLevel(level);
1923 sendAlert(alert);
1924 alert->release();
1925 }
1926 */
1927
sendAlert(LDLErrorType type,LDLAlertLevel level,const LDLFileLine & fileLine,CUCSTR format,va_list argPtr)1928 void LDLModel::sendAlert(LDLErrorType type, LDLAlertLevel level,
1929 const LDLFileLine &fileLine, CUCSTR format,
1930 va_list argPtr)
1931 {
1932 LDLError *alert;
1933
1934 alert = newError(type, fileLine, format, argPtr);
1935 alert->setLevel(level);
1936 sendAlert(alert);
1937 alert->release();
1938 }
1939
1940 /*
1941 void LDLModel::reportError(LDLErrorType type, const LDLFileLine &fileLine,
1942 const char* format, ...)
1943 {
1944 va_list argPtr;
1945
1946 va_start(argPtr, format);
1947 sendAlert(type, LDLAError, fileLine, format, argPtr);
1948 va_end(argPtr);
1949 }
1950 */
1951
reportError(LDLErrorType type,const LDLFileLine & fileLine,CUCSTR format,...)1952 void LDLModel::reportError(LDLErrorType type, const LDLFileLine &fileLine,
1953 CUCSTR format, ...)
1954 {
1955 va_list argPtr;
1956
1957 va_start(argPtr, format);
1958 sendAlert(type, LDLAError, fileLine, format, argPtr);
1959 va_end(argPtr);
1960 }
1961
1962 /*
1963 void LDLModel::reportWarning(LDLErrorType type, const LDLFileLine &fileLine,
1964 const char* format, ...)
1965 {
1966 va_list argPtr;
1967
1968 va_start(argPtr, format);
1969 sendAlert(type, LDLAWarning, fileLine, format, argPtr);
1970 va_end(argPtr);
1971 }
1972 */
1973
reportWarning(LDLErrorType type,const LDLFileLine & fileLine,CUCSTR format,...)1974 void LDLModel::reportWarning(LDLErrorType type, const LDLFileLine &fileLine,
1975 CUCSTR format, ...)
1976 {
1977 va_list argPtr;
1978
1979 va_start(argPtr, format);
1980 sendAlert(type, LDLAWarning, fileLine, format, argPtr);
1981 va_end(argPtr);
1982 }
1983
1984 /*
1985 void LDLModel::reportError(LDLErrorType type, const char* format, ...)
1986 {
1987 va_list argPtr;
1988
1989 va_start(argPtr, format);
1990 sendAlert(type, LDLAError, format, argPtr);
1991 va_end(argPtr);
1992 }
1993 */
1994
reportError(LDLErrorType type,CUCSTR format,...)1995 void LDLModel::reportError(LDLErrorType type, CUCSTR format, ...)
1996 {
1997 va_list argPtr;
1998
1999 va_start(argPtr, format);
2000 sendAlert(type, LDLAError, format, argPtr);
2001 va_end(argPtr);
2002 }
2003
2004 /*
2005 void LDLModel::reportWarning(LDLErrorType type, const char* format, ...)
2006 {
2007 va_list argPtr;
2008
2009 va_start(argPtr, format);
2010 sendAlert(type, LDLAWarning, format, argPtr);
2011 va_end(argPtr);
2012 }
2013 */
2014
reportWarning(LDLErrorType type,CUCSTR format,...)2015 void LDLModel::reportWarning(LDLErrorType type, CUCSTR format, ...)
2016 {
2017 va_list argPtr;
2018
2019 va_start(argPtr, format);
2020 sendAlert(type, LDLAWarning, format, argPtr);
2021 va_end(argPtr);
2022 }
2023
getLoadedModels(void)2024 TCDictionary *LDLModel::getLoadedModels(void)
2025 {
2026 return (m_mainModel->getLoadedModels());
2027 }
2028
print(int indent) const2029 void LDLModel::print(int indent) const
2030 {
2031 indentPrintf(indent, "LDLModel");
2032 if (m_flags.part)
2033 {
2034 printf(" (Part)");
2035 }
2036 if (m_flags.subPart)
2037 {
2038 printf(" (SubPart)");
2039 }
2040 switch (m_flags.bfcCertify)
2041 {
2042 case BFCOffState:
2043 printf(" (BFC Off)");
2044 break;
2045 case BFCOnState:
2046 printf(" (BFC On)");
2047 break;
2048 case BFCForcedOnState:
2049 printf(" (BFC Forced On)");
2050 break;
2051 default:
2052 break;
2053 }
2054 printf(": ");
2055 if (m_filename)
2056 {
2057 printf("%s", m_filename);
2058 }
2059 else
2060 {
2061 printf("<Unknown>");
2062 }
2063 printf("\n");
2064 if (m_fileLines)
2065 {
2066 int i;
2067 int count = m_fileLines->getCount();
2068
2069 for (i = 0; i < count; i++)
2070 {
2071 (*m_fileLines)[i]->print(indent + 1);
2072 }
2073 }
2074 }
2075
getLowResStuds(void) const2076 bool LDLModel::getLowResStuds(void) const
2077 {
2078 return m_mainModel->getLowResStuds();
2079 }
2080
newError(LDLErrorType type,const LDLFileLine & fileLine,CUCSTR format,va_list argPtr)2081 LDLError *LDLModel::newError(LDLErrorType type, const LDLFileLine &fileLine,
2082 CUCSTR format, va_list argPtr)
2083 {
2084 UCCHAR message[1024];
2085 UCCHAR** components;
2086 int componentCount;
2087 LDLError *error = NULL;
2088
2089 vsucprintf(message, COUNT_OF(message), format, argPtr);
2090 stripCRLF(message);
2091 components = componentsSeparatedByString(message, _UC("\n"),
2092 componentCount);
2093 if (componentCount > 1)
2094 {
2095 int i;
2096 #ifdef TC_NO_UNICODE
2097 TCStringArray *extraInfo = new TCStringArray(componentCount - 1);
2098
2099 *strchr(message, '\n') = 0;
2100 for (i = 1; i < componentCount; i++)
2101 {
2102 extraInfo->addString(components[i]);
2103 }
2104 error = new LDLError(type, message, m_filename, fileLine,
2105 fileLine.getFormattedLine(), fileLine.getLineNumber(), extraInfo);
2106 extraInfo->release();
2107 #else // TC_NO_UNICODE
2108 ucstringVector extraInfo;
2109 *wcschr(message, '\n') = 0;
2110 extraInfo.reserve(componentCount - 1);
2111 for (i = 1; i < componentCount; i++)
2112 {
2113 extraInfo.push_back(components[i]);
2114 }
2115 error = new LDLError(type, message, m_filename, fileLine,
2116 fileLine.getFormattedLine(), fileLine.getLineNumber(), extraInfo);
2117 #endif // TC_NO_UNICODE
2118 }
2119 else
2120 {
2121 error = new LDLError(type, message, m_filename, fileLine,
2122 fileLine.getFormattedLine(), fileLine.getLineNumber());
2123 }
2124 deleteStringArray(components, componentCount);
2125 return error;
2126 }
2127
newError(LDLErrorType type,const LDLFileLine & fileLine,CUCSTR format,...)2128 LDLError *LDLModel::newError(LDLErrorType type, const LDLFileLine &fileLine,
2129 CUCSTR format, ...)
2130 {
2131 va_list argPtr;
2132 LDLError *retValue;
2133
2134 va_start(argPtr, format);
2135 retValue = newError(type, fileLine, format, argPtr);
2136 va_end(argPtr);
2137 return retValue;
2138 }
2139
newError(LDLErrorType type,CUCSTR format,va_list argPtr)2140 LDLError *LDLModel::newError(LDLErrorType type, CUCSTR format, va_list argPtr)
2141 {
2142 UCCHAR message[1024];
2143 UCCHAR** components;
2144 int componentCount;
2145 LDLError *error = NULL;
2146
2147 vsucprintf(message, COUNT_OF(message), format, argPtr);
2148 stripCRLF(message);
2149 components = componentsSeparatedByString(message, _UC("\n"),
2150 componentCount);
2151 if (componentCount > 1)
2152 {
2153 int i;
2154 #ifdef TC_NO_UNICODE
2155 TCStringArray *extraInfo = new TCStringArray(componentCount - 1);
2156
2157 *strchr(message, '\n') = 0;
2158 for (i = 1; i < componentCount; i++)
2159 {
2160 extraInfo->addString(components[i]);
2161 }
2162 error = new LDLError(type, message, m_filename, NULL, NULL, -1,
2163 extraInfo);
2164 extraInfo->release();
2165 #else // TC_NO_UNICODE
2166 ucstringVector extraInfo;
2167 *wcschr(message, '\n') = 0;
2168 extraInfo.reserve(componentCount - 1);
2169 for (i = 1; i < componentCount; i++)
2170 {
2171 extraInfo.push_back(components[i]);
2172 }
2173 error = new LDLError(type, message, m_filename, NULL, NULL, -1,
2174 extraInfo);
2175 #endif // TC_NO_UNICODE
2176 }
2177 else
2178 {
2179 error = new LDLError(type, message, m_filename, NULL, NULL, -1);
2180 }
2181 deleteStringArray(components, componentCount);
2182 return error;
2183 }
2184
newError(LDLErrorType type,CUCSTR format,...)2185 LDLError *LDLModel::newError(LDLErrorType type, CUCSTR format, ...)
2186 {
2187 va_list argPtr;
2188 LDLError *retValue;
2189
2190 va_start(argPtr, format);
2191 retValue = newError(type, format, argPtr);
2192 va_end(argPtr);
2193 return retValue;
2194 }
2195
scanPoints(TCObject * scanner,LDLScanPointCallback scanPointCallback,const TCFloat * matrix,int step,bool watchBBoxIgnore) const2196 void LDLModel::scanPoints(
2197 TCObject *scanner,
2198 LDLScanPointCallback scanPointCallback,
2199 const TCFloat *matrix,
2200 int step /*= -1*/,
2201 bool watchBBoxIgnore /*= false*/) const
2202 {
2203 if (this != m_mainModel && isPart() && m_mainModel->getBoundingBoxesOnly()
2204 && m_flags.haveBoundingBox)
2205 {
2206 TCVector boxPoints[8];
2207 TCVector point;
2208 int i;
2209
2210 boxPoints[0] = m_boundingMin;
2211 boxPoints[4] = m_boundingMax;
2212 // Bottom square
2213 boxPoints[1] = boxPoints[0];
2214 boxPoints[1][0] = boxPoints[4][0];
2215 boxPoints[2] = boxPoints[0];
2216 boxPoints[2][1] = boxPoints[4][1];
2217 boxPoints[3] = boxPoints[4];
2218 boxPoints[3][2] = boxPoints[0][2];
2219 // Top square
2220 boxPoints[5] = boxPoints[4];
2221 boxPoints[5][0] = boxPoints[0][0];
2222 boxPoints[6] = boxPoints[4];
2223 boxPoints[6][1] = boxPoints[0][1];
2224 boxPoints[7] = boxPoints[0];
2225 boxPoints[7][2] = boxPoints[4][2];
2226 for (i = 0; i < 8; i++)
2227 {
2228 boxPoints[i].transformPoint(matrix, point);
2229 ((*scanner).*scanPointCallback)(point, NULL);
2230 }
2231 }
2232 else
2233 {
2234 int curStep = 0;
2235 bool emptyStep = true;
2236
2237 for (int i = 0; i < m_activeLineCount; i++)
2238 {
2239 LDLFileLine *fileLine = (*m_fileLines)[i];
2240 if (step >= 0 && fileLine->getLineType() == LDLLineTypeComment)
2241 {
2242 LDLCommentLine *commentLine = (LDLCommentLine *)fileLine;
2243
2244 if (commentLine->isStepMeta() && !emptyStep)
2245 {
2246 emptyStep = true;
2247 if (++curStep > step)
2248 {
2249 break;
2250 }
2251 }
2252 }
2253 if (fileLine->isActionLine())
2254 {
2255 LDLActionLine *actionLine = (LDLActionLine *)fileLine;
2256
2257 emptyStep = false;
2258 if (!watchBBoxIgnore || !actionLine->getBBoxIgnore())
2259 {
2260 actionLine->scanPoints(scanner, scanPointCallback,
2261 matrix, watchBBoxIgnore);
2262 }
2263 }
2264 }
2265 }
2266 }
2267
getBoundingBox(TCVector & min,TCVector & max) const2268 void LDLModel::getBoundingBox(TCVector &min, TCVector &max) const
2269 {
2270 calcBoundingBox();
2271 min = m_boundingMin;
2272 max = m_boundingMax;
2273 }
2274
getMaxRadius(const TCVector & center,bool watchBBoxIgnore)2275 TCFloat LDLModel::getMaxRadius(const TCVector ¢er, bool watchBBoxIgnore)
2276 {
2277 calcMaxRadius(center, watchBBoxIgnore);
2278 if (watchBBoxIgnore)
2279 {
2280 return m_maxRadius;
2281 }
2282 else
2283 {
2284 return m_maxFullRadius;
2285 }
2286 }
2287
scanBoundingBoxPoint(const TCVector & point,LDLFileLine * pFileLine)2288 void LDLModel::scanBoundingBoxPoint(
2289 const TCVector &point,
2290 LDLFileLine *pFileLine)
2291 {
2292 if (pFileLine == NULL ||
2293 pFileLine->getLineType() != LDLLineTypeConditionalLine)
2294 {
2295 if (m_flags.haveBoundingBox)
2296 {
2297 for (int i = 0; i < 3; i++)
2298 {
2299 if (point[i] < m_boundingMin[i])
2300 {
2301 m_boundingMin[i] = point[i];
2302 }
2303 else if (point[i] > m_boundingMax[i])
2304 {
2305 m_boundingMax[i] = point[i];
2306 }
2307 }
2308 }
2309 else
2310 {
2311 m_boundingMin = m_boundingMax = point;
2312 m_flags.haveBoundingBox = true;
2313 }
2314 }
2315 }
2316
calcBoundingBox(void) const2317 void LDLModel::calcBoundingBox(void) const
2318 {
2319 if (!m_flags.haveBoundingBox)
2320 {
2321 TCFloat matrix[16];
2322
2323 if (this == m_mainModel && m_mainModel->getBoundingBoxesOnly())
2324 {
2325 int i;
2326
2327 for (i = 0; i < m_fileLines->getCount(); i++)
2328 {
2329 LDLFileLine *fileLine = (*m_fileLines)[i];
2330
2331 if (fileLine->getLineType() == LDLLineTypeModel)
2332 {
2333 LDLModelLine *modelLine = (LDLModelLine *)fileLine;
2334 LDLModel *model = modelLine->getModel();
2335
2336 if (model != NULL)
2337 {
2338 model->calcBoundingBox();
2339 }
2340 }
2341 }
2342 }
2343 TCVector::initIdentityMatrix(matrix);
2344 // NOTE: we cannot compute bounding boxes heirarchically due to
2345 // rotation of child models. With their rotation, their bounding
2346 // boxes can easily stick out of the really minimum bounding box of
2347 // their parent.
2348 scanPoints(const_cast<LDLModel *>(this),
2349 (LDLScanPointCallback)&LDLModel::scanBoundingBoxPoint, matrix, -1, true);
2350 }
2351 }
2352
scanRadiusSquaredPoint(const TCVector & point,LDLFileLine * pFileLine)2353 void LDLModel::scanRadiusSquaredPoint(
2354 const TCVector &point,
2355 LDLFileLine *pFileLine)
2356 {
2357 if (pFileLine == NULL ||
2358 pFileLine->getLineType() != LDLLineTypeConditionalLine)
2359 {
2360 TCFloat radius = (m_center - point).lengthSquared();
2361
2362 if (m_flags.fullRadius)
2363 {
2364 if (!m_flags.haveMaxFullRadius || radius > m_maxFullRadius)
2365 {
2366 m_flags.haveMaxFullRadius = true;
2367 m_maxFullRadius = radius;
2368 }
2369 }
2370 else
2371 {
2372 if (!m_flags.haveMaxRadius || radius > m_maxRadius)
2373 {
2374 m_flags.haveMaxRadius = true;
2375 m_maxRadius = radius;
2376 }
2377 }
2378 }
2379 }
2380
calcMaxRadius(const TCVector & center,bool watchBBoxIgnore)2381 void LDLModel::calcMaxRadius(const TCVector ¢er, bool watchBBoxIgnore)
2382 {
2383 if (m_center != center)
2384 {
2385 m_flags.haveMaxRadius = false;
2386 m_flags.haveMaxFullRadius = false;
2387 }
2388 if ((watchBBoxIgnore && !m_flags.haveMaxRadius) ||
2389 (!watchBBoxIgnore && !m_flags.haveMaxFullRadius))
2390 {
2391 TCFloat matrix[16];
2392
2393 TCVector::initIdentityMatrix(matrix);
2394 m_center = center;
2395 m_flags.fullRadius = !watchBBoxIgnore;
2396 if (watchBBoxIgnore)
2397 {
2398 m_maxRadius = 0;
2399 }
2400 else
2401 {
2402 m_maxFullRadius = 0;
2403 }
2404 scanPoints(this,
2405 (LDLScanPointCallback)&LDLModel::scanRadiusSquaredPoint, matrix, -1,
2406 watchBBoxIgnore);
2407 if (watchBBoxIgnore)
2408 {
2409 m_maxRadius = (float)sqrt(m_maxRadius);
2410 }
2411 else
2412 {
2413 m_maxFullRadius = (float)sqrt(m_maxFullRadius);
2414 }
2415 }
2416 }
2417
getAlertSender(void)2418 TCObject *LDLModel::getAlertSender(void)
2419 {
2420 return m_mainModel->getAlertSender();
2421 }
2422
hasBoundingBox(void) const2423 bool LDLModel::hasBoundingBox(void) const
2424 {
2425 return m_flags.haveBoundingBox != false;
2426 }
2427
getFileLines(bool initialize)2428 LDLFileLineArray *LDLModel::getFileLines(bool initialize /*= false*/)
2429 {
2430 if (initialize && m_fileLines == NULL)
2431 {
2432 m_fileLines = new LDLFileLineArray;
2433 }
2434 return m_fileLines;
2435 }
2436
copyPublicFlags(const LDLModel * src)2437 void LDLModel::copyPublicFlags(const LDLModel *src)
2438 {
2439 m_flags.part = src->m_flags.part;
2440 m_flags.subPart = src->m_flags.subPart;
2441 m_flags.primitive = src->m_flags.primitive;
2442 m_flags.mpd = src->m_flags.mpd;
2443 m_flags.noShrink = src->m_flags.noShrink;
2444 m_flags.official = src->m_flags.official;
2445 m_flags.hasStuds = src->m_flags.hasStuds;
2446 m_flags.bfcCertify = src->m_flags.bfcCertify;
2447 }
2448
copyBoundingBox(const LDLModel * src)2449 void LDLModel::copyBoundingBox(const LDLModel *src)
2450 {
2451 if (!src->m_flags.haveBoundingBox)
2452 {
2453 src->calcBoundingBox();
2454 }
2455 m_boundingMin = src->m_boundingMin;
2456 m_boundingMax = src->m_boundingMax;
2457 m_flags.haveBoundingBox = true;
2458 }
2459
searchNext(const std::string & searchString,IntVector & path,int loopEnd,TCULong activeLineTypes) const2460 bool LDLModel::searchNext(
2461 const std::string &searchString,
2462 IntVector& path,
2463 int loopEnd,
2464 TCULong activeLineTypes) const
2465 {
2466 if (m_fileLines == NULL || m_activeLineCount == 0)
2467 {
2468 path.clear();
2469 return false;
2470 }
2471 IntVector result;
2472 int count = m_activeLineCount;
2473 int endIndex = path.empty() && loopEnd != -1 ? loopEnd + 1: count;
2474 int startIndex = path.empty() ? 0 : path[0];
2475 for (int i = startIndex; i < endIndex; ++i)
2476 {
2477 const LDLFileLine *child = (*m_fileLines)[i];
2478 if (((1 << child->getLineType()) & activeLineTypes) == 0)
2479 {
2480 continue;
2481 }
2482 IntVector childPath;
2483 int lineOffset = 0;
2484 bool skipText = false;
2485 if (!path.empty() && i == path[0])
2486 {
2487 skipText = true;
2488 if (path.size() > 1)
2489 {
2490 childPath.insert(childPath.end(), path.begin() + 1, path.end());
2491 }
2492 }
2493 if (!skipText)
2494 {
2495 const char *match = strcasestr(child->getLine() + lineOffset,
2496 searchString.c_str());
2497
2498 if (match != NULL)
2499 {
2500 result.push_back(i);
2501 path = result;
2502 return true;
2503 }
2504 }
2505 if (child->getLineType() == LDLLineTypeModel)
2506 {
2507 LDLModel *childModel = ((LDLModelLine *)child)->getModel();
2508
2509 if (childModel != NULL && childModel->searchNext(searchString,
2510 childPath, -1, activeLineTypes))
2511 {
2512 result.push_back(i);
2513 result.insert(result.end(), childPath.begin(), childPath.end());
2514 path = result;
2515 return true;
2516 }
2517 }
2518 }
2519 if (!path.empty() && loopEnd != -1)
2520 {
2521 path.clear();
2522 return searchNext(searchString, path, loopEnd, activeLineTypes);
2523 }
2524 path.clear();
2525 return false;
2526 }
2527
searchPrevious(const std::string & searchString,IntVector & path,int loopEnd,TCULong activeLineTypes) const2528 bool LDLModel::searchPrevious(
2529 const std::string &searchString,
2530 IntVector& path,
2531 int loopEnd,
2532 TCULong activeLineTypes) const
2533 {
2534 if (m_fileLines == NULL || m_activeLineCount == 0)
2535 {
2536 path.clear();
2537 return false;
2538 }
2539 IntVector result;
2540 int count = m_activeLineCount;
2541 int startIndex = path.empty() ? count - 1 : path[0];
2542 int endIndex = path.empty() && loopEnd != -1 ? loopEnd : 0;
2543 for (int i = startIndex; i >= endIndex; --i)
2544 {
2545 const LDLFileLine *child = (*m_fileLines)[i];
2546 if (((1 << child->getLineType()) & activeLineTypes) == 0)
2547 {
2548 continue;
2549 }
2550 IntVector childPath;
2551 int lineOffset = 0;
2552 if (!path.empty() && i == path[0])
2553 {
2554 if (path.size() == 1)
2555 {
2556 continue;
2557 }
2558 else
2559 {
2560 childPath.insert(childPath.end(), path.begin() + 1, path.end());
2561 }
2562 }
2563 if (child->getLineType() == LDLLineTypeModel)
2564 {
2565 LDLModel *childModel = ((LDLModelLine *)child)->getModel();
2566
2567 if (childModel != NULL && childModel->searchPrevious(searchString,
2568 childPath, -1, activeLineTypes))
2569 {
2570 result.push_back(i);
2571 result.insert(result.end(), childPath.begin(), childPath.end());
2572 path = result;
2573 return true;
2574 }
2575 }
2576 const char *match = strcasestr(child->getLine() + lineOffset,
2577 searchString.c_str());
2578
2579 if (match != NULL)
2580 {
2581 result.push_back(i);
2582 path = result;
2583 return true;
2584 }
2585 }
2586 if (!path.empty() && loopEnd != -1)
2587 {
2588 path.clear();
2589 return searchPrevious(searchString, path, loopEnd, activeLineTypes);
2590 }
2591 path.clear();
2592 return false;
2593 }
2594