1 #include "LDrawModelViewer.h"
2 #include <TCFoundation/TCMacros.h>
3 #include <TCFoundation/TCAutoreleasePool.h>
4 #include <TCFoundation/mystring.h>
5 #include <TCFoundation/TCImage.h>
6 #include <TCFoundation/TCJpegOptions.h>
7 #include <TCFoundation/TCAlertManager.h>
8 #include <TCFoundation/TCProgressAlert.h>
9 #include <TCFoundation/TCLocalStrings.h>
10 #include <TCFoundation/TCUserDefaults.h>
11 #include <TCFoundation/TCWebClient.h>
12 #include <LDLoader/LDLMainModel.h>
13 #include <LDLoader/LDLError.h>
14 #include <LDLoader/LDLFindFileAlert.h>
15 #include <LDLoader/LDLPalette.h>
16 #include <LDLoader/LDLAutoCamera.h>
17 #include <LDLoader/LDLModelLine.h>
18 #include <LDLoader/LDLConditionalLineLine.h>
19 #include <LDExporter/LDPovExporter.h>
20 #include <LDExporter/LDStlExporter.h>
21 #ifdef EXPORT_3DS
22 #include <LDExporter/LD3dsExporter.h>
23 #endif // EXPORT_3DS
24 #ifdef EXPORT_LDR
25 #include <LDExporter/LDLdrExporter.h>
26 #endif // EXPORT_LDR
27 #include "LDInputHandler.h"
28 #include "LDModelParser.h"
29 #include "LDPreferences.h"
30 #include "LDPartsList.h"
31 #include "LDViewPoint.h"
32 #include "LDUserDefaultsKeys.h"
33 #include <TRE/TREMainModel.h>
34 #include <TRE/TREGL.h>
35 #include <time.h>
36 #include <gl2ps/gl2ps.h>
37
38 #ifndef USE_STD_CHRONO
39 #ifdef COCOA
40 #include <Foundation/Foundation.h>
41 #define FRAME_TIME ((NSDate *&)frameTime)
42 #endif // COCOA
43 #endif // !USE_STD_CHRONO
44
45 #ifdef USE_STD_CHRONO
46 //#define TIME_MODEL_LOAD
47 #endif // USE_STD_CHRONO
48
49 #ifdef TIME_MODEL_LOAD
50 #include <iostream>
51 #include <ctime>
52 #endif // TIME_MODEL_LOAD
53
54 #ifdef WIN32
55 #if defined(_MSC_VER) && _MSC_VER >= 1400 && defined(_DEBUG)
56 #define new DEBUG_CLIENTBLOCK
57 #endif // _DEBUG
58 #endif // WIN32
59
60 #if (defined _AIX) && !defined (fmodf)
fmodf(float x,float y)61 float fmodf(float x, float y)
62 {
63 return (x-((int)(x/y))*y);
64 }
65 #endif
66
67 #define FONT_CHAR_WIDTH 8
68 #define FONT_IMAGE_WIDTH 128
69 #define FONT_IMAGE_HEIGHT 256
70 #define FONT_NUM_CHARACTERS 256
71 #define DEF_DISTANCE_MULT 1.0f
72
73 LDrawModelViewer::StandardSizeList LDrawModelViewer::standardSizes;
74 std::string LDrawModelViewer::sm_appVersion;
75 std::string LDrawModelViewer::sm_appCopyright;
76
LDrawModelViewer(TCFloat width,TCFloat height)77 LDrawModelViewer::LDrawModelViewer(TCFloat width, TCFloat height)
78 :mainTREModel(NULL),
79 mainModel(NULL),
80 whiteLightDirModel(NULL),
81 blueLightDirModel(NULL),
82 highlightModel(NULL),
83 filename(NULL),
84 programPath(NULL),
85 width(width),
86 height(height),
87 scaleFactor(1.0f),
88 pixelAspectRatio(1.0f),
89 cullBackFaces(0),
90 viewMode(VMExamine),
91 examineMode(EMFree),
92 xRotate(0.0f),
93 yRotate(0.0f),
94 zRotate(0.0f),
95 rotationSpeed(0.0f),
96 cameraXRotate(0.0f),
97 cameraYRotate(0.0f),
98 cameraZRotate(0.0f),
99 zoomSpeed(0.0f),
100 xPan(0.0f),
101 yPan(0.0f),
102 rotationMatrix(NULL),
103 defaultRotationMatrix(NULL),
104 defaultLatitude(30.0f),
105 defaultLongitude(45.0f),
106 examineLatitude(defaultLatitude),
107 examineLongitude(defaultLongitude),
108 clipAmount(0.0f),
109 nextClipAmount(-1.0f),
110 nextDistance(-1.0f),
111 highlightLineWidth(1.0f),
112 wireframeLineWidth(1.0f),
113 anisoLevel(1.0f),
114 clipZoom(false),
115 fontListBase(0),
116 backgroundR(0.0f),
117 backgroundG(0.0f),
118 backgroundB(0.0f),
119 backgroundA(1.0f),
120 defaultR(153),
121 defaultG(153),
122 defaultB(153),
123 defaultColorNumber(-1),
124 xTile(0),
125 yTile(0),
126 numXTiles(1),
127 numYTiles(1),
128 stereoMode(LDVStereoNone),
129 stereoEyeSpacing(50.0f),
130 cutawayMode(LDVCutawayNormal),
131 cutawayAlpha(1.0f),
132 cutawayLineWidth(1.0f),
133 zoomMax(1.99f),
134 curveQuality(2),
135 textureFilterType(GL_LINEAR_MIPMAP_LINEAR),
136 distanceMultiplier(DEF_DISTANCE_MULT),
137 fontImage1x(NULL),
138 fontImage2x(NULL),
139 aspectRatio(1.0f),
140 currentFov(45.0f),
141 fov(45.0f),
142 extraSearchDirs(NULL),
143 seamWidth(0.5f),
144 memoryUsage(2),
145 lightVector(0.0f, 0.0f, 1.0f),
146 preferences(NULL),
147 mouseMode(LDVMouseNone),
148 inputHandler(NULL),
149 step(-1),
150 commandLineStep(-1),
151 mpdChildIndex(0),
152 exporter(NULL),
153 exportType(ETPov),
154 highlightR(160),
155 highlightG(224),
156 highlightB(255),
157 cameraData(NULL)
158 {
159 #ifdef _LEAK_DEBUG
160 strcpy(className, "LDrawModelViewer");
161 #endif
162 flags.qualityLighting = false;
163 flags.showsHighlightLines = false;
164 flags.qualityStuds = false;
165 flags.usesFlatShading = false;
166 flags.usesSpecular = true;
167 flags.drawWireframe = false;
168 flags.useWireframeFog = false;
169 flags.usePolygonOffset = true;
170 flags.useLighting = true;
171 flags.subduedLighting = false;
172 flags.allowPrimitiveSubstitution = true;
173 flags.useStipple = false;
174 flags.fileIsPart = false;
175 flags.sortTransparent = true;
176 flags.needsSetup = true;
177 flags.needsTextureSetup = true;
178 flags.needsMaterialSetup = true;
179 flags.needsLightingSetup = true;
180 flags.needsReload = false;
181 flags.needsReparse = false;
182 flags.needsRecompile = false;
183 flags.needsResize = true;
184 flags.needsCalcSize = false;
185 flags.needsRotationMatrixSetup = true;
186 flags.needsViewReset = true;
187 flags.needsResetStep = false;
188 flags.needsResetMpd = false;
189 flags.paused = false;
190 flags.slowClear = false;
191 flags.blackHighlights = false;
192 flags.textureStuds = true;
193 flags.oneLight = false;
194 flags.drawConditionalHighlights = false;
195 flags.showAllConditionalLines = false;
196 flags.showConditionalControlPoints = false;
197 flags.performSmoothing = true;
198 flags.lineSmoothing = false;
199 flags.constrainZoom = true;
200 flags.edgesOnly = false;
201 flags.hiResPrimitives = false;
202 flags.processLDConfig = true;
203 flags.skipValidation = false;
204 flags.autoCenter = true;
205 flags.forceZoomToFit = false;
206 flags.defaultTrans = false;
207 flags.bfc = true;
208 flags.redBackFaces = false;
209 flags.greenFrontFaces = false;
210 flags.blueNeutralFaces = false;
211 flags.defaultLightVector = true;
212 flags.overrideModelCenter = false;
213 flags.overrideModelSize = false;
214 flags.overrideDefaultDistance = false;
215 flags.checkPartTracker = true;
216 flags.showLight = false;
217 flags.drawLightDats = true;
218 flags.optionalStandardLight = true;
219 flags.noLightGeom = false;
220 flags.updating = false;
221 flags.saveAlpha = false;
222 flags.showAxes = false;
223 flags.axesAtOrigin = true;
224 flags.showBoundingBox = false;
225 flags.boundingBoxesOnly = false;
226 flags.obi = false;
227 flags.gl2ps = false;
228 flags.povCameraAspect = TCUserDefaults::boolForKey(POV_CAMERA_ASPECT_KEY,
229 false, false);
230 flags.animating = false;
231 flags.randomColors = false;
232 flags.noUI = false;
233 flags.keepRightSideUp = false;
234 flags.texmaps = true;
235 flags.useStrips = true;
236 TCAlertManager::registerHandler(LDLFindFileAlert::alertClass(), this,
237 (TCAlertCallback)&LDrawModelViewer::findFileAlertCallback);
238 // Set 4:4:4 as the default sub-sample pattern for JPEG images.
239 TCJpegOptions::setDefaultSubSampling(TCJpegOptions::SS444);
240 #ifndef USE_STD_CHRONO
241 #ifdef WIN32
242 if (!QueryPerformanceFrequency(&hrpcFrequency))
243 {
244 hrpcFrequency.QuadPart = 0;
245 }
246 #endif // WIN32
247 #ifdef _QT
248 qtime.start();
249 #endif
250 #ifdef COCOA
251 FRAME_TIME = nil;
252 #endif // COCOA
253 #endif // !USE_STD_CHRONO
254 updateFrameTime(true);
255 }
256
~LDrawModelViewer(void)257 LDrawModelViewer::~LDrawModelViewer(void)
258 {
259 }
260
dealloc(void)261 void LDrawModelViewer::dealloc(void)
262 {
263 #ifndef USE_STD_CHRONO
264 #ifdef COCOA
265 [FRAME_TIME release];
266 #endif // COCOA
267 #endif // !USE_STD_CHRONO
268 TCAlertManager::unregisterHandler(this);
269 TCObject::release(inputHandler);
270 TCObject::release(mainTREModel);
271 TCObject::release(mainModel);
272 TCObject::release(whiteLightDirModel);
273 TCObject::release(blueLightDirModel);
274 TCObject::release(highlightModel);
275 TCObject::release(exporter);
276 mainTREModel = NULL;
277 delete[] filename;
278 filename = NULL;
279 delete[] programPath;
280 programPath = NULL;
281 delete[] rotationMatrix;
282 rotationMatrix = NULL;
283 delete[] defaultRotationMatrix;
284 defaultRotationMatrix = NULL;
285 TCObject::release(fontImage1x);
286 fontImage1x = NULL;
287 TCObject::release(fontImage2x);
288 fontImage2x = NULL;
289 TCObject::release(extraSearchDirs);
290 extraSearchDirs = NULL;
291 delete cameraData;
292 cameraData = NULL;
293 TCObject::dealloc();
294 }
295
getInputHandler(void)296 LDInputHandler *LDrawModelViewer::getInputHandler(void)
297 {
298 if (!inputHandler)
299 {
300 inputHandler = new LDInputHandler(this);
301 }
302 return inputHandler;
303 }
304
setFilename(const char * value)305 void LDrawModelViewer::setFilename(const char* value)
306 {
307 delete[] filename;
308 filename = copyString(value);
309 mpdName = "";
310 if (filename != NULL)
311 {
312 char *mpdSpot = NULL;
313
314 #ifdef WIN32
315 if (strlen(filename) > 2)
316 {
317 mpdSpot = strchr(&filename[2], ':');
318 }
319 #else // WIN32
320 mpdSpot = strchr(filename, ':');
321 #endif // WIN32
322 if (mpdSpot != NULL)
323 {
324 mpdName = &mpdSpot[1];
325 mpdSpot[0] = 0;
326 }
327 }
328 flags.needsResetStep = true;
329 flags.needsResetMpd = true;
330 highlightPaths.clear();
331 }
332
setProgramPath(const char * value)333 void LDrawModelViewer::setProgramPath(const char *value)
334 {
335 delete[] programPath;
336 programPath = copyString(value);
337 stripTrailingPathSeparators(programPath);
338 }
339
setFileIsPart(bool value)340 void LDrawModelViewer::setFileIsPart(bool value)
341 {
342 flags.fileIsPart = value;
343 }
344
applyTile(void)345 void LDrawModelViewer::applyTile(void)
346 {
347 //if (1 || numXTiles > 1 || numYTiles > 1)
348 {
349 GLint tileLeft;
350 GLint tileBottom;
351 GLfloat xScale, yScale;
352 GLfloat xOffset, yOffset;
353
354 tileLeft = (int)(xTile * scale(width));
355 tileBottom = (int)((numYTiles - yTile - 1) * scale(height));
356 xScale = (GLfloat)((width * numXTiles) / width);
357 yScale = (GLfloat)((height * numYTiles) / height);
358 xOffset = (-2.0f * tileLeft) / (scale(width) * numXTiles) +
359 (1 - 1.0f / numXTiles);
360 yOffset = (-2.0f * tileBottom) / (scale(height) * numYTiles) +
361 (1 - 1.0f / numYTiles);
362 glScalef(xScale, yScale, 1.0f);
363 treGlTranslatef(xOffset, yOffset, 0.0f);
364 }
365 }
366
getStereoWidthModifier(void)367 TCFloat LDrawModelViewer::getStereoWidthModifier(void)
368 {
369 if (stereoMode == LDVStereoCrossEyed || stereoMode == LDVStereoParallel)
370 {
371 return 2.0f;
372 }
373 else
374 {
375 return 1.0f;
376 }
377 }
378
setFieldOfView(double lfov,TCFloat nClip,TCFloat fClip)379 void LDrawModelViewer::setFieldOfView(double lfov, TCFloat nClip, TCFloat fClip)
380 {
381 GLdouble aspectWidth, aspectHeight;
382
383 // printf("LDrawModelViewer::setFieldOfView(%.5f, %.5f, %.5f)\n", lfov, nClip,
384 // fClip);
385 glMatrixMode(GL_PROJECTION);
386 glLoadIdentity();
387 applyTile();
388 aspectWidth = width * numXTiles / getStereoWidthModifier();
389 aspectHeight = height * numYTiles * pixelAspectRatio;
390 #ifdef __clang__
391 #pragma clang diagnostic push
392 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
393 #endif
394 gluPerspective(lfov, aspectWidth / aspectHeight, nClip, fClip);
395 #ifdef __clang__
396 #pragma clang diagnostic pop
397 #endif
398 glMatrixMode(GL_MODELVIEW);
399 }
400
perspectiveView(void)401 void LDrawModelViewer::perspectiveView(void)
402 {
403 perspectiveView(true);
404 }
405
setFov(TCFloat value)406 void LDrawModelViewer::setFov(TCFloat value)
407 {
408 if (value != fov)
409 {
410 fov = value;
411 flags.needsViewReset = true;
412 }
413 }
414
getHFov(void)415 TCFloat LDrawModelViewer::getHFov(void)
416 {
417 TCFloat actualWidth = width / getStereoWidthModifier();
418
419 return (TCFloat)(2.0 * rad2deg(atan(tan(deg2rad(fov / 2.0)) *
420 (actualWidth * numXTiles) / height * numYTiles)));
421 }
422
updateCurrentFov(void)423 void LDrawModelViewer::updateCurrentFov(void)
424 {
425 TCFloat actualWidth = width / getStereoWidthModifier();
426
427 currentFov = TCUserDefaults::floatForKey(HFOV_KEY, -1, false);
428 if (currentFov == -1)
429 {
430 currentFov = fov;
431 if (actualWidth * numXTiles < height * numYTiles)
432 {
433 // When the window is taller than it is wide, we want our current
434 // FOV to be the horizontal FOV, so we need to calculate the
435 // vertical FOV.
436 //
437 // From Lars Hassing:
438 // Vertical FOV = 2*atan(tan(hfov/2)/(width/height))
439 currentFov = (TCFloat)(2.0 * rad2deg(atan(tan(deg2rad(fov / 2.0)) *
440 height * numYTiles / (actualWidth * numXTiles))));
441
442 if (currentFov > 179.0f)
443 {
444 currentFov = 179.0f;
445 }
446 aspectRatio = height / actualWidth;
447 }
448 else
449 {
450 aspectRatio = actualWidth / height;
451 }
452 }
453 else
454 {
455 fov = (TCFloat)(2.0 * rad2deg(atan(tan(deg2rad(currentFov / 2.0)) *
456 height * numYTiles / (actualWidth * numXTiles))));
457 if (actualWidth * numXTiles > height * numYTiles)
458 {
459 currentFov = fov;
460 aspectRatio = actualWidth / height;
461 }
462 else
463 {
464 aspectRatio = height / actualWidth;
465 }
466 }
467 }
468
getClipRadius(void)469 TCFloat LDrawModelViewer::getClipRadius(void)
470 {
471 TCFloat clipRadius;
472
473 if (flags.autoCenter)
474 {
475 clipRadius = clipSize / 1.45f;
476 }
477 else
478 {
479 // If we aren't centered, then just double the clip radius, and that
480 // guarantees everything will fit. Remember that the near clip plane
481 // has a minimum distance, so even if it ends up initially behind the
482 // camera, we clamp it to be in front.
483 clipRadius = clipSize;
484 }
485 return clipRadius;
486 }
487
getZDistance(void)488 TCFloat LDrawModelViewer::getZDistance(void)
489 {
490 if (flags.autoCenter)
491 {
492 TCFloat inverseMatrix[16];
493 TCVector vector = camera.getPosition();
494
495 camera.getFacing().getInverseMatrix(inverseMatrix);
496 vector = vector.transformPoint(inverseMatrix);
497 return vector[2];
498 }
499 else
500 {
501 return (center - camera.getPosition()).length();
502 }
503 }
504
perspectiveView(bool resetViewport)505 void LDrawModelViewer::perspectiveView(bool resetViewport)
506 {
507 TCFloat nClip;
508 TCFloat fClip;
509 TCFloat clipRadius = getClipRadius();
510 int actualWidth = (int)scale(width) / (int)getStereoWidthModifier();
511 TCFloat zDistance = getZDistance();
512 //TCFloat aspectAdjust = (TCFloat)tan(1.0f);
513
514 if (flags.forceZoomToFit)
515 {
516 zoomToFit();
517 }
518 updateCurrentFov();
519 if (resetViewport)
520 {
521 glViewport(0, 0, actualWidth, (GLsizei)scale(height));
522 flags.needsResize = false;
523 }
524 //aspectRatio = (TCFloat)(1.0f / tan(1.0f / aspectRatio)) * aspectAdjust;
525 aspectRatio = 1.0f;
526 nClip = zDistance - clipRadius * aspectRatio + clipAmount * aspectRatio *
527 clipRadius;
528 if (nClip < clipSize / 1000.0f)
529 {
530 nClip = clipSize / 1000.0f;
531 }
532 fClip = zDistance + clipRadius * aspectRatio;
533 setFieldOfView(currentFov, nClip, fClip);
534 glLoadIdentity();
535 glFogi(GL_FOG_MODE, GL_LINEAR);
536 glFogf(GL_FOG_START, (GLfloat)nClip);
537 glFogf(GL_FOG_END, (GLfloat)fClip);
538 }
539
perspectiveViewToClipPlane(void)540 void LDrawModelViewer::perspectiveViewToClipPlane(void)
541 {
542 TCFloat nClip;
543 TCFloat fClip;
544 TCFloat zDistance = getZDistance();
545 // TCFloat zDistance = (camera.getPosition()).length();
546 TCFloat clipRadius = getClipRadius();
547 // TCFloat distance = (camera.getPosition() - center).length();
548
549 nClip = zDistance - clipSize / 2.0f;
550 // fClip = distance - size * aspectRatio / 2.0f + clipAmount * aspectRatio *
551 // size;
552 fClip = zDistance - clipRadius * aspectRatio + clipAmount * aspectRatio *
553 clipRadius;
554 if (fClip < clipSize / 1000.0f)
555 {
556 fClip = clipSize / 1000.0f;
557 }
558 if (nClip < clipSize / 1000.0f)
559 {
560 nClip = clipSize / 1000.0f;
561 }
562 setFieldOfView(currentFov, nClip, fClip);
563 glLoadIdentity();
564 glFogi(GL_FOG_MODE, GL_LINEAR);
565 glFogf(GL_FOG_START, (GLfloat)nClip);
566 glFogf(GL_FOG_END, (GLfloat)fClip);
567 }
568
569 /*
570 void LDrawModelViewer::setProgressCallback(LDMProgressCallback callback,
571 void* userData)
572 {
573 progressCallback = callback;
574 progressUserData = userData;
575 }
576
577 void LDrawModelViewer::setErrorCallback(LDMErrorCallback callback,
578 void* userData)
579 {
580 errorCallback = callback;
581 errorUserData = userData;
582 }
583 */
584
skipCameraPositioning(void)585 bool LDrawModelViewer::skipCameraPositioning(void)
586 {
587 return defaultRotationMatrix && (defaultRotationMatrix[12] != 0.0f ||
588 defaultRotationMatrix[13] != 0.0f || defaultRotationMatrix[14] != 0.0f);
589 }
590
calcDefaultDistance(void)591 TCFloat LDrawModelViewer::calcDefaultDistance(void)
592 {
593 if (flags.overrideDefaultDistance)
594 {
595 return defaultDistance;
596 }
597 // Note that the margin is on all sides, so it's on two edges per axis,
598 // which is why we multiply by 2.
599 TCFloat margin = getWideLineMargin() * 2.0f;
600 TCFloat marginAdjust = 1.0f;
601
602 if (margin != 0.0f)
603 {
604 int actualWidth = (int)scale(width) / (int)getStereoWidthModifier() *
605 numXTiles;
606 int actualHeight = (int)scale(height) * numYTiles;
607
608 if (actualWidth < actualHeight)
609 {
610 marginAdjust = (actualHeight + margin) / actualHeight;
611 }
612 else
613 {
614 marginAdjust = (actualWidth + margin) / actualWidth;
615 }
616 }
617 return (TCFloat)(size / 2.0 / sin(deg2rad(fov / 2.0))) * distanceMultiplier
618 * marginAdjust;
619 /*
620 double angle1 = deg2rad(90.0f - (currentFov / 2.0));
621 double angle2 = deg2rad(currentFov / 4.0);
622 double radius = size / 2.0;
623
624 return (TCFloat)(radius * tan(angle1) + radius * tan(angle2)) *
625 distanceMultiplier;
626 */
627 }
628
resetView(LDVAngle viewAngle)629 void LDrawModelViewer::resetView(LDVAngle viewAngle)
630 {
631 flags.needsViewReset = false;
632 flags.autoCenter = true;
633 if (!mainTREModel)
634 {
635 return;
636 }
637 if (clipAmount != 0.0f)
638 {
639 clipAmount = 0.0f;
640 perspectiveView();
641 }
642 defaultDistance = calcDefaultDistance();
643 if (!skipCameraPositioning())
644 {
645 // If the user specifies a rotation matrix that includes a translation,
646 // then don't move the camera.
647 camera.setPosition(TCVector(0.0f, 0.0f, defaultDistance));
648 }
649 camera.setFacing(LDLFacing());
650 if (!rotationMatrix)
651 {
652 rotationMatrix = new TCFloat[16];
653 }
654 switch (viewAngle)
655 {
656 case LDVAngleDefault:
657 setupDefaultViewAngle();
658 break;
659 case LDVAngleFront:
660 setupFrontViewAngle();
661 break;
662 case LDVAngleBack:
663 setupBackViewAngle();
664 break;
665 case LDVAngleLeft:
666 setupLeftViewAngle();
667 break;
668 case LDVAngleRight:
669 setupRightViewAngle();
670 break;
671 case LDVAngleTop:
672 setupTopViewAngle();
673 break;
674 case LDVAngleBottom:
675 setupBottomViewAngle();
676 break;
677 case LDVAngleIso:
678 setupIsoViewAngle();
679 break;
680 }
681 flags.needsRotationMatrixSetup = true;
682 xPan = 0.0f;
683 yPan = 0.0f;
684 perspectiveView(true);
685 }
686
setModelCenter(const TCVector & value)687 void LDrawModelViewer::setModelCenter(const TCVector &value)
688 {
689 if (value)
690 {
691 flags.overrideModelCenter = true;
692 center = value;
693 flags.needsSetup = true;
694 }
695 }
696
setPovCameraAspect(bool value,bool saveSetting)697 void LDrawModelViewer::setPovCameraAspect(bool value, bool saveSetting)
698 {
699 if (value != flags.povCameraAspect)
700 {
701 flags.povCameraAspect = value;
702 if (saveSetting)
703 {
704 TCUserDefaults::setBoolForKey(value, POV_CAMERA_ASPECT_KEY, false);
705 }
706 }
707 }
708
setModelSize(const TCFloat value)709 void LDrawModelViewer::setModelSize(const TCFloat value)
710 {
711 if (value)
712 {
713 flags.overrideModelSize = true;
714 size = value;
715 clipSize = value;
716 flags.needsSetup = true;
717 }
718 }
719
setDefaultDistance(TCFloat value)720 void LDrawModelViewer::setDefaultDistance(TCFloat value)
721 {
722 if (value > 0)
723 {
724 defaultDistance = value;
725 flags.overrideDefaultDistance = true;
726 }
727 }
728
setDefaultRotationMatrix(const TCFloat * value)729 void LDrawModelViewer::setDefaultRotationMatrix(const TCFloat *value)
730 {
731 if (value)
732 {
733 if (!defaultRotationMatrix || memcmp(defaultRotationMatrix, value,
734 16 * sizeof(TCFloat)) != 0)
735 {
736 delete[] defaultRotationMatrix;
737 defaultRotationMatrix = new TCFloat[16];
738 memcpy(defaultRotationMatrix, value, 16 * sizeof(TCFloat));
739 flags.needsSetup = true;
740 }
741 }
742 else if (defaultRotationMatrix)
743 {
744 delete[] defaultRotationMatrix;
745 defaultRotationMatrix = NULL;
746 flags.needsSetup = true;
747 }
748 }
749
setDefaultLatLong(TCFloat latitude,TCFloat longitude)750 void LDrawModelViewer::setDefaultLatLong(TCFloat latitude, TCFloat longitude)
751 {
752 if (defaultLatitude != latitude || defaultLongitude != longitude)
753 {
754 defaultLatitude = latitude;
755 defaultLongitude = longitude;
756 flags.needsSetup = true;
757 }
758 }
759
setupDefaultViewAngle(void)760 void LDrawModelViewer::setupDefaultViewAngle(void)
761 {
762 if (viewMode == VMExamine && examineMode == EMLatLong)
763 {
764 examineLatitude = defaultLatitude;
765 examineLongitude = defaultLongitude;
766 }
767 else if (defaultRotationMatrix)
768 {
769 memcpy(rotationMatrix, defaultRotationMatrix, 16 * sizeof(TCFloat));
770 }
771 else
772 {
773 setupIsoViewAngle();
774 }
775 }
776
setupIsoViewAngle(void)777 void LDrawModelViewer::setupIsoViewAngle(void)
778 {
779 rotationMatrix[0] = (TCFloat)(sqrt(2.0) / 2.0);
780 rotationMatrix[1] = (TCFloat)(sqrt(2.0) / 4.0);
781 rotationMatrix[2] = (TCFloat)(-sqrt(1.5) / 2.0);
782 rotationMatrix[3] = 0.0f;
783 rotationMatrix[4] = 0.0f;
784 rotationMatrix[5] = (TCFloat)(sin(M_PI / 3.0));
785 rotationMatrix[6] = 0.5f;
786 rotationMatrix[7] = 0.0f;
787 rotationMatrix[8] = (TCFloat)(sqrt(2.0) / 2.0);
788 rotationMatrix[9] = (TCFloat)(-sqrt(2.0) / 4.0);
789 rotationMatrix[10] = (TCFloat)(sqrt(1.5) / 2.0);
790 rotationMatrix[11] = 0.0f;
791 rotationMatrix[12] = 0.0f;
792 rotationMatrix[13] = 0.0f;
793 rotationMatrix[14] = 0.0f;
794 rotationMatrix[15] = 1.0f;
795 examineLatitude = 30.0f;
796 examineLongitude = 45.0f;
797 }
798
setupFrontViewAngle(void)799 void LDrawModelViewer::setupFrontViewAngle(void)
800 {
801 rotationMatrix[0] = 1.0f;
802 rotationMatrix[1] = 0.0f;
803 rotationMatrix[2] = 0.0f;
804 rotationMatrix[3] = 0.0f;
805
806 rotationMatrix[4] = 0.0f;
807 rotationMatrix[5] = 1.0f;
808 rotationMatrix[6] = 0.0f;
809 rotationMatrix[7] = 0.0f;
810
811 rotationMatrix[8] = 0.0f;
812 rotationMatrix[9] = 0.0f;
813 rotationMatrix[10] = 1.0f;
814 rotationMatrix[11] = 0.0f;
815
816 rotationMatrix[12] = 0.0f;
817 rotationMatrix[13] = 0.0f;
818 rotationMatrix[14] = 0.0f;
819 rotationMatrix[15] = 1.0f;
820 examineLatitude = 0.0f;
821 examineLongitude = 0.0f;
822 }
823
setupBackViewAngle(void)824 void LDrawModelViewer::setupBackViewAngle(void)
825 {
826 rotationMatrix[0] = -1.0f;
827 rotationMatrix[1] = 0.0f;
828 rotationMatrix[2] = 0.0f;
829 rotationMatrix[3] = 0.0f;
830
831 rotationMatrix[4] = 0.0f;
832 rotationMatrix[5] = 1.0f;
833 rotationMatrix[6] = 0.0f;
834 rotationMatrix[7] = 0.0f;
835
836 rotationMatrix[8] = 0.0f;
837 rotationMatrix[9] = 0.0f;
838 rotationMatrix[10] = -1.0f;
839 rotationMatrix[11] = 0.0f;
840
841 rotationMatrix[12] = 0.0f;
842 rotationMatrix[13] = 0.0f;
843 rotationMatrix[14] = 0.0f;
844 rotationMatrix[15] = 1.0f;
845 examineLatitude = 0.0f;
846 examineLongitude = 180.0f;
847 }
848
setupLeftViewAngle(void)849 void LDrawModelViewer::setupLeftViewAngle(void)
850 {
851 rotationMatrix[0] = 0.0f;
852 rotationMatrix[1] = 0.0f;
853 rotationMatrix[2] = -1.0f;
854 rotationMatrix[3] = 0.0f;
855
856 rotationMatrix[4] = 0.0f;
857 rotationMatrix[5] = 1.0f;
858 rotationMatrix[6] = 0.0f;
859 rotationMatrix[7] = 0.0f;
860
861 rotationMatrix[8] = 1.0f;
862 rotationMatrix[9] = 0.0f;
863 rotationMatrix[10] = 0.0f;
864 rotationMatrix[11] = 0.0f;
865
866 rotationMatrix[12] = 0.0f;
867 rotationMatrix[13] = 0.0f;
868 rotationMatrix[14] = 0.0f;
869 rotationMatrix[15] = 1.0f;
870 examineLatitude = 0.0f;
871 examineLongitude = 90.0f;
872 }
873
setupRightViewAngle(void)874 void LDrawModelViewer::setupRightViewAngle(void)
875 {
876 rotationMatrix[0] = 0.0f;
877 rotationMatrix[1] = 0.0f;
878 rotationMatrix[2] = 1.0f;
879 rotationMatrix[3] = 0.0f;
880
881 rotationMatrix[4] = 0.0f;
882 rotationMatrix[5] = 1.0f;
883 rotationMatrix[6] = 0.0f;
884 rotationMatrix[7] = 0.0f;
885
886 rotationMatrix[8] = -1.0f;
887 rotationMatrix[9] = 0.0f;
888 rotationMatrix[10] = 0.0f;
889 rotationMatrix[11] = 0.0f;
890
891 rotationMatrix[12] = 0.0f;
892 rotationMatrix[13] = 0.0f;
893 rotationMatrix[14] = 0.0f;
894 rotationMatrix[15] = 1.0f;
895 examineLatitude = 0.0f;
896 examineLongitude = -90.0f;
897 }
898
setupTopViewAngle(void)899 void LDrawModelViewer::setupTopViewAngle(void)
900 {
901 rotationMatrix[0] = 1.0f;
902 rotationMatrix[1] = 0.0f;
903 rotationMatrix[2] = 0.0f;
904 rotationMatrix[3] = 0.0f;
905
906 rotationMatrix[4] = 0.0f;
907 rotationMatrix[5] = 0.0f;
908 rotationMatrix[6] = 1.0f;
909 rotationMatrix[7] = 0.0f;
910
911 rotationMatrix[8] = 0.0f;
912 rotationMatrix[9] = -1.0f;
913 rotationMatrix[10] = 0.0f;
914 rotationMatrix[11] = 0.0f;
915
916 rotationMatrix[12] = 0.0f;
917 rotationMatrix[13] = 0.0f;
918 rotationMatrix[14] = 0.0f;
919 rotationMatrix[15] = 1.0f;
920 examineLatitude = 90.0f;
921 examineLongitude = 0.0f;
922 }
923
setupBottomViewAngle(void)924 void LDrawModelViewer::setupBottomViewAngle(void)
925 {
926 rotationMatrix[0] = 1.0f;
927 rotationMatrix[1] = 0.0f;
928 rotationMatrix[2] = 0.0f;
929 rotationMatrix[3] = 0.0f;
930
931 rotationMatrix[4] = 0.0f;
932 rotationMatrix[5] = 0.0f;
933 rotationMatrix[6] = -1.0f;
934 rotationMatrix[7] = 0.0f;
935
936 rotationMatrix[8] = 0.0f;
937 rotationMatrix[9] = 1.0f;
938 rotationMatrix[10] = 0.0f;
939 rotationMatrix[11] = 0.0f;
940
941 rotationMatrix[12] = 0.0f;
942 rotationMatrix[13] = 0.0f;
943 rotationMatrix[14] = 0.0f;
944 rotationMatrix[15] = 1.0f;
945 examineLatitude = -90.0f;
946 examineLongitude = 0.0f;
947 }
948
949 /*
950 void LDrawModelViewer::ldlErrorCallback(LDLError *error)
951 {
952 // static int errorCount = 0;
953
954 if (error)
955 {
956 TCStringArray *extraInfo = error->getExtraInfo();
957
958 if (getDebugLevel() > 0)
959 {
960 printf("Error on line %d in: %s\n", error->getLineNumber(),
961 error->getFilename());
962 indentPrintf(4, "%s\n", error->getMessage());
963 indentPrintf(4, "%s\n", error->getFileLine());
964 }
965 if (extraInfo)
966 {
967 int i;
968 int count = extraInfo->getCount();
969
970 for (i = 0; i < count; i++)
971 {
972 indentPrintf(4, "%s\n", (*extraInfo)[i]);
973 }
974 }
975 }
976 }
977
978 void LDrawModelViewer::progressAlertCallback(TCProgressAlert *alert)
979 {
980 if (alert)
981 {
982 printf("Progress message from %s:\n%s (%f)\n", alert->getSource(),
983 alert->getMessage(), alert->getProgress());
984 }
985 }
986 */
987
haveLightDats(void) const988 bool LDrawModelViewer::haveLightDats(void) const
989 {
990 return flags.drawLightDats && mainTREModel &&
991 mainTREModel->getLightLocations().size() > 0;
992 }
993
haveStandardLight(void)994 bool LDrawModelViewer::haveStandardLight(void)
995 {
996 return flags.useLighting && (!haveLightDats() || !flags.optionalStandardLight);
997 }
998
forceOneLight(void) const999 bool LDrawModelViewer::forceOneLight(void) const
1000 {
1001 return flags.oneLight || flags.usesSpecular || !flags.defaultLightVector ||
1002 (flags.bfc && (flags.redBackFaces | flags.greenFrontFaces |
1003 flags.blueNeutralFaces)) || haveLightDats();
1004 }
1005
loadModel(bool resetViewpoint)1006 int LDrawModelViewer::loadModel(bool resetViewpoint)
1007 {
1008 int retValue = 0;
1009
1010 debugLog1s(LOGFILE_KEY, "Load Started for %s.\n", filename);
1011 TCAlertManager::sendAlert(loadAlertClass(), this, _UC("ModelLoading"));
1012 if (filename && filename[0])
1013 {
1014 if (clipAmount != 0.0f && resetViewpoint)
1015 {
1016 clipAmount = 0.0f;
1017 perspectiveView();
1018 }
1019 if (loadLDLModel() && parseModel())
1020 {
1021 retValue = 1;
1022 if (resetViewpoint)
1023 {
1024 resetView();
1025 }
1026 }
1027 }
1028 // This shouldn't be necessary, but something is occasionally setting the
1029 // needsReload flag during loading.
1030 flags.needsReload = false;
1031 flags.needsLightingSetup = true;
1032 if (retValue)
1033 {
1034 debugLog1s(LOGFILE_KEY, "Successfully loaded %s.\n", filename);
1035 TCAlertManager::sendAlert(loadAlertClass(), this, _UC("ModelLoaded"));
1036 }
1037 else
1038 {
1039 TCObject::release(mainModel);
1040 mainModel = NULL;
1041 releaseTREModels();
1042 step = -1;
1043 TCAlertManager::sendAlert(loadAlertClass(), this,
1044 _UC("ModelLoadCanceled"));
1045 requestRedraw();
1046 debugLog1s(LOGFILE_KEY, "Failed to load %s.\n", filename);
1047 }
1048 return retValue;
1049 }
1050
releaseTREModels(void)1051 void LDrawModelViewer::releaseTREModels(void)
1052 {
1053 TCObject::release(mainTREModel);
1054 mainTREModel = NULL;
1055 TCObject::release(whiteLightDirModel);
1056 whiteLightDirModel = NULL;
1057 TCObject::release(blueLightDirModel);
1058 blueLightDirModel = NULL;
1059 TCObject::release(highlightModel);
1060 highlightModel = NULL;
1061 }
1062
calcSize(void)1063 bool LDrawModelViewer::calcSize(void)
1064 {
1065 bool abort = false;
1066 LDLModel *curModel = getCurModel();
1067
1068 TCProgressAlert::send("LDrawModelViewer", ls(_UC("CalculatingSizeStatus")),
1069 0.0f, &abort, this);
1070 if (!abort)
1071 {
1072 curModel->getBoundingBox(boundingMin, boundingMax);
1073 TCProgressAlert::send("LDrawModelViewer",
1074 ls(_UC("CalculatingSizeStatus")), 0.5f, &abort, this);
1075 }
1076 if (!abort)
1077 {
1078 if (!flags.overrideModelCenter)
1079 {
1080 if (!getRotationCenter(center))
1081 {
1082 center = (boundingMin + boundingMax) / 2.0f;
1083 }
1084 }
1085 if (!flags.overrideModelSize)
1086 {
1087 size = curModel->getMaxRadius(center, true) * 2.0f;
1088 if (mainModel->getBBoxIgnoreUsed())
1089 {
1090 clipSize = curModel->getMaxRadius(center, false) * 2.0f;
1091 if (size == 0.0)
1092 {
1093 size = clipSize;
1094 }
1095 }
1096 else
1097 {
1098 clipSize = size;
1099 }
1100 }
1101 TCProgressAlert::send("LDrawModelViewer",
1102 ls(_UC("CalculatingSizeStatus")), 1.0f, &abort, this);
1103 flags.needsCalcSize = false;
1104 }
1105 return !abort;
1106 }
1107
loadLDLModel(void)1108 bool LDrawModelViewer::loadLDLModel(void)
1109 {
1110 TCObject::release(mainModel);
1111 mainModel = new LDLMainModel;
1112 mainModel->setAlertSender(this);
1113
1114 // First, release the current TREModels, if they exist.
1115 releaseTREModels();
1116 mainModel->setLDConfig(m_ldConfig);
1117 mainModel->setLowResStuds(!flags.qualityStuds);
1118 mainModel->setGreenFrontFaces(flags.bfc && flags.greenFrontFaces);
1119 mainModel->setRedBackFaces(flags.bfc && flags.redBackFaces);
1120 mainModel->setBlueNeutralFaces(flags.bfc && flags.blueNeutralFaces);
1121 mainModel->setBlackEdgeLines(flags.blackHighlights);
1122 mainModel->setExtraSearchDirs(extraSearchDirs);
1123 mainModel->setProcessLDConfig(flags.processLDConfig);
1124 mainModel->setSkipValidation(flags.skipValidation);
1125 mainModel->setBoundingBoxesOnly(flags.boundingBoxesOnly);
1126 mainModel->setSeamWidth(seamWidth);
1127 mainModel->setCheckPartTracker(flags.checkPartTracker);
1128 mainModel->setTexmaps(flags.texmaps);
1129 if (flags.needsResetMpd)
1130 {
1131 mpdChildIndex = 0;
1132 flags.needsResetMpd = false;
1133 }
1134 #ifdef TIME_MODEL_LOAD
1135 auto start = std::chrono::high_resolution_clock::now();
1136 #endif // TIME_MODEL_LOAD
1137 if (mainModel->load(filename))
1138 {
1139 #ifdef TIME_MODEL_LOAD
1140 auto end = std::chrono::high_resolution_clock::now();
1141 std::chrono::duration<double> elapsed_seconds = end-start;
1142 std::cout << "\n\nModel load of " << filename << " took " <<
1143 elapsed_seconds.count() << "s\n\n\n";
1144 #endif // TIME_MODEL_LOAD
1145 return calcSize();
1146 }
1147 else
1148 {
1149 return false;
1150 }
1151 }
1152
parseModel(void)1153 bool LDrawModelViewer::parseModel(void)
1154 {
1155 LDModelParser *modelParser = NULL;
1156 bool retValue = false;
1157 LDLModel *model = NULL;
1158
1159 if (!mainModel)
1160 {
1161 return false;
1162 }
1163 if (flags.randomColors)
1164 {
1165 // Even though they're "random", make them the same each time the model
1166 // is loaded.
1167 srand((unsigned)time(NULL));
1168 }
1169 // Note: this is the only mainModel setting that's applied here in
1170 // parseModel, but it needs to be here to make it so that a reload isn't
1171 // required when you change the setting. The setting doesn't affect the
1172 // loading of the model at all, just how it reports colors while parsing.
1173 mainModel->setRandomColors(flags.randomColors);
1174 TCAlertManager::sendAlert(loadAlertClass(), this, _UC("ModelParsing"));
1175 releaseTREModels();
1176 if (flags.needsCalcSize && !calcSize())
1177 {
1178 return false;
1179 }
1180 modelParser = new LDModelParser(this);
1181 modelParser->setAlertSender(this);
1182 modelParser->setTexmapsFlag(getTexmaps());
1183 model = getCurModel();
1184 if (modelParser->parseMainModel(model))
1185 {
1186 mainTREModel = modelParser->getMainTREModel();
1187 mainTREModel->setTexturesAfterTransparentFlag(getTexturesAfterTransparent());
1188 mainTREModel->setTextureOffsetFactor(getTextureOffsetFactor());
1189 mainTREModel->retain();
1190 flags.needsRecompile = false;
1191 flags.needsReparse = false;
1192 retValue = true;
1193 initLightDirModels();
1194 TCProgressAlert::send("LDrawModelViewer", ls(_UC("Done")), 2.0f, this);
1195 if (commandLineStep > 0)
1196 {
1197 setStep(commandLineStep);
1198 flags.needsResetStep = false;
1199 commandLineStep = -1;
1200 }
1201 if (flags.needsResetStep)
1202 {
1203 setStep(getNumSteps());
1204 flags.needsResetStep = false;
1205 }
1206 else
1207 {
1208 mainTREModel->setStep(step);
1209 }
1210 }
1211 modelParser->release();
1212 highlightPathsChanged();
1213 if (retValue)
1214 {
1215 TCAlertManager::sendAlert(loadAlertClass(), this, _UC("ModelParsed"));
1216 }
1217 else
1218 {
1219 TCAlertManager::sendAlert(loadAlertClass(), this,
1220 _UC("ModelParseCanceled"));
1221 }
1222 return retValue;
1223 }
1224
setShowsHighlightLines(bool value)1225 void LDrawModelViewer::setShowsHighlightLines(bool value)
1226 {
1227 if (value != flags.showsHighlightLines)
1228 {
1229 flags.showsHighlightLines = value;
1230 flags.needsReparse = true;
1231 }
1232 }
1233
setDrawConditionalHighlights(bool value)1234 void LDrawModelViewer::setDrawConditionalHighlights(bool value)
1235 {
1236 if (flags.drawConditionalHighlights != value)
1237 {
1238 flags.drawConditionalHighlights = value;
1239 if (flags.showsHighlightLines)
1240 {
1241 flags.needsReparse = true;
1242 }
1243 }
1244 }
1245
setPerformSmoothing(bool value)1246 void LDrawModelViewer::setPerformSmoothing(bool value)
1247 {
1248 if (flags.performSmoothing != value)
1249 {
1250 flags.performSmoothing = value;
1251 flags.needsReparse = true;
1252 }
1253 }
1254
setForceZoomToFit(bool value)1255 void LDrawModelViewer::setForceZoomToFit(bool value)
1256 {
1257 if (flags.forceZoomToFit != value)
1258 {
1259 flags.forceZoomToFit = value;
1260 }
1261 }
1262
setSaveAlpha(bool value)1263 void LDrawModelViewer::setSaveAlpha(bool value)
1264 {
1265 if (flags.saveAlpha != value)
1266 {
1267 flags.saveAlpha = value;
1268 }
1269 }
1270
setSkipValidation(bool value)1271 void LDrawModelViewer::setSkipValidation(bool value)
1272 {
1273 if (flags.skipValidation != value)
1274 {
1275 flags.skipValidation = value;
1276 if (!value)
1277 {
1278 // If they turn the flag on, there's no point reloading. If, on the
1279 // other hand, they turn the flag off, we need to reload so that we
1280 // can work around any problems the model might have.
1281 flags.needsReload = true;
1282 }
1283 }
1284 }
1285
setProcessLDConfig(bool value)1286 void LDrawModelViewer::setProcessLDConfig(bool value)
1287 {
1288 if (flags.processLDConfig != value)
1289 {
1290 flags.processLDConfig = value;
1291 flags.needsReload = true;
1292 }
1293 }
1294
setMemoryUsage(int value)1295 void LDrawModelViewer::setMemoryUsage(int value)
1296 {
1297 if (value < 0 || value > 2)
1298 {
1299 value = 2;
1300 }
1301 if (value != memoryUsage)
1302 {
1303 memoryUsage = value;
1304 flags.needsReparse = true;
1305 }
1306 }
1307
setLineSmoothing(bool value)1308 void LDrawModelViewer::setLineSmoothing(bool value)
1309 {
1310 if (flags.lineSmoothing != value)
1311 {
1312 flags.lineSmoothing = value;
1313 flags.needsReparse = true;
1314 }
1315 }
1316
setSubduedLighting(bool value)1317 void LDrawModelViewer::setSubduedLighting(bool value)
1318 {
1319 if (value != flags.subduedLighting)
1320 {
1321 flags.subduedLighting = value;
1322 flags.needsLightingSetup = true;
1323 }
1324 }
1325
recompile(void)1326 bool LDrawModelViewer::recompile(void)
1327 {
1328 if (mainTREModel)
1329 {
1330 if (whiteLightDirModel != NULL)
1331 {
1332 // It crashes after an FSAA change if we simply recompile here.
1333 // I suspect it's related to the VBO/VAR extensions. Deleting the
1334 // lightDirModel objects and recreating them causes the vertex
1335 // buffers to be reloaded from scratch.
1336 whiteLightDirModel->release();
1337 whiteLightDirModel = NULL;
1338 blueLightDirModel->release();
1339 blueLightDirModel = NULL;
1340 initLightDirModels();
1341 }
1342 if (highlightModel != NULL)
1343 {
1344 // See comment above about crash.
1345 highlightModel->release();
1346 highlightModel = NULL;
1347 highlightPathsChanged();
1348 }
1349 mainTREModel->recompile();
1350 flags.needsRecompile = false;
1351 TCProgressAlert::send("LDrawModelViewer", ls(_UC("Done")), 2.0f, this);
1352 }
1353 return true;
1354 }
1355
uncompile(void)1356 void LDrawModelViewer::uncompile(void)
1357 {
1358 if (fontListBase)
1359 {
1360 glDeleteLists(fontListBase, 128);
1361 fontListBase = 0;
1362 }
1363 if (mainTREModel)
1364 {
1365 mainTREModel->uncompile();
1366 }
1367 if (whiteLightDirModel)
1368 {
1369 whiteLightDirModel->uncompile();
1370 }
1371 if (blueLightDirModel)
1372 {
1373 blueLightDirModel->uncompile();
1374 }
1375 if (highlightModel)
1376 {
1377 highlightModel->uncompile();
1378 }
1379 }
1380
reparse(void)1381 void LDrawModelViewer::reparse(void)
1382 {
1383 if (mainModel)
1384 {
1385 parseModel();
1386 }
1387 flags.needsReparse = false;
1388 }
1389
reload(void)1390 void LDrawModelViewer::reload(void)
1391 {
1392 if (filename)
1393 {
1394 bool lastStep = step == getNumSteps() - 1;
1395
1396 loadModel(false);
1397 if (lastStep)
1398 {
1399 setStep(getNumSteps());
1400 }
1401 }
1402 flags.needsReload = false;
1403 }
1404
setObi(bool value)1405 void LDrawModelViewer::setObi(bool value)
1406 {
1407 if (value != flags.obi)
1408 {
1409 flags.obi = value;
1410 flags.needsReparse = true;
1411 }
1412 }
1413
setGl2ps(bool value)1414 void LDrawModelViewer::setGl2ps(bool value)
1415 {
1416 if (value != flags.gl2ps)
1417 {
1418 flags.gl2ps = value;
1419 if (mainTREModel)
1420 {
1421 mainTREModel->setGl2psFlag(value);
1422 }
1423 }
1424 }
1425
setUsesSpecular(bool value)1426 void LDrawModelViewer::setUsesSpecular(bool value)
1427 {
1428 if (value != flags.usesSpecular)
1429 {
1430 flags.usesSpecular = value;
1431 flags.needsMaterialSetup = true;
1432 flags.needsLightingSetup = true;
1433 }
1434 }
1435
setOneLight(bool value)1436 void LDrawModelViewer::setOneLight(bool value)
1437 {
1438 if (value != flags.oneLight)
1439 {
1440 flags.oneLight = value;
1441 flags.needsLightingSetup = true;
1442 }
1443 }
1444
setupMaterial(void)1445 void LDrawModelViewer::setupMaterial(void)
1446 {
1447 // GLfloat mAmbient[] = {0.5f, 0.5f, 0.5f, 1.0f};
1448 GLfloat mAmbient[] = {0.0f, 0.0f, 0.0f, 1.0f};
1449 // GLfloat mSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f};
1450 GLfloat mSpecular[] = {0.5f, 0.5f, 0.5f, 1.0f};
1451 // GLfloat mSpecular[] = {0.0f, 0.0f, 0.0f, 1.0f};
1452
1453 // Note: default emission is <0,0,0,1>, which is what we want.
1454 if (!flags.usesSpecular)
1455 {
1456 mSpecular[0] = mSpecular[1] = mSpecular[2] = 0.0f;
1457 }
1458 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mAmbient);
1459 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mSpecular);
1460 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 64.0f);
1461 glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
1462 glEnable(GL_COLOR_MATERIAL);
1463 flags.needsMaterialSetup = false;
1464 }
1465
setupLight(GLenum light,const TCVector & color)1466 void LDrawModelViewer::setupLight(GLenum light, const TCVector &color)
1467 {
1468 GLfloat lDiffuse[4];
1469 GLfloat lSpecular[4];
1470 int i;
1471
1472 if (flags.subduedLighting)
1473 {
1474 for (i = 0; i < 3; i++)
1475 {
1476 lDiffuse[i] = color.get(i) * 0.5f;
1477 }
1478 }
1479 else
1480 {
1481 for (i = 0; i < 3; i++)
1482 {
1483 lDiffuse[i] = color.get(i);
1484 }
1485 }
1486 if (!flags.usesSpecular)
1487 {
1488 lSpecular[0] = lSpecular[1] = lSpecular[2] = 0.0f;
1489 }
1490 else
1491 {
1492 for (i = 0; i < 3; i++)
1493 {
1494 lSpecular[i] = color.get(i);
1495 }
1496 }
1497 lDiffuse[3] = 1.0f;
1498 lSpecular[3] = 1.0f;
1499 glLightfv(light, GL_SPECULAR, lSpecular);
1500 glLightfv(light, GL_DIFFUSE, lDiffuse);
1501 glEnable(light);
1502 }
1503
setupLighting(void)1504 void LDrawModelViewer::setupLighting(void)
1505 {
1506 glDisable(GL_NORMALIZE);
1507 if (flags.useLighting)
1508 {
1509 GLint maxLights;
1510 int i;
1511 bool lightDats = haveLightDats();
1512 GLfloat ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
1513
1514 if (flags.subduedLighting)
1515 {
1516 ambient[0] = ambient[1] = ambient[2] = 0.7f;
1517 }
1518 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
1519 glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
1520 glEnable(GL_LIGHTING);
1521 glGetIntegerv(GL_MAX_LIGHTS, &maxLights);
1522 for (i = 0; i < maxLights; i++)
1523 {
1524 glDisable(GL_LIGHT0 + i);
1525 }
1526 if (!lightDats || !flags.optionalStandardLight)
1527 {
1528 setupLight(GL_LIGHT0);
1529 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f);
1530 glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f);
1531 glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f);
1532 }
1533 if (lightDats)
1534 {
1535 const TCULongList &lightColors = mainTREModel->getLightColors();
1536 TCULongList::const_iterator itColor = lightColors.begin();
1537 float lightScale = 1.0f / 255.0f;
1538 float atten = (float)sqr(size);
1539 int start = 0;
1540
1541 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
1542 if (!flags.optionalStandardLight)
1543 {
1544 start = 1;
1545 }
1546 for (i = start; i < maxLights && itColor != lightColors.end(); i++)
1547 {
1548 TCByte rgb[4];
1549 TCULong color = htonl(*itColor);
1550 float minBrightness = 0.5f;
1551 float brightness;
1552
1553 memcpy(rgb, &color, 4);
1554 brightness = std::max(std::max(rgb[0] * lightScale,
1555 rgb[1] * lightScale), rgb[2] * lightScale) * (1.0f - minBrightness) +
1556 minBrightness;
1557 setupLight(GL_LIGHT0 + i, TCVector(rgb[0] * lightScale,
1558 rgb[1] * lightScale, rgb[2] * lightScale));
1559 glLightf(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, 0.5f);
1560 glLightf(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, 0.0f);//1.0f / size);
1561 glLightf(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, 2.0f / atten /
1562 brightness);
1563 itColor++;
1564 }
1565 }
1566 else
1567 {
1568 if (forceOneLight())
1569 {
1570 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
1571 }
1572 else
1573 {
1574 setupLight(GL_LIGHT1);
1575 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
1576 }
1577 }
1578 flags.needsLightingSetup = false;
1579 }
1580 else
1581 {
1582 glDisable(GL_LIGHTING);
1583 flags.needsLightingSetup = false;
1584 }
1585 }
1586
setFontData(TCByte * fontData,long length)1587 void LDrawModelViewer::setFontData(TCByte *fontData, long length)
1588 {
1589 if (length == 4096)
1590 {
1591 int i, j;
1592 TCByte *imageData = NULL;
1593 int rowSize;
1594 int imageSize;
1595
1596 fontImage1x = new TCImage;
1597 fontImage1x->setFlipped(true);
1598 fontImage1x->setLineAlignment(4);
1599 fontImage1x->setDataFormat(TCRgba8);
1600 fontImage1x->setSize(FONT_IMAGE_WIDTH, FONT_IMAGE_HEIGHT);
1601 fontImage1x->allocateImageData();
1602 imageData = fontImage1x->getImageData();
1603 rowSize = fontImage1x->getRowSize();
1604 imageSize = rowSize * FONT_IMAGE_HEIGHT;
1605 for (i = 0; i < imageSize; i++)
1606 {
1607 if (i % 4 == 3)
1608 {
1609 imageData[i] = 0x00; // Init alpha channel to clear.
1610 }
1611 else
1612 {
1613 imageData[i] = 0xFF; // Init color channels to white.
1614 }
1615 }
1616 for (i = 0; i < 4096; i++)
1617 {
1618 int row = i / 256 * 16 + i % 16; // logical row (kinda)
1619 int col = i / 16 % 16; // logical column
1620 int yOffset = (FONT_IMAGE_HEIGHT - row - 1) * rowSize;
1621 TCByte fontByte = fontData[i];
1622
1623 for (j = 0; j < 8; j++)
1624 {
1625 if (fontByte & (1 << (7 - j)))
1626 {
1627 int offset = yOffset + (col * FONT_CHAR_WIDTH + j) * 4 + 3;
1628
1629 // That spot should be on, so set the bit.
1630 if (offset < 131072)
1631 {
1632 imageData[offset] = 0xFF;
1633 }
1634 }
1635 }
1636 }
1637 }
1638 }
1639
1640 // Loads a font file in the format of VGA text-mode font data.
loadVGAFont(const char * fontFilename)1641 void LDrawModelViewer::loadVGAFont(const char *fontFilename)
1642 {
1643 if (fontImage1x == NULL)
1644 {
1645 FILE *fontFile = ucfopen(fontFilename, "rb");
1646
1647 if (fontFile)
1648 {
1649 TCByte fontData[4096];
1650
1651 if (fread(fontData, 1, 4096, fontFile) == 4096)
1652 {
1653 setFontData(fontData, 4096);
1654 }
1655 fclose(fontFile);
1656 }
1657 }
1658 }
1659
setFont2x(TCImage * image)1660 void LDrawModelViewer::setFont2x(TCImage *image)
1661 {
1662 if (fontImage2x == NULL)
1663 {
1664 fontImage2x = TCObject::retain(image);
1665 }
1666 }
1667
setRawFont2xData(const TCByte * data,long length)1668 void LDrawModelViewer::setRawFont2xData(const TCByte *data, long length)
1669 {
1670 if (fontImage2x == NULL)
1671 {
1672 int rowSize;
1673 const int fontWidth = 256;
1674 const int fontHeight = 512;
1675
1676 fontImage2x = new TCImage;
1677 fontImage2x->setFlipped(true);
1678 fontImage2x->setLineAlignment(4);
1679 fontImage2x->setDataFormat(TCRgba8);
1680 fontImage2x->setSize(fontWidth, fontHeight);
1681 fontImage2x->allocateImageData();
1682 rowSize = fontImage2x->getRowSize();
1683 if (length == rowSize * fontHeight)
1684 {
1685 TCByte *imageData = fontImage2x->getImageData();
1686 int i;
1687
1688 for (i = 0; i < fontHeight; ++i)
1689 {
1690 memcpy(imageData + rowSize * (fontHeight - 1 - i),
1691 data + rowSize * i, rowSize);
1692 }
1693 }
1694 else
1695 {
1696 fontImage2x->release();
1697 fontImage2x = NULL;
1698 }
1699 }
1700 }
1701
setupFont2x(const char * fontFilename)1702 void LDrawModelViewer::setupFont2x(const char *fontFilename)
1703 {
1704 if (fontImage2x == NULL)
1705 {
1706 fontImage2x = new TCImage;
1707 fontImage2x->setFlipped(true);
1708 fontImage2x->setLineAlignment(4);
1709 if (!fontImage2x->loadFile(fontFilename))
1710 {
1711 fontImage2x->release();
1712 }
1713 }
1714 setupFont(NULL);
1715 }
1716
setupFont(const char * fontFilename)1717 void LDrawModelViewer::setupFont(const char *fontFilename)
1718 {
1719 // printf("LDrawModelViewer::setupFont\n");
1720 if (fontFilename)
1721 {
1722 loadVGAFont(fontFilename);
1723 }
1724 TCImage *fontImage = fontImage1x;
1725 TCFloat fontScale = scaleFactor;
1726 if (scaleFactor >= 1.5 && fontImage2x != NULL)
1727 {
1728 fontImage = fontImage2x;
1729 fontScale = scaleFactor / 2.0;
1730 }
1731 if (fontImage != NULL)
1732 {
1733 int i;
1734 int fontImageWidth = fontImage->getWidth();
1735 int fontImageHeight = fontImage->getHeight();
1736 int fontCharWidth = fontImageWidth / 16;
1737 int fontCharHeight = fontImageHeight / 16;
1738
1739 glGenTextures(1, &fontTextureID);
1740 glBindTexture(GL_TEXTURE_2D, fontTextureID);
1741 glTexImage2D(GL_TEXTURE_2D, 0, 4, fontImageWidth, fontImageHeight,
1742 0, GL_RGBA, GL_UNSIGNED_BYTE, fontImage->getImageData());
1743 if (fontListBase)
1744 {
1745 glDeleteLists(fontListBase, FONT_NUM_CHARACTERS);
1746 }
1747 fontListBase = glGenLists(FONT_NUM_CHARACTERS);
1748 for (i = 0; i < FONT_NUM_CHARACTERS; i++)
1749 {
1750 TCFloat cx, cy;
1751 TCFloat wx, hy;
1752 TCFloat tx, ty;
1753
1754 cx = (TCFloat)(i % 16) * fontCharWidth /
1755 (TCFloat)(fontImageWidth);
1756 cy = (TCFloat)(i / 16) * fontCharHeight /
1757 (TCFloat)(fontImageHeight);
1758 wx = (TCFloat)fontCharWidth / fontImageWidth;
1759 hy = (TCFloat)fontCharHeight / fontImageHeight;
1760 glNewList(fontListBase + i, GL_COMPILE);
1761 glBegin(GL_QUADS);
1762 tx = cx;
1763 ty = 1.0f - cy - hy;
1764 treGlTexCoord2f(tx, ty); // Bottom Left
1765 glVertex2i(0, 0);
1766 tx = cx + wx;
1767 ty = 1.0f - cy - hy;
1768 treGlTexCoord2f(tx, ty); // Bottom Right
1769 glVertex2i(fontCharWidth * fontScale, 0);
1770 tx = cx + wx;
1771 ty = 1 - cy;
1772 treGlTexCoord2f(tx, ty); // Top Right
1773 glVertex2i(fontCharWidth * fontScale,
1774 fontCharHeight * fontScale);
1775 tx = cx;
1776 ty = 1 - cy;
1777 treGlTexCoord2f(tx , ty); // Top Left
1778 glVertex2i(0, fontCharHeight * fontScale);
1779 glEnd();
1780 glTranslated((fontCharWidth + 1) * fontScale, 0, 0);
1781 glEndList();
1782 }
1783 }
1784 }
1785
setupTextures(void)1786 void LDrawModelViewer::setupTextures(void)
1787 {
1788 if (programPath)
1789 {
1790 char textureFilename[1024];
1791
1792 sprintf(textureFilename, "%s/StudLogo.png", programPath);
1793 TREMainModel::loadStudTexture(textureFilename);
1794 // sprintf(textureFilename, "%s/Font.png", programPath);
1795 sprintf(textureFilename, "%s/SansSerif.fnt", programPath);
1796 setupFont(textureFilename);
1797 }
1798 else if (fontImage1x != NULL)
1799 {
1800 setupFont(NULL);
1801 }
1802 flags.needsTextureSetup = false;
1803 }
1804
setup(void)1805 void LDrawModelViewer::setup(void)
1806 {
1807 glEnable(GL_DEPTH_TEST);
1808 setupLighting();
1809 setupMaterial();
1810 setupTextures();
1811 flags.needsSetup = false;
1812 }
1813
drawBoundingBox(void)1814 void LDrawModelViewer::drawBoundingBox(void)
1815 {
1816 int lightingEnabled = glIsEnabled(GL_LIGHTING);
1817
1818 if (lightingEnabled)
1819 {
1820 glDisable(GL_LIGHTING);
1821 }
1822 if ((backgroundR + backgroundG + backgroundB) / 3.0 < 0.5)
1823 {
1824 glColor3ub(255, 255, 255);
1825 }
1826 else
1827 {
1828 glColor3ub(0, 0, 0);
1829 }
1830 glBegin(GL_LINE_STRIP);
1831 treGlVertex3fv(boundingMin);
1832 treGlVertex3f(boundingMax[0], boundingMin[1], boundingMin[2]);
1833 treGlVertex3f(boundingMax[0], boundingMax[1], boundingMin[2]);
1834 treGlVertex3f(boundingMin[0], boundingMax[1], boundingMin[2]);
1835 treGlVertex3fv(boundingMin);
1836 treGlVertex3f(boundingMin[0], boundingMin[1], boundingMax[2]);
1837 treGlVertex3f(boundingMax[0], boundingMin[1], boundingMax[2]);
1838 treGlVertex3fv(boundingMax);
1839 treGlVertex3f(boundingMin[0], boundingMax[1], boundingMax[2]);
1840 treGlVertex3f(boundingMin[0], boundingMin[1], boundingMax[2]);
1841 glEnd();
1842 glBegin(GL_LINES);
1843 treGlVertex3f(boundingMin[0], boundingMax[1], boundingMin[2]);
1844 treGlVertex3f(boundingMin[0], boundingMax[1], boundingMax[2]);
1845 treGlVertex3f(boundingMax[0], boundingMax[1], boundingMin[2]);
1846 treGlVertex3f(boundingMax[0], boundingMax[1], boundingMax[2]);
1847 treGlVertex3f(boundingMax[0], boundingMin[1], boundingMin[2]);
1848 treGlVertex3f(boundingMax[0], boundingMin[1], boundingMax[2]);
1849 glEnd();
1850 if (lightingEnabled)
1851 {
1852 glEnable(GL_LIGHTING);
1853 }
1854 }
1855
orthoView(void)1856 void LDrawModelViewer::orthoView(void)
1857 {
1858 int actualWidth = (int)scale(width);
1859 int actualHeight = (int)scale(height);
1860 const char *glVendor = "";
1861 const GLubyte *origVendorString = glGetString(GL_VENDOR);
1862
1863 if (origVendorString)
1864 {
1865 glVendor = (const char *)origVendorString;
1866 }
1867 if (stereoMode == LDVStereoCrossEyed || stereoMode == LDVStereoParallel)
1868 {
1869 glViewport(0, 0, actualWidth, actualHeight);
1870 }
1871 glMatrixMode(GL_PROJECTION);
1872 glLoadIdentity();
1873 #ifdef __clang__
1874 #pragma clang diagnostic push
1875 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1876 #endif
1877 gluOrtho2D(0.0, actualWidth, 0.0, actualHeight);
1878 #ifdef __clang__
1879 #pragma clang diagnostic pop
1880 #endif
1881 glMatrixMode(GL_MODELVIEW);
1882 glLoadIdentity();
1883 if (strncmp(glVendor, "ATI Technologies Inc.", 3) != 0)
1884 {
1885 // This doesn't work right on ATI video cards, so skip.
1886 treGlTranslatef(0.375f, 0.375f, 0.0f);
1887 }
1888 }
1889
setSeamWidth(TCFloat value)1890 void LDrawModelViewer::setSeamWidth(TCFloat value)
1891 {
1892 if (!fEq(value, seamWidth))
1893 {
1894 seamWidth = value;
1895 flags.needsReparse = true;
1896 if (mainModel)
1897 {
1898 mainModel->setSeamWidth(seamWidth);
1899 }
1900 flags.needsCalcSize = true;
1901 }
1902 }
1903
drawString(TCFloat xPos,TCFloat yPos,char * string)1904 void LDrawModelViewer::drawString(TCFloat xPos, TCFloat yPos, char* string)
1905 {
1906 if (!fontListBase)
1907 {
1908 setupTextures();
1909 }
1910 if (!fontListBase)
1911 {
1912 // Font file wasn't found; bail.
1913 return;
1914 }
1915 if ((backgroundR + backgroundG + backgroundB) / 3.0f > 0.5)
1916 {
1917 glColor3ub(0, 0, 0);
1918 }
1919 else
1920 {
1921 glColor3ub(255, 255, 255);
1922 }
1923 orthoView();
1924 treGlTranslatef(xPos * scaleFactor, yPos * scaleFactor, 0);
1925 glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT);
1926 glDisable(GL_DEPTH_TEST);
1927 glEnable(GL_TEXTURE_2D);
1928 enable(GL_BLEND);
1929 blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1930 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1931 glBindTexture(GL_TEXTURE_2D, fontTextureID);
1932 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1933 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1934 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1935 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1936 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1937 glListBase(fontListBase);
1938 glCallLists((GLsizei)strlen(string), GL_UNSIGNED_BYTE, string);
1939 glPopAttrib();
1940 perspectiveView();
1941 }
1942
drawFPS(TCFloat fps)1943 void LDrawModelViewer::drawFPS(TCFloat fps)
1944 {
1945 if (mainTREModel && fps > 0.0f)
1946 {
1947 char fpsString[1024];
1948 int lightingEnabled = glIsEnabled(GL_LIGHTING);
1949 int zBufferEnabled = glIsEnabled(GL_DEPTH_TEST);
1950
1951 if (lightingEnabled)
1952 {
1953 glDisable(GL_LIGHTING);
1954 }
1955 if (zBufferEnabled)
1956 {
1957 glDisable(GL_DEPTH_TEST);
1958 }
1959 sprintf(fpsString, "%4.4f", fps);
1960 drawString(2.0f, 0.0f, fpsString);
1961 if (lightingEnabled)
1962 {
1963 glEnable(GL_LIGHTING);
1964 }
1965 if (zBufferEnabled)
1966 {
1967 glEnable(GL_DEPTH_TEST);
1968 }
1969 }
1970 }
1971
drawLight(GLenum light,TCFloat x,TCFloat y,TCFloat z)1972 void LDrawModelViewer::drawLight(GLenum light, TCFloat x, TCFloat y, TCFloat z)
1973 {
1974 GLfloat position[4];
1975
1976 position[0] = (GLfloat)x;
1977 position[1] = (GLfloat)y;
1978 position[2] = (GLfloat)z;
1979 // 0.0 in the w component of the light "position" vector means that this is
1980 // a directional light. As such, the "position" provided is actually a
1981 // direction (or maybe the inverse of the direction).
1982 position[3] = 0.0f;
1983 glLightfv(light, GL_POSITION, position);
1984 }
1985
drawLights(void)1986 void LDrawModelViewer::drawLights(void)
1987 {
1988 drawLight(GL_LIGHT0, lightVector[0], lightVector[1], lightVector[2]);
1989 if (!forceOneLight())
1990 {
1991 drawLight(GL_LIGHT1, -lightVector[0], -lightVector[1], -lightVector[2]);
1992 }
1993 }
1994
setLightVector(const TCVector & value)1995 void LDrawModelViewer::setLightVector(const TCVector &value)
1996 {
1997 TCFloat length = value.length();
1998 bool oldForce = forceOneLight();
1999
2000 if (length)
2001 {
2002 lightVector = value / length;
2003 }
2004 if (lightVector.approxEquals(TCVector(0.0f, 0.0f, 1.0f), 0.000001f))
2005 {
2006 flags.defaultLightVector = true;
2007 }
2008 else
2009 {
2010 flags.defaultLightVector = false;
2011 }
2012 if (forceOneLight() != oldForce)
2013 {
2014 flags.needsLightingSetup = true;
2015 }
2016 }
2017
setBackgroundRGB(int r,int g,int b)2018 void LDrawModelViewer::setBackgroundRGB(int r, int g, int b)
2019 {
2020 setBackgroundRGBA(r, g, b, 255);
2021 /*
2022 backgroundR = (GLclampf)r / 255.0f;
2023 backgroundG = (GLclampf)g / 255.0f;
2024 backgroundB = (GLclampf)b / 255.0f;
2025 backgroundA = 1.0f;
2026 */
2027 }
2028
setDefaultRGB(TCByte r,TCByte g,TCByte b,bool transparent)2029 void LDrawModelViewer::setDefaultRGB(TCByte r, TCByte g, TCByte b,
2030 bool transparent)
2031 {
2032 if (defaultR != r || defaultG != g || defaultB != b ||
2033 flags.defaultTrans != transparent)
2034 {
2035 defaultR = r;
2036 defaultG = g;
2037 defaultB = b;
2038 flags.defaultTrans = transparent;
2039 if (mainTREModel && defaultColorNumber == -1)
2040 {
2041 flags.needsReparse = true;
2042 }
2043 }
2044 }
2045
getDefaultRGB(TCByte & r,TCByte & g,TCByte & b,bool & transparent) const2046 void LDrawModelViewer::getDefaultRGB(
2047 TCByte &r,
2048 TCByte &g,
2049 TCByte &b,
2050 bool &transparent) const
2051 {
2052 r = defaultR;
2053 g = defaultG;
2054 b = defaultB;
2055 transparent = flags.defaultTrans;
2056 }
2057
setDefaultColorNumber(int value)2058 void LDrawModelViewer::setDefaultColorNumber(int value)
2059 {
2060 if (value != defaultColorNumber)
2061 {
2062 defaultColorNumber = value;
2063 flags.needsReparse = true;
2064 }
2065 }
2066
setBackgroundRGBA(int r,int g,int b,int a)2067 void LDrawModelViewer::setBackgroundRGBA(int r, int g, int b, int a)
2068 {
2069 backgroundR = (GLclampf)r / 255.0f;
2070 backgroundG = (GLclampf)g / 255.0f;
2071 backgroundB = (GLclampf)b / 255.0f;
2072 backgroundA = (GLclampf)a / 255.0f;
2073 }
2074
setRedBackFaces(bool value)2075 void LDrawModelViewer::setRedBackFaces(bool value)
2076 {
2077 if (value != flags.redBackFaces)
2078 {
2079 flags.redBackFaces = value;
2080 if (flags.bfc)
2081 {
2082 flags.needsReload = true;
2083 flags.needsLightingSetup = true;
2084 }
2085 }
2086 }
2087
setGreenFrontFaces(bool value)2088 void LDrawModelViewer::setGreenFrontFaces(bool value)
2089 {
2090 if (value != flags.greenFrontFaces)
2091 {
2092 flags.greenFrontFaces = value;
2093 if (flags.bfc)
2094 {
2095 flags.needsReload = true;
2096 flags.needsLightingSetup = true;
2097 }
2098 }
2099 }
2100
setBlueNeutralFaces(bool value)2101 void LDrawModelViewer::setBlueNeutralFaces(bool value)
2102 {
2103 if (value != flags.blueNeutralFaces)
2104 {
2105 flags.blueNeutralFaces = value;
2106 if (flags.bfc)
2107 {
2108 flags.needsReload = true;
2109 flags.needsLightingSetup = true;
2110 }
2111 }
2112 }
2113
setBfc(bool value)2114 void LDrawModelViewer::setBfc(bool value)
2115 {
2116 if (value != flags.bfc)
2117 {
2118 flags.bfc = value;
2119 flags.needsReparse = true;
2120 if (flags.redBackFaces || flags.greenFrontFaces ||
2121 flags.blueNeutralFaces)
2122 {
2123 flags.needsReload = true;
2124 //flags.needsMaterialSetup = true;
2125 flags.needsLightingSetup = true;
2126 }
2127 }
2128 }
2129
setDrawWireframe(bool value)2130 void LDrawModelViewer::setDrawWireframe(bool value)
2131 {
2132 if (value != flags.drawWireframe)
2133 {
2134 flags.drawWireframe = value;
2135 flags.needsReparse = true;
2136 }
2137 }
2138
setUseWireframeFog(bool value)2139 void LDrawModelViewer::setUseWireframeFog(bool value)
2140 {
2141 flags.useWireframeFog = value;
2142 }
2143
setDrawLightDats(bool value)2144 void LDrawModelViewer::setDrawLightDats(bool value)
2145 {
2146 if (value != flags.drawLightDats)
2147 {
2148 flags.drawLightDats = value;
2149 flags.needsLightingSetup = true;
2150 }
2151 }
2152
setOptionalStandardLight(bool value)2153 void LDrawModelViewer::setOptionalStandardLight(bool value)
2154 {
2155 if (value != flags.optionalStandardLight)
2156 {
2157 flags.optionalStandardLight = value;
2158 flags.needsLightingSetup = true;
2159 }
2160 }
2161
setNoLightGeom(bool value)2162 void LDrawModelViewer::setNoLightGeom(bool value)
2163 {
2164 if (value != flags.noLightGeom)
2165 {
2166 flags.noLightGeom = value;
2167 flags.needsReparse = true;
2168 }
2169 }
2170
setRemoveHiddenLines(bool value)2171 void LDrawModelViewer::setRemoveHiddenLines(bool value)
2172 {
2173 if (value != flags.removeHiddenLines)
2174 {
2175 flags.removeHiddenLines = value;
2176 flags.needsReparse = true;
2177 }
2178 }
2179
setEdgesOnly(bool value)2180 void LDrawModelViewer::setEdgesOnly(bool value)
2181 {
2182 if (value != flags.edgesOnly)
2183 {
2184 flags.edgesOnly = value;
2185 }
2186 if (mainTREModel)
2187 {
2188 bool realValue = flags.edgesOnly && flags.showsHighlightLines;
2189
2190 if (realValue != mainTREModel->getEdgesOnlyFlag())
2191 {
2192 flags.needsReparse = true;
2193 }
2194 }
2195 }
2196
setHiResPrimitives(bool value)2197 void LDrawModelViewer::setHiResPrimitives(bool value)
2198 {
2199 if (value != flags.hiResPrimitives)
2200 {
2201 flags.hiResPrimitives = value;
2202 flags.needsReparse = true;
2203 }
2204 }
2205
setUsePolygonOffset(bool value)2206 void LDrawModelViewer::setUsePolygonOffset(bool value)
2207 {
2208 if (value != flags.usePolygonOffset)
2209 {
2210 flags.usePolygonOffset = value;
2211 if (mainTREModel)
2212 {
2213 mainTREModel->setPolygonOffsetFlag(flags.usePolygonOffset);
2214 if (!flags.usePolygonOffset)
2215 {
2216 disable(GL_POLYGON_OFFSET_FILL);
2217 }
2218 }
2219 }
2220 }
2221
setMultiThreaded(bool value)2222 void LDrawModelViewer::setMultiThreaded(bool value)
2223 {
2224 if (value != flags.multiThreaded)
2225 {
2226 flags.multiThreaded = value;
2227 flags.needsReparse = true;
2228 }
2229
2230 }
2231
setBlackHighlights(bool value)2232 void LDrawModelViewer::setBlackHighlights(bool value)
2233 {
2234 if (value != flags.blackHighlights)
2235 {
2236 flags.blackHighlights = value;
2237 if (flags.showsHighlightLines)
2238 {
2239 flags.needsReload = true;
2240 }
2241 }
2242 }
2243
setShowAllConditionalLines(bool value)2244 void LDrawModelViewer::setShowAllConditionalLines(bool value)
2245 {
2246 if (value != flags.showAllConditionalLines)
2247 {
2248 flags.showAllConditionalLines = value;
2249 flags.needsReparse = true;
2250 }
2251 }
2252
setShowConditionalControlPoints(bool value)2253 void LDrawModelViewer::setShowConditionalControlPoints(bool value)
2254 {
2255 if (value != flags.showConditionalControlPoints)
2256 {
2257 flags.showConditionalControlPoints = value;
2258 flags.needsReparse = true;
2259 }
2260 }
2261
setCurveQuality(int value)2262 void LDrawModelViewer::setCurveQuality(int value)
2263 {
2264 if (value != curveQuality)
2265 {
2266 curveQuality = value;
2267 if (flags.allowPrimitiveSubstitution)
2268 {
2269 flags.needsReparse = true;
2270 }
2271 }
2272 }
2273
setRandomColors(bool value)2274 void LDrawModelViewer::setRandomColors(bool value)
2275 {
2276 if (value != flags.randomColors)
2277 {
2278 flags.randomColors = value;
2279 flags.needsReparse = true;
2280 }
2281 }
2282
setTextureStuds(bool value)2283 void LDrawModelViewer::setTextureStuds(bool value)
2284 {
2285 if (value != flags.textureStuds)
2286 {
2287 flags.textureStuds = value;
2288 // TGLStudLogo::setTextureStuds(value);
2289 flags.needsReparse = true;
2290 }
2291 }
2292
setTextureFilterType(int value)2293 void LDrawModelViewer::setTextureFilterType(int value)
2294 {
2295 if (value != textureFilterType)
2296 {
2297 textureFilterType = value;
2298 if (mainTREModel)
2299 {
2300 mainTREModel->setStudTextureFilter(value);
2301 }
2302 // TGLStudLogo::setTextureFilterType(value);
2303 // flags.needsRecompile = true;
2304 // flags.needsTextureSetup = true;
2305 }
2306 }
2307
setScaleFactor(TCFloat value)2308 void LDrawModelViewer::setScaleFactor(TCFloat value)
2309 {
2310 if (value != scaleFactor)
2311 {
2312 if (fontListBase)
2313 {
2314 glDeleteLists(fontListBase, 128);
2315 fontListBase = 0;
2316 }
2317 scaleFactor = value;
2318 flags.needsResize = true;
2319 if (mainTREModel)
2320 {
2321 mainTREModel->setEdgeLineWidth(getScaledHighlightLineWidth());
2322 }
2323 requestRedraw();
2324 }
2325 }
2326
setWidth(TCFloat value)2327 void LDrawModelViewer::setWidth(TCFloat value)
2328 {
2329 if (value != width)
2330 {
2331 width = value;
2332 flags.needsResize = true;
2333 }
2334 }
2335
setHeight(TCFloat value)2336 void LDrawModelViewer::setHeight(TCFloat value)
2337 {
2338 if (value != height)
2339 {
2340 height = value;
2341 flags.needsResize = true;
2342 }
2343 }
2344
setXTile(int value)2345 void LDrawModelViewer::setXTile(int value)
2346 {
2347 if (value != xTile)
2348 {
2349 xTile = value;
2350 flags.needsResize = true;
2351 }
2352 }
2353
setYTile(int value)2354 void LDrawModelViewer::setYTile(int value)
2355 {
2356 if (value != yTile)
2357 {
2358 yTile = value;
2359 flags.needsResize = true;
2360 }
2361 }
2362
setNumXTiles(int value)2363 void LDrawModelViewer::setNumXTiles(int value)
2364 {
2365 if (value != numXTiles)
2366 {
2367 numXTiles = value;
2368 flags.needsResize = true;
2369 }
2370 }
2371
setNumYTiles(int value)2372 void LDrawModelViewer::setNumYTiles(int value)
2373 {
2374 if (value != numYTiles)
2375 {
2376 numYTiles = value;
2377 flags.needsResize = true;
2378 }
2379 }
2380
setStereoMode(LDVStereoMode mode)2381 void LDrawModelViewer::setStereoMode(LDVStereoMode mode)
2382 {
2383 if (mode != stereoMode)
2384 {
2385 stereoMode = mode;
2386 flags.needsResize = true;
2387 }
2388 }
2389
setCutawayMode(LDVCutawayMode mode)2390 void LDrawModelViewer::setCutawayMode(LDVCutawayMode mode)
2391 {
2392 cutawayMode = mode;
2393 }
2394
getScaledCutawayLineWidth(void) const2395 TCFloat32 LDrawModelViewer::getScaledCutawayLineWidth(void) const
2396 {
2397 return std::max(1.0f, scale(cutawayLineWidth));
2398 }
2399
setCutawayLineWidth(TCFloat32 value)2400 void LDrawModelViewer::setCutawayLineWidth(TCFloat32 value)
2401 {
2402 cutawayLineWidth = value;
2403 }
2404
setCutawayAlpha(TCFloat32 value)2405 void LDrawModelViewer::setCutawayAlpha(TCFloat32 value)
2406 {
2407 cutawayAlpha = value;
2408 }
2409
setUseLighting(bool value)2410 void LDrawModelViewer::setUseLighting(bool value)
2411 {
2412 if (value != flags.useLighting)
2413 {
2414 flags.useLighting = value;
2415 if (mainTREModel)
2416 {
2417 mainTREModel->setLightingFlag(value);
2418 }
2419 flags.needsRecompile = true;
2420 flags.needsLightingSetup = true;
2421 }
2422 }
2423
setUseStipple(bool value)2424 void LDrawModelViewer::setUseStipple(bool value)
2425 {
2426 if (value != flags.useStipple)
2427 {
2428 flags.useStipple = value;
2429 if (mainTREModel)
2430 {
2431 mainTREModel->setStippleFlag(value);
2432 }
2433 if (flags.sortTransparent)
2434 {
2435 flags.needsReparse = true;
2436 }
2437 else
2438 {
2439 // flags.needsRecompile = true;
2440 }
2441 }
2442 }
2443
setSortTransparent(bool value)2444 void LDrawModelViewer::setSortTransparent(bool value)
2445 {
2446 if (value != flags.sortTransparent)
2447 {
2448 flags.sortTransparent = value;
2449 flags.needsReparse = true;
2450 }
2451 }
2452
getScaledHighlightLineWidth(void) const2453 TCFloat32 LDrawModelViewer::getScaledHighlightLineWidth(void) const
2454 {
2455 return std::max(1.0f, scale(highlightLineWidth));
2456 }
2457
setHighlightLineWidth(TCFloat32 value)2458 void LDrawModelViewer::setHighlightLineWidth(TCFloat32 value)
2459 {
2460 if (value != highlightLineWidth)
2461 {
2462 highlightLineWidth = value;
2463 TCFloat32 scaledWidth = getScaledHighlightLineWidth();
2464 if (mainTREModel)
2465 {
2466 mainTREModel->setEdgeLineWidth(scaledWidth);
2467 }
2468 if (highlightModel)
2469 {
2470 highlightModel->setEdgeLineWidth(scaledWidth);
2471 }
2472 }
2473 }
2474
getScaledWireframeLineWidth(void) const2475 TCFloat32 LDrawModelViewer::getScaledWireframeLineWidth(void) const
2476 {
2477 return std::max(1.0f, scale(wireframeLineWidth));
2478 }
2479
setWireframeLineWidth(TCFloat32 value)2480 void LDrawModelViewer::setWireframeLineWidth(TCFloat32 value)
2481 {
2482 wireframeLineWidth = value;
2483 }
2484
setAnisoLevel(TCFloat32 value)2485 void LDrawModelViewer::setAnisoLevel(TCFloat32 value)
2486 {
2487 if (value != anisoLevel)
2488 {
2489 anisoLevel = value;
2490 if (mainTREModel)
2491 {
2492 mainTREModel->setStudAnisoLevel(anisoLevel);
2493 }
2494 }
2495 }
2496
setTexmaps(bool value)2497 void LDrawModelViewer::setTexmaps(bool value)
2498 {
2499 if (value != flags.texmaps)
2500 {
2501 flags.texmaps = value;
2502 flags.needsReload = true;
2503 }
2504 }
2505
setTexturesAfterTransparent(bool value)2506 void LDrawModelViewer::setTexturesAfterTransparent(bool value)
2507 {
2508 if (value != flags.texturesAfterTransparent)
2509 {
2510 flags.texturesAfterTransparent = value;
2511 if (mainTREModel)
2512 {
2513 mainTREModel->setTexturesAfterTransparentFlag(value);
2514 }
2515 requestRedraw();
2516 }
2517 }
2518
setTextureOffsetFactor(TCFloat value)2519 void LDrawModelViewer::setTextureOffsetFactor(TCFloat value)
2520 {
2521 if (value != textureOffsetFactor)
2522 {
2523 textureOffsetFactor = value;
2524 if (mainTREModel)
2525 {
2526 mainTREModel->setTextureOffsetFactor(value);
2527 }
2528 requestRedraw();
2529 }
2530 }
2531
setUseStrips(bool value)2532 void LDrawModelViewer::setUseStrips(bool value)
2533 {
2534 if (value != flags.useStrips)
2535 {
2536 flags.useStrips = value;
2537 flags.needsReload = true;
2538 }
2539 }
2540
setQualityStuds(bool value)2541 void LDrawModelViewer::setQualityStuds(bool value)
2542 {
2543 if (value != flags.qualityStuds)
2544 {
2545 flags.qualityStuds = value;
2546 flags.needsReload = true;
2547 }
2548 }
2549
setAllowPrimitiveSubstitution(bool value)2550 void LDrawModelViewer::setAllowPrimitiveSubstitution(bool value)
2551 {
2552 if (value != flags.allowPrimitiveSubstitution)
2553 {
2554 flags.allowPrimitiveSubstitution = value;
2555 flags.needsReparse = true;
2556 }
2557 }
2558
updateCameraPosition(void)2559 void LDrawModelViewer::updateCameraPosition(void)
2560 {
2561 float multiplier = 100.0f;
2562
2563 #ifdef USE_STD_CHRONO
2564 std::chrono::duration<double> diff = std::chrono::steady_clock::now() -
2565 frameTime;
2566 float factor = diff.count();
2567 #else
2568 float factor;
2569 #ifdef WIN32
2570 if (hrpcFrequency.QuadPart != 0)
2571 {
2572 LARGE_INTEGER newCount;
2573
2574 QueryPerformanceCounter(&newCount);
2575 factor = (float)((newCount.QuadPart - hrpcFrameCount.QuadPart) /
2576 (double)hrpcFrequency.QuadPart);
2577 }
2578 else
2579 {
2580 factor = (GetTickCount() - frameTicks) / 1000.0f;
2581 }
2582 #endif // WIN32
2583 #ifdef _QT
2584 factor = qtime.elapsed() /1000.0f;
2585 #endif
2586 #ifdef COCOA
2587 if (FRAME_TIME != nil)
2588 {
2589 factor = (float)-[FRAME_TIME timeIntervalSinceNow];
2590 }
2591 #endif // COCOA
2592 #if !defined(WIN32) && !defined(_QT) && !defined(COCOA)
2593 factor = 1.0f / multiplier;
2594 #endif // None of the above
2595 #endif // !USE_STD_CHRONO
2596 if (viewMode == VMWalk)
2597 {
2598 TCVector upVector(0.0f, -1.0f, 0.0f);
2599 TCVector tempMotion(0.0f, 0.0f, 0.0f);
2600 TCFloat matrix[16];
2601 TCFloat inverseMatrix[16];
2602 TCVector::invertMatrix(camera.getFacing().getMatrix(), inverseMatrix);
2603 TCVector::multMatrix(inverseMatrix, rotationMatrix, matrix);
2604 TCVector tempVector = upVector.transformNormal(matrix);
2605 TCFloat motionAmount = 20.0f * (TCFloat)sqrt(fov / 45.0f);
2606
2607 // TCFloat identity[16];
2608 // memcpy(identity, TCVector::sm_identityMatrix, sizeof(TCVector::sm_identityMatrix));
2609 // TCVector::calcRotationMatrix(cameraXRotate, cameraYRotate, identity);
2610
2611 TCFloat identity[16] =
2612 {
2613 1.0, 0.0, 0.0, 0.0,
2614 0.0, 1.0, 0.0, 0.0,
2615 0.0, 0.0, 1.0, 0.0,
2616 0.0, 0.0, 0.0, 1.0
2617 };
2618 TCVector::calcRotationMatrix(cameraXRotate, cameraYRotate, identity);
2619 TCVector whateverVector = upVector.transformNormal(identity);
2620
2621 if (cameraMotion[2] > 0.0f)
2622 {
2623 tempMotion[1] += -tempVector[2];
2624 tempMotion[2] += tempVector[1];
2625 }
2626 else if (cameraMotion[2] < 0.0f)
2627 {
2628 tempMotion[1] += tempVector[2];
2629 tempMotion[2] += -tempVector[1];
2630 }
2631 if (cameraMotion[1] > 0.0f)
2632 {
2633 tempMotion[1] += (1 - tempVector[2]);
2634 tempMotion[2] += (1 - tempVector[1]);
2635 }
2636 else if (cameraMotion[1] < 0.0f)
2637 {
2638 tempMotion[1] += -(1 - tempVector[2]);
2639 tempMotion[2] += -(1 - tempVector[1]);
2640 }
2641 tempMotion[0] = cameraMotion[0];
2642 camera.move(tempMotion * motionAmount * factor * multiplier);
2643 }
2644 else
2645 {
2646 camera.move(cameraMotion * size / 100.0f * factor * multiplier);
2647 }
2648 camera.rotate(TCVector(cameraXRotate, cameraYRotate, cameraZRotate) *
2649 factor * multiplier * 1.5f);
2650 }
2651
zoom(TCFloat amount,bool apply)2652 void LDrawModelViewer::zoom(TCFloat amount, bool apply)
2653 {
2654 if (flags.paused)
2655 {
2656 return;
2657 }
2658 if (clipZoom)
2659 {
2660 nextClipAmount = clipAmount - amount / 1000.0f;
2661
2662 if (nextClipAmount > aspectRatio * 2)
2663 {
2664 nextClipAmount = aspectRatio * 2;
2665 }
2666 /*
2667 if (nextClipAmount > 1.0f)
2668 {
2669 nextClipAmount = 1.0f;
2670 }
2671 */
2672 else if (nextClipAmount < 0.0f)
2673 {
2674 nextClipAmount = 0.0f;
2675 }
2676 }
2677 else
2678 {
2679 TCFloat distance = (camera.getPosition()).length();
2680 // TCFloat distance = (camera.getPosition() - center).length();
2681
2682 nextDistance = distance + (amount * distance / 300.0f);
2683 if (flags.constrainZoom && !skipCameraPositioning())
2684 {
2685 if (nextDistance < size / zoomMax)
2686 {
2687 nextDistance = size / zoomMax;
2688 }
2689 }
2690 // We may as well always constrain the maximum zoom, since there not
2691 // really any reason to move too far away.
2692 if (nextDistance > defaultDistance * 10.0f && !skipCameraPositioning())
2693 {
2694 nextDistance = defaultDistance * 10.0f;
2695 }
2696 }
2697 if (apply)
2698 {
2699 applyZoom();
2700 }
2701 if (!fEq(amount, 0))
2702 {
2703 requestRedraw();
2704 }
2705 }
2706
requestRedraw(void)2707 void LDrawModelViewer::requestRedraw(void)
2708 {
2709 TCAlertManager::sendAlert(redrawAlertClass(), this);
2710 }
2711
setShowBoundingBox(bool value)2712 void LDrawModelViewer::setShowBoundingBox(bool value)
2713 {
2714 if (value != flags.showBoundingBox)
2715 {
2716 flags.showBoundingBox = value;
2717 requestRedraw();
2718 }
2719 }
2720
setBoundingBoxesOnly(bool value)2721 void LDrawModelViewer::setBoundingBoxesOnly(bool value)
2722 {
2723 if (value != flags.boundingBoxesOnly)
2724 {
2725 flags.boundingBoxesOnly = value;
2726 flags.needsReparse = true;
2727 flags.needsCalcSize = true;
2728 }
2729 }
2730
applyZoom(void)2731 void LDrawModelViewer::applyZoom(void)
2732 {
2733 if (flags.paused)
2734 {
2735 return;
2736 }
2737 if (clipZoom)
2738 {
2739 if (!fEq(clipAmount, nextClipAmount))
2740 {
2741 clipAmount = nextClipAmount;
2742 perspectiveView(false);
2743 }
2744 }
2745 else
2746 {
2747 TCFloat distance = (camera.getPosition()).length();
2748
2749 if (!fEq(distance, nextDistance))
2750 {
2751 camera.move(TCVector(0.0f, 0.0f, nextDistance - distance));
2752 // distance = newDistance;
2753 // camera.move(TCVector(0.0f, 0.0f, amount * size / 300.0f));
2754 perspectiveView(false);
2755 }
2756 }
2757 }
2758
clearBackground(void)2759 void LDrawModelViewer::clearBackground(void)
2760 {
2761 GLfloat backgroundColor[3];
2762
2763 if (cullBackFaces)
2764 {
2765 glEnable(GL_CULL_FACE);
2766 glCullFace(GL_BACK);
2767 }
2768 else
2769 {
2770 glDisable(GL_CULL_FACE);
2771 }
2772 glClearDepth(1.0);
2773 backgroundColor[0] = (GLfloat)backgroundR;
2774 backgroundColor[1] = (GLfloat)backgroundG;
2775 backgroundColor[2] = (GLfloat)backgroundB;
2776 glFogfv(GL_FOG_COLOR, backgroundColor);
2777 if (flags.slowClear && !getGl2ps())
2778 {
2779 GLint oldDepthFunc;
2780 bool oldBlendEnabled = false;
2781 bool oldPolygonOffsetEnabled = false;
2782 bool oldLightingEnabled = false;
2783
2784 orthoView();
2785 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2786 glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc);
2787 glDepthFunc(GL_ALWAYS);
2788 glColor4f(backgroundR, backgroundG, backgroundB, backgroundA);
2789 if (glIsEnabled(GL_BLEND))
2790 {
2791 glDisable(GL_BLEND);
2792 oldBlendEnabled = true;
2793 }
2794 if (glIsEnabled(GL_POLYGON_OFFSET_FILL))
2795 {
2796 disable(GL_POLYGON_OFFSET_FILL);
2797 oldPolygonOffsetEnabled = true;
2798 }
2799 if (glIsEnabled(GL_LIGHTING))
2800 {
2801 glDisable(GL_LIGHTING);
2802 oldLightingEnabled = true;
2803 }
2804 glDepthMask(1);
2805 glBegin(GL_QUADS);
2806 glVertex3i(-1, -1, -1);
2807 glVertex3i((GLint)scale(width) + 1, -1, -1);
2808 glVertex3i((GLint)scale(width) + 1, (GLint)scale(height) + 1, -1);
2809 glVertex3i(-1, (GLint)scale(height) + 1, -1);
2810 glEnd();
2811 glDepthFunc(oldDepthFunc);
2812 if (oldBlendEnabled)
2813 {
2814 glEnable(GL_BLEND);
2815 }
2816 if (oldPolygonOffsetEnabled)
2817 {
2818 enable(GL_POLYGON_OFFSET_FILL);
2819 }
2820 if (oldLightingEnabled)
2821 {
2822 glEnable(GL_LIGHTING);
2823 }
2824 perspectiveView();
2825 }
2826 else
2827 {
2828 glClearColor(backgroundR, backgroundG, backgroundB, backgroundA);
2829 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
2830 GL_STENCIL_BUFFER_BIT);
2831 }
2832 if (flags.drawWireframe)
2833 {
2834 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2835 lineWidth(getScaledWireframeLineWidth());
2836 if (flags.useWireframeFog)
2837 {
2838 glEnable(GL_FOG);
2839 }
2840 else
2841 {
2842 glDisable(GL_FOG);
2843 }
2844 }
2845 else
2846 {
2847 lineWidth(getScaledHighlightLineWidth());
2848 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2849 glDisable(GL_FOG);
2850 }
2851 }
2852
projectCamera(const TCVector & distance)2853 void LDrawModelViewer::projectCamera(const TCVector &distance)
2854 {
2855 TCFloat inverseMatrix[16];
2856
2857 camera.getFacing().getInverseMatrix(inverseMatrix);
2858 TCVector camCenter = -distance.mult(inverseMatrix) - camera.getPosition();
2859 treGlMultMatrixf(inverseMatrix);
2860 treGlTranslatef(camCenter[0], camCenter[1], camCenter[2]);
2861 }
2862
drawSetup(TCFloat eyeXOffset)2863 void LDrawModelViewer::drawSetup(TCFloat eyeXOffset)
2864 {
2865 glLoadIdentity();
2866 if (flags.qualityLighting)
2867 {
2868 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
2869 }
2870 else
2871 {
2872 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 0);
2873 }
2874 if (flags.usesFlatShading)
2875 {
2876 glShadeModel(GL_FLAT);
2877 }
2878 else
2879 {
2880 glShadeModel(GL_SMOOTH);
2881 }
2882 drawLights();
2883 glLoadIdentity();
2884 if (mainTREModel)
2885 {
2886 applyZoom();
2887 // camera.move(cameraMotion * size / 100.0f);
2888 perspectiveView(false);
2889 // camera.rotate(TCVector(cameraXRotate, cameraYRotate, cameraZRotate));
2890 projectCamera(TCVector(-eyeXOffset - xPan, -yPan, 0.0f));
2891 // treGlTranslatef(eyeXOffset + xPan, yPan, -distance);
2892 }
2893 else
2894 {
2895 projectCamera(TCVector(-eyeXOffset - xPan, -yPan, 10.0f));
2896 // treGlTranslatef(eyeXOffset + xPan, yPan, -10.0f);
2897 }
2898 }
2899
setExamineMode(ExamineMode value)2900 void LDrawModelViewer::setExamineMode(ExamineMode value)
2901 {
2902 examineMode = value;
2903 }
2904
setViewMode(ViewMode value)2905 void LDrawModelViewer::setViewMode(ViewMode value)
2906 {
2907 viewMode = value;
2908 }
2909
innerDrawModel(void)2910 void LDrawModelViewer::innerDrawModel(void)
2911 {
2912 mainTREModel->draw();
2913 if (highlightModel != NULL)
2914 {
2915 if (flags.drawWireframe)
2916 {
2917 highlightModel->setEdgeLineWidth(getScaledWireframeLineWidth());
2918 }
2919 else
2920 {
2921 highlightModel->setEdgeLineWidth(getScaledHighlightLineWidth());
2922 }
2923 highlightModel->draw();
2924 }
2925 drawAxes(true);
2926 if (flags.showBoundingBox)
2927 {
2928 drawBoundingBox();
2929 }
2930 }
2931
drawToClipPlaneUsingStencil(TCFloat eyeXOffset)2932 void LDrawModelViewer::drawToClipPlaneUsingStencil(TCFloat eyeXOffset)
2933 {
2934 glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT |
2935 GL_LINE_BIT | GL_STENCIL_BUFFER_BIT);
2936 perspectiveViewToClipPlane();
2937 glDisable(GL_DEPTH_TEST);
2938 glClear(GL_STENCIL_BUFFER_BIT);
2939 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2940 glEnable(GL_STENCIL_TEST);
2941 glStencilFunc(GL_ALWAYS, 1, ~0u);
2942 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2943 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2944 lineWidth(getScaledCutawayLineWidth());
2945 glDisable(GL_FOG);
2946 glLoadIdentity();
2947 projectCamera(TCVector(-eyeXOffset - xPan, -yPan, 0.0f));
2948 if (viewMode == VMFlyThrough || viewMode == VMWalk || examineMode == EMFree)
2949 {
2950 if (rotationMatrix)
2951 {
2952 glPushMatrix();
2953 glLoadIdentity();
2954 treGlRotatef(rotationSpeed, xRotate, yRotate, zRotate);
2955 treGlMultMatrixf(rotationMatrix);
2956 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
2957 glPopMatrix();
2958 treGlMultMatrixf(rotationMatrix);
2959 }
2960 }
2961 else if (viewMode == VMExamine && examineMode == EMLatLong)
2962 {
2963 //examineLongitude += rotationSpeed;
2964 //examineLatitude += rotationSpeed;
2965 //examineLongitude = fmodf(examineLongitude, 360.0f);
2966 //examineLatitude = fmodf(examineLatitude, 360.0f);
2967 treGlRotatef(examineLongitude, 0.0f, 1.0f, 0.0f);
2968 treGlRotatef(examineLatitude, 1.0f, 0.0f, 0.0f);
2969 }
2970 if (flags.autoCenter)
2971 {
2972 treGlTranslatef(-center[0], -center[1], -center[2]);
2973 }
2974 showLight();
2975 innerDrawModel();
2976 glDisable(GL_LIGHTING);
2977 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2978 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2979 glMatrixMode(GL_PROJECTION);
2980 glLoadIdentity();
2981 glOrtho(0, 1, 0, 1, 0, 1);
2982 glMatrixMode(GL_MODELVIEW);
2983 glLoadIdentity();
2984 if ((backgroundR + backgroundG + backgroundB) / 3.0f > 0.5)
2985 {
2986 glColor4f(0.0f, 0.0f, 0.0f, cutawayAlpha);
2987 }
2988 else
2989 {
2990 glColor4f(1.0f, 1.0f, 1.0f, cutawayAlpha);
2991 }
2992 enable(GL_BLEND);
2993 blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2994 glStencilFunc(GL_EQUAL, 1, ~0u);
2995 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2996 glRectf(0.0, 0.0, 1.0, 1.0);
2997 perspectiveView();
2998 glEnable(GL_DEPTH_TEST);
2999 glDisable(GL_STENCIL_TEST);
3000 disable(GL_BLEND);
3001 glEnable(GL_LIGHTING);
3002 glPopAttrib();
3003 }
3004
3005 /*
3006 void LDrawModelViewer::drawToClipPlaneUsingAccum(GLfloat eyeXOffset)
3007 {
3008 TCFloat weight = 0.25f;
3009 TCFloat oldZoomSpeed = zoomSpeed;
3010
3011 glReadBuffer(GL_BACK);
3012 glAccum(GL_LOAD, 1.0f - weight);
3013 glClearColor(backgroundR, backgroundG, backgroundB, backgroundA);
3014 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3015
3016 zoomSpeed = 0.0f;
3017 clearBackground();
3018 drawSetup();
3019 zoomSpeed = oldZoomSpeed;
3020 if (rotationMatrix)
3021 {
3022 glPushMatrix();
3023 glLoadIdentity();
3024 treGlMultMatrixf(rotationMatrix);
3025 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3026 glPopMatrix();
3027 treGlMultMatrixf(rotationMatrix);
3028 }
3029 treGlTranslatef(-center[0], -center[1], -center[2]);
3030 glColor3ub(192, 192, 192);
3031 mainTREModel->draw();
3032
3033 glClearDepth(1.0);
3034 glClear(GL_DEPTH_BUFFER_BIT);
3035 perspectiveViewToClipPlane();
3036 glClearDepth(1.0);
3037 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
3038 lineWidth(cutawayLineWidth);
3039 glDisable(GL_FOG);
3040 glLoadIdentity();
3041 treGlTranslatef(eyeXOffset + xPan, yPan, -distance);
3042 if (rotationMatrix)
3043 {
3044 glPushMatrix();
3045 glLoadIdentity();
3046 treGlMultMatrixf(rotationMatrix);
3047 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3048 glPopMatrix();
3049 treGlMultMatrixf(rotationMatrix);
3050 }
3051 treGlTranslatef(-center[0], -center[1], -center[2]);
3052 mainTREModel->draw();
3053 perspectiveView();
3054 glAccum(GL_ACCUM, weight);
3055 glAccum(GL_RETURN, 1.0f);
3056 }
3057 */
3058
drawToClipPlaneUsingDestinationAlpha(TCFloat eyeXOffset)3059 void LDrawModelViewer::drawToClipPlaneUsingDestinationAlpha(TCFloat eyeXOffset)
3060 {
3061 TCFloat32 weight = cutawayAlpha;
3062
3063 glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT |
3064 GL_LINE_BIT);
3065 perspectiveViewToClipPlane();
3066 glClearDepth(1.0);
3067 glClearColor(weight, weight, weight, weight);
3068 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
3069 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3070 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
3071 enable(GL_BLEND);
3072 blendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
3073 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
3074 lineWidth(getScaledCutawayLineWidth());
3075 glDisable(GL_FOG);
3076 glLoadIdentity();
3077 projectCamera(TCVector(-eyeXOffset - xPan, -yPan, 0.0f));
3078 // treGlTranslatef(eyeXOffset + xPan, yPan, -distance);
3079 //treGlTranslatef(0.0f, 0.0f, -distance);
3080 if (rotationMatrix)
3081 {
3082 glPushMatrix();
3083 glLoadIdentity();
3084 treGlRotatef(rotationSpeed, xRotate, yRotate, zRotate);
3085 treGlMultMatrixf(rotationMatrix);
3086 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3087 glPopMatrix();
3088 treGlMultMatrixf(rotationMatrix);
3089 }
3090 if (flags.autoCenter)
3091 {
3092 treGlTranslatef(-center[0], -center[1], -center[2]);
3093 }
3094 showLight();
3095 mainTREModel->setCutawayDrawFlag(true);
3096 innerDrawModel();
3097 mainTREModel->setCutawayDrawFlag(false);
3098 perspectiveView();
3099 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3100 disable(GL_BLEND);
3101 glPopAttrib();
3102 }
3103
drawToClipPlaneUsingNoEffect(TCFloat eyeXOffset)3104 void LDrawModelViewer::drawToClipPlaneUsingNoEffect(TCFloat eyeXOffset)
3105 {
3106 glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT |
3107 GL_LINE_BIT);
3108 perspectiveViewToClipPlane();
3109 glClearDepth(1.0);
3110 glClear(GL_DEPTH_BUFFER_BIT);
3111 // glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
3112 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
3113 lineWidth(getScaledCutawayLineWidth());
3114 glDisable(GL_FOG);
3115 glLoadIdentity();
3116 projectCamera(TCVector(-eyeXOffset - xPan, -yPan, 0.0f));
3117 // treGlTranslatef(eyeXOffset + xPan, yPan, -distance);
3118 if (rotationMatrix)
3119 {
3120 glPushMatrix();
3121 glLoadIdentity();
3122 treGlRotatef(rotationSpeed, xRotate, yRotate, zRotate);
3123 treGlMultMatrixf(rotationMatrix);
3124 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3125 glPopMatrix();
3126 treGlMultMatrixf(rotationMatrix);
3127 }
3128 if (flags.autoCenter)
3129 {
3130 treGlTranslatef(-center[0], -center[1], -center[2]);
3131 }
3132 showLight();
3133 innerDrawModel();
3134 perspectiveView();
3135 glPopAttrib();
3136 // glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3137 }
3138
drawToClipPlane(TCFloat eyeXOffset)3139 void LDrawModelViewer::drawToClipPlane(TCFloat eyeXOffset)
3140 {
3141 switch (cutawayMode)
3142 {
3143 case LDVCutawayNormal:
3144 // Don't do anything
3145 break;
3146 case LDVCutawayWireframe:
3147 if (fEq(cutawayAlpha, 1.0f))
3148 {
3149 drawToClipPlaneUsingNoEffect(eyeXOffset);
3150 }
3151 else
3152 {
3153 drawToClipPlaneUsingDestinationAlpha(eyeXOffset);
3154 }
3155 break;
3156 case LDVCutawayStencil:
3157 drawToClipPlaneUsingStencil(eyeXOffset);
3158 break;
3159 }
3160 // drawToClipPlaneUsingAccum(eyeXOffset);
3161 }
3162
clear(void)3163 void LDrawModelViewer::clear(void)
3164 {
3165 glClearStencil(0);
3166 glClearDepth(1.0);
3167 glClearColor(backgroundR, backgroundG, backgroundB, backgroundA);
3168 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
3169 }
3170
getLDrawCommandLineMatrix(char * matrixString,int bufferLength)3171 bool LDrawModelViewer::getLDrawCommandLineMatrix(char *matrixString,
3172 int bufferLength)
3173 {
3174 if (rotationMatrix && bufferLength)
3175 {
3176 char buf[1024];
3177 TCFloat matrix[16];
3178 int i;
3179 TCVector point;
3180
3181 if (flags.autoCenter)
3182 {
3183 point = -center;
3184 }
3185 for (i = 0; i < 16; i++)
3186 {
3187 if (fEq(rotationMatrix[i], 0.0f))
3188 {
3189 matrix[i] = 0.0f;
3190 }
3191 else if (i == 0 || i == 4 || i == 8)
3192 {
3193 matrix[i] = rotationMatrix[i];
3194 }
3195 else
3196 {
3197 matrix[i] = -rotationMatrix[i];
3198 }
3199 }
3200 point = point.transformPoint(matrix);
3201 sprintf(buf, "-a%.2g,%.2g,%.2g,%.2g,%.2g,%.2g,%.2g,%.2g,%.2g",
3202 matrix[0], matrix[4], matrix[8],
3203 matrix[1], matrix[5], matrix[9],
3204 matrix[2], matrix[6], matrix[10]);
3205 strncpy(matrixString, buf, bufferLength);
3206 matrixString[bufferLength - 1] = 0;
3207 return true;
3208 }
3209 else
3210 {
3211 return false;
3212 }
3213 }
3214
getLDGLiteCommandLine(char * commandString,int bufferLength)3215 bool LDrawModelViewer::getLDGLiteCommandLine(char *commandString,
3216 int bufferLength)
3217 {
3218 if (rotationMatrix && bufferLength)
3219 {
3220 char buf[1024];
3221 char matrixString[512];
3222 TCVector cameraPoint = camera.getPosition();
3223 // TCVector cameraPoint = TCVector(0.0f, 0.0f, distance);
3224 TCVector lookAt;
3225
3226 if (flags.autoCenter)
3227 {
3228 lookAt = center;
3229 }
3230 int i;
3231 TCFloat transformationMatrix[16];
3232
3233 if (!getLDrawCommandLineMatrix(matrixString, 512))
3234 {
3235 return false;
3236 }
3237 TCVector::invertMatrix(rotationMatrix, transformationMatrix);
3238 lookAt = lookAt.transformPoint(rotationMatrix);
3239 cameraPoint += lookAt;
3240 // cameraPoint = TGLShape::transformPoint(cameraPoint, rotationMatrix);
3241 for (i = 0; i < 3; i++)
3242 {
3243 if (fEq(cameraPoint[i], 0.0f))
3244 {
3245 cameraPoint[i] = 0.0f;
3246 }
3247 }
3248 sprintf(buf, "ldglite -J -v%d,%d "
3249 "-cc%.4f,%.4f,%.4f -co%.4f,%.4f,%.4f "
3250 "-cu0,1,0 %s \"%s\"", (int)scale(width), (int)scale(height),
3251 cameraPoint[0], cameraPoint[1], cameraPoint[2],
3252 lookAt[0], lookAt[1], lookAt[2],
3253 matrixString, filename);
3254 strncpy(commandString, buf, bufferLength);
3255 commandString[bufferLength - 1] = 0;
3256 return true;
3257 }
3258 return false;
3259 }
3260
getLDrawCommandLine(char * shortFilename,char * commandString,int bufferLength)3261 bool LDrawModelViewer::getLDrawCommandLine(char *shortFilename,
3262 char *commandString,
3263 int bufferLength)
3264 {
3265 if (rotationMatrix && bufferLength)
3266 {
3267 char buf[1024];
3268 TCFloat matrix[16];
3269 int i;
3270 TCVector point;
3271 TCFloat lScaleFactor = 500.0f;
3272 TCFloat distance = (camera.getPosition()).length();
3273 // TCFloat distance = (camera.getPosition() - center).length();
3274
3275 if (flags.autoCenter)
3276 {
3277 point = -center;
3278 }
3279 for (i = 0; i < 16; i++)
3280 {
3281 if (fEq(rotationMatrix[i], 0.0f))
3282 {
3283 matrix[i] = 0.0f;
3284 }
3285 else if (i == 0 || i == 4 || i == 8)
3286 {
3287 matrix[i] = rotationMatrix[i];
3288 }
3289 else
3290 {
3291 matrix[i] = -rotationMatrix[i];
3292 }
3293 }
3294 point = point.transformPoint(matrix);
3295 sprintf(buf, "ldraw -s%.4g -o%d,%d "
3296 "-a%.4g,%.4g,%.4g,%.4g,%.4g,%.4g,%.4g,%.4g,%.4g "
3297 "%s",
3298 lScaleFactor / distance,
3299 (int)(point[0] * lScaleFactor / distance),
3300 (int)(point[1] * lScaleFactor / distance),
3301 matrix[0], matrix[4], matrix[8],
3302 matrix[1], matrix[5], matrix[9],
3303 matrix[2], matrix[6], matrix[10],
3304 shortFilename);
3305 strncpy(commandString, buf, bufferLength);
3306 commandString[bufferLength - 1] = 0;
3307 return true;
3308 }
3309 else
3310 {
3311 return false;
3312 }
3313 }
3314
3315 // Note: static method
fixLongitude(TCFloat & lon)3316 void LDrawModelViewer::fixLongitude(TCFloat &lon)
3317 {
3318 lon = fmodf(lon, 360.0f);
3319 if (lon > 180.0f)
3320 {
3321 lon -= 360.0f;
3322 }
3323 if (lon <= -180.0f)
3324 {
3325 lon += 360.0f;
3326 }
3327 }
3328
applyModelRotation(void)3329 void LDrawModelViewer::applyModelRotation(void)
3330 {
3331 if (rotationMatrix)
3332 {
3333 if (!flags.paused)
3334 {
3335 if (viewMode == VMFlyThrough || viewMode == VMWalk || examineMode == EMFree)
3336 {
3337 TCFloat matrix[16];
3338 TCVector rotation = TCVector(xRotate, yRotate, zRotate);
3339
3340 camera.getFacing().getInverseMatrix(matrix);
3341 glPushMatrix();
3342 glLoadIdentity();
3343 rotation = rotation.mult(matrix);
3344 treGlRotatef(rotationSpeed, rotation[0], rotation[1], rotation[2]);
3345 treGlMultMatrixf(rotationMatrix);
3346 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3347 glPopMatrix();
3348 }
3349 else if (viewMode == VMExamine && examineMode == EMLatLong)
3350 {
3351 if (rotationSpeed != 0)
3352 {
3353 examineLongitude += rotationSpeed * -yRotate * 0.1f;
3354 fixLongitude(examineLongitude);
3355 examineLatitude += rotationSpeed * xRotate * 0.2f;
3356 if (examineLatitude > 90.0f)
3357 {
3358 examineLatitude = 90.0f;
3359 }
3360 if (examineLatitude < -90.0f)
3361 {
3362 examineLatitude = -90.0f;
3363 }
3364 }
3365 glPushMatrix();
3366 glLoadIdentity();
3367 treGlRotatef(examineLatitude + 180.0f, 1.0f, 0.0f, 0.0f);
3368 treGlRotatef(examineLongitude, 0.0f, 1.0f, 0.0f);
3369 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3370 glPopMatrix();
3371 }
3372 }
3373 }
3374 }
3375
update(void)3376 void LDrawModelViewer::update(void)
3377 {
3378 static GLubyte stipplePattern[128];
3379 static bool stipplePatternSet = false;
3380 TCFloat eyeXOffset = 0.0f;
3381
3382 flags.updating = true;
3383 if (flags.needsReload)
3384 {
3385 reload();
3386 }
3387 if (flags.needsReparse)
3388 {
3389 parseModel();
3390 }
3391 if (!stipplePatternSet)
3392 {
3393 int i;
3394
3395 for (i = 0; i < 32; i++)
3396 {
3397 if (i % 2)
3398 {
3399 memset(stipplePattern + i * 4, 0xAA, 4);
3400 }
3401 else
3402 {
3403 memset(stipplePattern + i * 4, 0x55, 4);
3404 }
3405 }
3406 stipplePatternSet = true;
3407 }
3408 if (!rotationMatrix)
3409 {
3410 rotationMatrix = new TCFloat[16];
3411 setupDefaultViewAngle();
3412 flags.needsRotationMatrixSetup = true;
3413 }
3414 if (flags.needsSetup)
3415 {
3416 setup();
3417 }
3418 if (flags.needsTextureSetup)
3419 {
3420 setupTextures();
3421 }
3422 if (flags.needsLightingSetup)
3423 {
3424 setupLighting();
3425 }
3426 if (flags.needsMaterialSetup)
3427 {
3428 setupMaterial();
3429 }
3430 else if (flags.needsRecompile)
3431 {
3432 recompile();
3433 }
3434 if (flags.needsViewReset)
3435 {
3436 perspectiveView();
3437 resetView();
3438 }
3439 if (flags.needsResize)
3440 {
3441 perspectiveView();
3442 }
3443 glPolygonStipple(stipplePattern);
3444 if (flags.needsRotationMatrixSetup)
3445 {
3446 setupRotationMatrix();
3447 }
3448 if ((flags.keepRightSideUp && viewMode == VMFlyThrough) || viewMode == VMWalk)
3449 {
3450 rightSideUp(false);
3451 }
3452 clearBackground();
3453 if (!mainTREModel)
3454 {
3455 flags.updating = false;
3456 return;
3457 }
3458 mainTREModel->setSaveAlphaFlag(flags.saveAlpha);
3459 if (stereoMode == LDVStereoCrossEyed || stereoMode == LDVStereoParallel)
3460 {
3461 TCFloat distance = (camera.getPosition()).length();
3462
3463 eyeXOffset = stereoEyeSpacing * 2.0f / (TCFloat)pow((double)distance,
3464 0.25);
3465 if (stereoMode == LDVStereoCrossEyed)
3466 {
3467 eyeXOffset = -eyeXOffset;
3468 }
3469 }
3470 if (rotationMatrix)
3471 {
3472 applyModelRotation();
3473 }
3474 updateCameraPosition();
3475 zoom(zoomSpeed, false);
3476 if (flags.drawWireframe && flags.removeHiddenLines)
3477 {
3478 removeHiddenLines();
3479 drawModel(0.0f);
3480 }
3481 else
3482 {
3483 drawModel(eyeXOffset);
3484 }
3485 drawAxes(false);
3486 if (stereoMode == LDVStereoCrossEyed || stereoMode == LDVStereoParallel)
3487 {
3488 eyeXOffset = -eyeXOffset;
3489 GLint eyeWidth = (GLint)(scale(width) / 2);
3490 glViewport(eyeWidth, 0, eyeWidth, (GLsizei)scale(height));
3491 if (flags.slowClear)
3492 {
3493 clearBackground();
3494 glViewport(eyeWidth, 0, eyeWidth, (GLsizei)scale(height));
3495 }
3496 if (flags.drawWireframe && flags.removeHiddenLines)
3497 {
3498 removeHiddenLines();
3499 drawModel(0.0f);
3500 }
3501 else
3502 {
3503 drawModel(eyeXOffset);
3504 }
3505 drawAxes(false);
3506 glViewport(0, 0, eyeWidth, (GLsizei)scale(height));
3507 }
3508 flags.updating = false;
3509 if ((!fEq(rotationSpeed, 0.0f) ||
3510 !fEq(zoomSpeed, 0.0f) ||
3511 !fEq(cameraXRotate, 0.0f) ||
3512 !fEq(cameraYRotate, 0.0f) ||
3513 !fEq(cameraZRotate, 0.0f) ||
3514 !fEq(cameraMotion.length(), 0.0f))
3515 && !getPaused())
3516 {
3517 requestRedraw();
3518 flags.animating = true;
3519 }
3520 else
3521 {
3522 flags.animating = false;
3523 }
3524 TCAlertManager::sendAlert(frameDoneAlertClass(), this);
3525 updateFrameTime(true);
3526 }
3527
updateFrameTime(bool force)3528 void LDrawModelViewer::updateFrameTime(bool force /*=false*/)
3529 {
3530 if (!flags.animating || force)
3531 {
3532 #ifdef USE_STD_CHRONO
3533 frameTime = std::chrono::steady_clock::now();
3534 #else
3535 #ifdef WIN32
3536 if (hrpcFrequency.QuadPart != 0)
3537 {
3538 QueryPerformanceCounter(&hrpcFrameCount);
3539 }
3540 else
3541 {
3542 frameTicks = GetTickCount();
3543 }
3544 #endif // WIN32
3545 #ifdef _QT
3546 qtime.restart();
3547 #endif
3548 #ifdef COCOA
3549 [FRAME_TIME release];
3550 FRAME_TIME = [[NSDate alloc] init];
3551 #endif // COCOA
3552 #endif // !USE_STD_CHRONO
3553 }
3554 }
3555
removeHiddenLines(TCFloat eyeXOffset)3556 void LDrawModelViewer::removeHiddenLines(TCFloat eyeXOffset)
3557 {
3558 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
3559 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3560 mainTREModel->setLightingFlag(false);
3561 mainTREModel->setRemovingHiddenLines(true);
3562 if (flags.usePolygonOffset)
3563 {
3564 glPolygonOffset(POLYGON_OFFSET_FACTOR, POLYGON_OFFSET_UNITS);
3565 enable(GL_POLYGON_OFFSET_FILL);
3566 }
3567 drawModel(eyeXOffset);
3568 // Not sure why the following is necessary.
3569 lineWidth(getScaledWireframeLineWidth());
3570 if (flags.usePolygonOffset)
3571 {
3572 glPolygonOffset(0.0f, 0.0f);
3573 enable(GL_POLYGON_OFFSET_FILL);
3574 }
3575 mainTREModel->setRemovingHiddenLines(false);
3576 mainTREModel->setLightingFlag(flags.useLighting);
3577 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
3578 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3579 }
3580
setupRotationMatrix(void)3581 void LDrawModelViewer::setupRotationMatrix(void)
3582 {
3583 glMatrixMode(GL_MODELVIEW);
3584 glPushMatrix();
3585 glLoadIdentity();
3586 treGlRotatef(180.0f, 0.0f, 0.0f, 1.0f);
3587 treGlRotatef(180.0f, 0.0f, 1.0f, 0.0f);
3588 treGlMultMatrixf(rotationMatrix);
3589 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3590 glPopMatrix();
3591 flags.needsRotationMatrixSetup = false;
3592 /*
3593 glMatrixMode(GL_MODELVIEW);
3594 glPushMatrix();
3595 glLoadIdentity();
3596 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3597 treGlRotatef(180.0f, 0.0f, 0.0f, 1.0f);
3598 treGlRotatef(-90.0f, 0.0f, 1.0f, 0.0f);
3599 treGlRotatef(50.0f, 0.0f, -1.5f, 1.0f);
3600 treGlGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
3601 glPopMatrix();
3602 */
3603 }
3604
initLightDirModel(TREMainModel * & lightDirModel,TCULong color)3605 void LDrawModelViewer::initLightDirModel(
3606 TREMainModel *&lightDirModel,
3607 TCULong color)
3608 {
3609 TREModel *subModel = new TREModel;
3610 lightDirModel = new TREMainModel;
3611 lightDirModel->setSendProgressFlag(false);
3612 lightDirModel->setUseStripsFlag(getUseStrips());
3613 TCFloat identityMatrix[16];
3614 float length = size / 1.5f;
3615 float radius = size / 100.0f;
3616 int segments = 32;
3617 float coneLength = radius * 4.0f;
3618 float coneRadius = radius * 2.0f;
3619 float offset = length / 3.0f;
3620
3621 lightDirModel->setLightingFlag(true);
3622 TCVector::initIdentityMatrix(identityMatrix);
3623 subModel->setMainModel(lightDirModel);
3624 subModel->addCylinder(TCVector(0.0f, coneLength + offset, 0.0f), radius,
3625 length - coneLength - offset, segments);
3626 subModel->addDisc(TCVector(0.0, coneLength + offset, 0.0), coneRadius,
3627 segments);
3628 subModel->addDisc(TCVector(0.0, length, 0.0), radius, segments);
3629 subModel->addCone(TCVector(0.0, coneLength + offset, 0.0), coneRadius,
3630 -coneLength, segments);
3631 lightDirModel->addSubModel(color, color, identityMatrix, subModel, false);
3632 subModel->release();
3633 lightDirModel->postProcess();
3634 }
3635
initLightDirModels(void)3636 void LDrawModelViewer::initLightDirModels(void)
3637 {
3638 if (size > 0.0 && !flags.noUI)
3639 {
3640 if (whiteLightDirModel == NULL)
3641 {
3642 initLightDirModel(whiteLightDirModel,
3643 LDLPalette::colorForRGBA(255, 255, 255, 255));
3644 }
3645 if (blueLightDirModel == NULL)
3646 {
3647 initLightDirModel(blueLightDirModel,
3648 LDLPalette::colorForRGBA(128, 128, 255, 255));
3649 }
3650 }
3651 }
3652
getContrastingLightDirModel()3653 TREMainModel *LDrawModelViewer::getContrastingLightDirModel()
3654 {
3655 if ((backgroundR + backgroundG + backgroundB) / 3.0f > 0.9f)
3656 {
3657 return blueLightDirModel;
3658 }
3659 else
3660 {
3661 return whiteLightDirModel;
3662 }
3663 }
3664
showLight(void)3665 void LDrawModelViewer::showLight(void)
3666 {
3667 if (flags.showLight)
3668 {
3669 TCVector oldLightVector = lightVector;
3670 TCFloat rotInverse[16];
3671 LDLFacing facing;
3672 bool oldSpecular = flags.usesSpecular;
3673 bool oldSubdued = flags.subduedLighting;
3674
3675 TCVector::invertMatrix(rotationMatrix, rotInverse);
3676 flags.usesSpecular = false;
3677 flags.subduedLighting = false;
3678 glPushMatrix();
3679 glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT |
3680 GL_LINE_BIT | GL_LIGHTING_BIT);
3681 facing.setFacing(TCVector(1.0f, 0.0f, 0.0f), (TCFloat)M_PI / 2.0f);
3682 facing.pointAt(oldLightVector);
3683 treGlTranslatef(center[0], center[1], center[2]);
3684 treGlMultMatrixf(rotInverse);
3685 treGlMultMatrixf(camera.getFacing().getMatrix());
3686 lightVector = TCVector(0.0f, 0.0f, 1.0f);
3687 setupLight(GL_LIGHT0);
3688 glEnable(GL_LIGHTING);
3689 glEnable(GL_LIGHT0);
3690 glDisable(GL_LIGHT1);
3691 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
3692 glPushMatrix();
3693 glLoadIdentity();
3694 drawLights();
3695 glPopMatrix();
3696 lightVector = oldLightVector;
3697 treGlMultMatrixf(facing.getMatrix());
3698 getContrastingLightDirModel()->draw();
3699 glPopAttrib();
3700 glPopMatrix();
3701 flags.usesSpecular = oldSpecular;
3702 flags.subduedLighting = oldSubdued;
3703 }
3704 }
3705
drawLightDats(void)3706 void LDrawModelViewer::drawLightDats(void)
3707 {
3708 if (haveLightDats())
3709 {
3710 const TCVectorList &lightLocs = mainTREModel->getLightLocations();
3711 GLint maxLights;
3712
3713 glGetIntegerv(GL_MAX_LIGHTS, &maxLights);
3714 if (lightLocs.size() > 0)
3715 {
3716 int i;
3717 TCVectorList::const_iterator itLoc = lightLocs.begin();
3718 GLfloat position[4];
3719 int start = 0;
3720
3721 if (!flags.optionalStandardLight)
3722 {
3723 start = 1;
3724 }
3725 // Point lights are distinguished from directional lights by the w
3726 // component of their position vector. Setting it to 0 yields a
3727 // directional light. Setting it to non-zero yields a point light.
3728 position[3] = 1.0f;
3729 for (i = start; i < maxLights && itLoc != lightLocs.end(); i++)
3730 {
3731 const TCVector &lightLoc = *itLoc;
3732
3733 position[0] = (GLfloat)lightLoc.get(0);
3734 position[1] = (GLfloat)lightLoc.get(1);
3735 position[2] = (GLfloat)lightLoc.get(2);
3736 glLightfv(GL_LIGHT0 + i, GL_POSITION, position);
3737 itLoc++;
3738 }
3739 }
3740 }
3741 }
3742
drawAxes(bool atOrigin)3743 void LDrawModelViewer::drawAxes(bool atOrigin)
3744 {
3745 if (flags.showAxes && flags.axesAtOrigin == atOrigin)
3746 {
3747 if (flags.axesAtOrigin)
3748 {
3749 TCFloat margin = size / 8.0f;
3750 TCVector minPoint(boundingMin);
3751 TCVector maxPoint(boundingMax);
3752 //TCVector margin = (boundingMax - boundingMin) / 8.0f;
3753 int i;
3754
3755 for (i = 0; i < 3; i++)
3756 {
3757 if (boundingMin[i] > 0.0f)
3758 {
3759 minPoint[i] = -margin;
3760 }
3761 else
3762 {
3763 minPoint[i] = boundingMin[i] - margin;
3764 }
3765 if (boundingMax[i] < 0.0f)
3766 {
3767 maxPoint[i] = margin;
3768 }
3769 else
3770 {
3771 maxPoint[i] = boundingMax[i] + margin;
3772 }
3773 }
3774 glPushAttrib(GL_LIGHTING_BIT | GL_LINE_BIT);
3775 lineWidth(scale(2.0f));
3776 glDisable(GL_LIGHTING);
3777 glBegin(GL_LINES);
3778 glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
3779 glVertex3f(0.0f, 0.0f, 0.0f);
3780 glVertex3f(maxPoint[0], 0.0f, 0.0f);
3781 glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
3782 glVertex3f(0.0f, 0.0f, 0.0f);
3783 glVertex3f(0.0f, maxPoint[1], 0.0f);
3784 glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
3785 glVertex3f(0.0f, 0.0f, 0.0f);
3786 glVertex3f(0.0f, 0.0f, maxPoint[2]);
3787
3788 glColor4f(0.5f, 0.0f, 0.0f, 1.0f);
3789 glVertex3f(0.0f, 0.0f, 0.0f);
3790 glVertex3f(minPoint[0], 0.0f, 0.0f);
3791 glColor4f(0.0f, 0.5f, 0.0f, 1.0f);
3792 glVertex3f(0.0f, 0.0f, 0.0f);
3793 glVertex3f(0.0f, minPoint[1], 0.0f);
3794 glColor4f(0.0f, 0.0f, 0.5f, 1.0f);
3795 glVertex3f(0.0f, 0.0f, 0.0f);
3796 glVertex3f(0.0f, 0.0f, minPoint[2]);
3797 glEnd();
3798 glPopAttrib();
3799 }
3800 else
3801 {
3802 int actualWidth = (int)scale(width);
3803
3804 if (stereoMode == LDVStereoCrossEyed || stereoMode == LDVStereoParallel)
3805 {
3806 actualWidth = (int)(scale(width) / 2);
3807 }
3808 glPushAttrib(GL_LIGHTING_BIT | GL_VIEWPORT_BIT);
3809 glMatrixMode(GL_PROJECTION);
3810 glLoadIdentity();
3811 glOrtho(0.0f, actualWidth, 0.0f, (int)scale(height), -25.0f, 25.0f);
3812 glMatrixMode(GL_MODELVIEW);
3813 glLoadIdentity();
3814 glTranslatef(30.0f, 30.0f, 0.0f);
3815 if (rotationMatrix)
3816 {
3817 treGlMultMatrixf(rotationMatrix);
3818 }
3819 glDisable(GL_LIGHTING);
3820 glBegin(GL_LINES);
3821 glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
3822 glVertex3f(0.0f, 0.0f, 0.0f);
3823 glVertex3f(25.0f, 0.0f, 0.0f);
3824 glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
3825 glVertex3f(0.0f, 0.0f, 0.0f);
3826 glVertex3f(0.0f, 25.0f, 0.0f);
3827 glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
3828 glVertex3f(0.0f, 0.0f, 0.0f);
3829 glVertex3f(0.0f, 0.0f, 25.0f);
3830 glEnd();
3831 glPopAttrib();
3832 }
3833 }
3834 }
3835
drawModel(TCFloat eyeXOffset)3836 void LDrawModelViewer::drawModel(TCFloat eyeXOffset)
3837 {
3838 drawSetup(eyeXOffset);
3839 if (rotationMatrix)
3840 {
3841 treGlMultMatrixf(rotationMatrix);
3842 }
3843 if (flags.autoCenter)
3844 {
3845 treGlTranslatef(-center[0], -center[1], -center[2]);
3846 }
3847 drawLightDats();
3848 // drawBoundingBox();
3849 glColor3ub(192, 192, 192);
3850 if (mainTREModel)
3851 {
3852 showLight();
3853 innerDrawModel();
3854 if (clipAmount > 0.01)
3855 {
3856 drawToClipPlane(eyeXOffset);
3857 }
3858 }
3859 }
3860
pause(void)3861 void LDrawModelViewer::pause(void)
3862 {
3863 flags.paused = true;
3864 }
3865
unpause(void)3866 void LDrawModelViewer::unpause(void)
3867 {
3868 flags.paused = false;
3869 }
3870
setRotationSpeed(TCFloat value)3871 void LDrawModelViewer::setRotationSpeed(TCFloat value)
3872 {
3873 rotationSpeed = value;
3874 if (value)
3875 {
3876 flags.paused = false;
3877 }
3878 }
3879
setZoomSpeed(TCFloat value)3880 void LDrawModelViewer::setZoomSpeed(TCFloat value)
3881 {
3882 zoomSpeed = value;
3883 if (value)
3884 {
3885 flags.paused = false;
3886 }
3887 }
3888
setExtraSearchDirs(TCStringArray * value)3889 void LDrawModelViewer::setExtraSearchDirs(TCStringArray *value)
3890 {
3891 if (extraSearchDirs != value)
3892 {
3893 TCObject::release(extraSearchDirs);
3894 extraSearchDirs = value;
3895 TCObject::retain(extraSearchDirs);
3896 }
3897 // Since it is a string array, the contents might have changed, even if
3898 // the array pointer itself didn't.
3899 flags.needsReload = true;
3900 }
3901
panXY(int xValue,int yValue)3902 void LDrawModelViewer::panXY(int xValue, int yValue)
3903 {
3904 TCFloat adjustment;
3905 TCFloat distance = (camera.getPosition()).length();
3906 // TCFloat distance = (camera.getPosition() - center).length();
3907
3908 if (width > height)
3909 {
3910 adjustment = scale(width);
3911 }
3912 else
3913 {
3914 adjustment = scale(height);
3915 }
3916 // xPan += xValue / (TCFloat)pow(distance, 0.001);
3917 // yPan -= yValue / (TCFloat)pow(distance, 0.001);
3918 xPan += xValue / adjustment / (TCFloat)pow(2.0 / distance, 1.1) *
3919 (TCFloat)(sin(deg2rad(fov)) / sin(deg2rad(45.0)));
3920 yPan -= yValue / adjustment / (TCFloat)pow(2.0 / distance, 1.1) *
3921 (TCFloat)(sin(deg2rad(fov)) / sin(deg2rad(45.0)));
3922 }
3923
setXYPan(TCFloat xValue,TCFloat yValue)3924 void LDrawModelViewer::setXYPan(TCFloat xValue, TCFloat yValue)
3925 {
3926 xPan = xValue;
3927 yPan = yValue;
3928 }
3929
openGlWillEnd(void)3930 void LDrawModelViewer::openGlWillEnd(void)
3931 {
3932 if (mainTREModel)
3933 {
3934 mainTREModel->openGlWillEnd();
3935 }
3936 }
3937
getCompiled(void) const3938 bool LDrawModelViewer::getCompiled(void) const
3939 {
3940 if (mainTREModel)
3941 {
3942 return mainTREModel->getCompiled();
3943 }
3944 else
3945 {
3946 return false;
3947 }
3948 }
3949
connectionFailure(TCWebClient * webClient)3950 bool LDrawModelViewer::connectionFailure(TCWebClient *webClient)
3951 {
3952 // I'm not sure if we should add more errors here or not. Hopefully this is
3953 // enough. In reality, the hostname lookup will fail if the user doesn't
3954 // have an internet connection. If they have a mis-configured proxy, they
3955 // should either get TCNCE_CONNECT or TCNCE_CONNECTION_REFUSED.
3956 switch (webClient->getErrorNumber())
3957 {
3958 case TCNCE_HOSTNAME_LOOKUP:
3959 case TCNCE_NO_PORT:
3960 case TCNCE_CONNECT:
3961 case TCNCE_CONNECTION_REFUSED:
3962 return true;
3963 default:
3964 return false;
3965 }
3966 }
3967
findFileAlertCallback(LDLFindFileAlert * alert)3968 void LDrawModelViewer::findFileAlertCallback(LDLFindFileAlert *alert)
3969 {
3970 char *lfilename = copyString(alert->getFilename());
3971 size_t len = strlen(lfilename);
3972 char *url;
3973 char *partOutputFilename = copyString(LDLModel::lDrawDir(), len + 32);
3974 char *primitiveOutputFilename = copyString(LDLModel::lDrawDir(), len + 32);
3975 bool primitive = false;
3976 bool part = false;
3977 //const char *partUrlBase = "http://media.peeron.com/tmp/";
3978 const char *partUrlBase = "http://www.ldraw.org/library/unofficial/parts/";
3979 const char *primitiveUrlBase = "http://www.ldraw.org/library/unofficial/p/";
3980 bool found = false;
3981 char *key = new char[strlen(lfilename) + 128];
3982
3983 replaceStringCharacter(partOutputFilename, '\\', '/');
3984 replaceStringCharacter(primitiveOutputFilename, '\\', '/');
3985 strcat(partOutputFilename, "/Unofficial/parts/");
3986 strcat(primitiveOutputFilename, "/Unofficial/p/");
3987 convertStringToLower(lfilename);
3988 replaceStringCharacter(lfilename, '\\', '/');
3989 if (stringHasPrefix(lfilename, "48/"))
3990 {
3991 primitive = true;
3992 url = copyString(primitiveUrlBase, len + 2);
3993 }
3994 else
3995 {
3996 if (stringHasPrefix(lfilename, "s/"))
3997 {
3998 // The only thing this is used for is to prevent it from checking
3999 // for the file as a primitive if it's not found as a part.
4000 part = true;
4001 }
4002 url = copyString(partUrlBase, len + 2);
4003 }
4004 strcat(partOutputFilename, lfilename);
4005 strcat(primitiveOutputFilename, lfilename);
4006 if (fileExists(partOutputFilename))
4007 {
4008 primitive = false;
4009 found = true;
4010 alert->setPartFlag(true);
4011 }
4012 else if (!part && fileExists(primitiveOutputFilename))
4013 {
4014 primitive = true;
4015 found = true;
4016 delete[] url;
4017 url = copyString(primitiveUrlBase, len + 2);
4018 }
4019 if (canCheckForUnofficialPart(lfilename, found))
4020 {
4021 TCWebClient *webClient;
4022 // FIX: dynamically allocate and use local string AND handle abort
4023 UCCHAR message[1024];
4024 bool abort;
4025 UCSTR ucFilename = mbstoucstring(lfilename);
4026
4027 sprintf(key, "UnofficialPartChecks/%s/LastModified", lfilename);
4028 if (found)
4029 {
4030 sucprintf(message, COUNT_OF(message), ls(_UC("CheckingForUpdates")),
4031 ucFilename);
4032 }
4033 else
4034 {
4035 sucprintf(message, COUNT_OF(message), ls(_UC("TryingToDownload")),
4036 ucFilename);
4037 }
4038 delete[] ucFilename;
4039 TCProgressAlert::send("LDrawModelViewer", message, -1.0f, &abort, this);
4040 strcat(url, lfilename);
4041 webClient = new TCWebClient(url);
4042 if (found)
4043 {
4044 char *lastModified = TCUserDefaults::stringForKey(key, NULL, false);
4045
4046 if (lastModified)
4047 {
4048 webClient->setLastModifiedString(lastModified);
4049 delete[] lastModified;
4050 }
4051 }
4052 if (primitive)
4053 {
4054 *strrchr(primitiveOutputFilename, '/') = 0;
4055 webClient->setOutputDirectory(primitiveOutputFilename);
4056 primitiveOutputFilename[strlen(primitiveOutputFilename)] = '/';
4057 }
4058 else
4059 {
4060 *strrchr(partOutputFilename, '/') = 0;
4061 webClient->setOutputDirectory(partOutputFilename);
4062 partOutputFilename[strlen(partOutputFilename)] = '/';
4063 }
4064 if (webClient->fetchURL() ||
4065 webClient->getErrorNumber() == WCE_NOT_MODIFIED)
4066 {
4067 found = true;
4068 if (!primitive)
4069 {
4070 alert->setPartFlag(true);
4071 }
4072 }
4073 else if (connectionFailure(webClient))
4074 {
4075 // If we had a connection failure, we probably don't have an
4076 // internet connection, or our proxy is mis-configured. Don't try
4077 // to connect again for now, and let the user know that auto part
4078 // updates have been disabled.
4079
4080 preferences->setCheckPartTracker(false, true);
4081 flags.checkPartTracker = false;
4082 TCAlertManager::sendAlert(alertClass(), this,
4083 ls(_UC("PartCheckDisabled")));
4084 }
4085 else
4086 {
4087 if (!primitive && !part)
4088 {
4089 // We don't know if it's a primitive or a part. The part
4090 // download failed, so try as a primitive.
4091 delete[] url;
4092 url = copyString(primitiveUrlBase, len + 2);
4093 strcat(url, lfilename);
4094 webClient->release();
4095 webClient = new TCWebClient(url);
4096 *strrchr(primitiveOutputFilename, '/') = 0;
4097 webClient->setOutputDirectory(primitiveOutputFilename);
4098 primitiveOutputFilename[strlen(primitiveOutputFilename)] = '/';
4099 if (webClient->fetchURL() ||
4100 webClient->getErrorNumber() == WCE_NOT_MODIFIED)
4101 {
4102 primitive = true;
4103 found = true;
4104 }
4105 }
4106 }
4107 if (webClient->getLastModifiedString())
4108 {
4109 TCUserDefaults::setStringForKey(
4110 webClient->getLastModifiedString(), key, false);
4111 }
4112 webClient->release();
4113 sprintf(key, "UnofficialPartChecks/%s/LastUpdateCheckTime",
4114 lfilename);
4115 TCUserDefaults::setLongForKey((long)time(NULL), key, false);
4116 if (!found)
4117 {
4118 unofficialPartNotFound(lfilename);
4119 }
4120 }
4121 if (found)
4122 {
4123 alert->setFileFound(true);
4124 if (primitive)
4125 {
4126 alert->setFilename(primitiveOutputFilename);
4127 }
4128 else
4129 {
4130 alert->setFilename(partOutputFilename);
4131 }
4132 setUnofficialPartPrimitive(lfilename, primitive);
4133 }
4134 delete[] key;
4135 delete[] lfilename;
4136 delete[] url;
4137 delete[] partOutputFilename;
4138 delete[] primitiveOutputFilename;
4139 }
4140
getPartsList(void)4141 LDPartsList *LDrawModelViewer::getPartsList(void)
4142 {
4143 if (mainModel)
4144 {
4145 LDPartsList *partsList = new LDPartsList;
4146
4147 partsList->scanModel(getCurModel(), defaultColorNumber);
4148 return partsList;
4149 }
4150 else
4151 {
4152 return NULL;
4153 }
4154 }
4155
4156 // NOTE: static function
fileExists(const char * filename)4157 bool LDrawModelViewer::fileExists(const char* filename)
4158 {
4159 FILE* file = ucfopen(filename, "r");
4160
4161 if (file)
4162 {
4163 fclose(file);
4164 return true;
4165 }
4166 else
4167 {
4168 return false;
4169 }
4170 }
4171
4172 // NOTE: static function
setUnofficialPartPrimitive(const char * filename,bool primitive)4173 void LDrawModelViewer::setUnofficialPartPrimitive(const char *filename,
4174 bool primitive)
4175 {
4176 char *key = new char[strlen(filename) + 128];
4177
4178 sprintf(key, "UnofficialPartChecks/%s/Primitive", filename);
4179 TCUserDefaults::setLongForKey(primitive ? 1 : 0, key, false);
4180 delete[] key;
4181 }
4182
saveViewPoint(void) const4183 LDViewPoint *LDrawModelViewer::saveViewPoint(void) const
4184 {
4185 LDViewPoint *viewPoint = new LDViewPoint;
4186
4187 viewPoint->setCamera(camera);
4188 viewPoint->setRotation(TCVector(xRotate, yRotate, zRotate));
4189 viewPoint->setCameraRotation(TCVector(cameraXRotate, cameraYRotate,
4190 cameraZRotate));
4191 viewPoint->setPan(TCVector(xPan, yPan, 0.0f));
4192 viewPoint->setRotationMatrix(rotationMatrix);
4193 viewPoint->setRotationSpeed(rotationSpeed);
4194 viewPoint->setAutoCenter(flags.autoCenter != false);
4195 viewPoint->setBackgroundColor(backgroundR, backgroundG, backgroundB,
4196 backgroundA);
4197 viewPoint->setStereoMode(stereoMode);
4198 viewPoint->setClipAmount(clipAmount);
4199 viewPoint->setDefaultDistance(defaultDistance);
4200 return viewPoint;
4201 }
4202
rightSideUp(bool shouldRequestRedraw)4203 void LDrawModelViewer::rightSideUp(bool shouldRequestRedraw /*= true*/)
4204 {
4205 if (!flags.needsSetup)
4206 {
4207 TCVector upVector(0.0, -1.0, 0.0);
4208 TCFloat matrix[16];
4209 TCFloat inverseMatrix[16];
4210 TCVector::invertMatrix(camera.getFacing().getMatrix(), inverseMatrix);
4211 TCVector::multMatrix(inverseMatrix, rotationMatrix, matrix);
4212 TCVector tempVector = upVector.transformNormal(matrix);
4213 float lzRotate = 0.0;
4214
4215 if (tempVector[0] == 0.0 && tempVector[1] == 0.0)
4216 {
4217 lzRotate = 0;
4218 }
4219 else
4220 {
4221 lzRotate = atan2(tempVector[0], tempVector[1]);
4222 }
4223 camera.rotate(TCVector(0.0f, 0.0f, lzRotate));
4224 if (shouldRequestRedraw)
4225 {
4226 requestRedraw();
4227 }
4228 }
4229 }
4230
restoreViewPoint(const LDViewPoint * viewPoint)4231 void LDrawModelViewer::restoreViewPoint(const LDViewPoint *viewPoint)
4232 {
4233 TCVector tempVector;
4234
4235 camera = viewPoint->getCamera();
4236 tempVector = viewPoint->getRotation();
4237 xRotate = tempVector[0];
4238 yRotate = tempVector[1];
4239 zRotate = tempVector[2];
4240 tempVector = viewPoint->getCameraRotation();
4241 cameraXRotate = tempVector[0];
4242 cameraYRotate = tempVector[1];
4243 cameraZRotate = tempVector[2];
4244 tempVector = viewPoint->getPan();
4245 xPan = tempVector[0];
4246 yPan = tempVector[1];
4247 memcpy(rotationMatrix, viewPoint->getRotationMatrix(),
4248 16 * sizeof(rotationMatrix[0]));
4249 rotationSpeed = viewPoint->getRotationSpeed();
4250 flags.autoCenter = viewPoint->getAutoCenter();
4251 viewPoint->getBackgroundColor(backgroundR, backgroundG, backgroundB,
4252 backgroundA);
4253 stereoMode = viewPoint->getStereoMode();
4254 clipAmount = viewPoint->getClipAmount();
4255 defaultDistance = viewPoint->getDefaultDistance();
4256 }
4257
canCheckForUnofficialPart(const char * lfilename,bool exists)4258 bool LDrawModelViewer::canCheckForUnofficialPart(const char *lfilename,
4259 bool exists)
4260 {
4261 bool retValue = false;
4262
4263 if (flags.checkPartTracker)
4264 {
4265 char *key = new char[strlen(lfilename) + 128];
4266 time_t lastCheck;
4267 time_t now = time(NULL);
4268 int days;
4269
4270 if (exists)
4271 {
4272 sprintf(key, "UnofficialPartChecks/%s/LastUpdateCheckTime",
4273 lfilename);
4274 days = updatedPartWait;
4275 }
4276 else
4277 {
4278 sprintf(key, "UnofficialPartChecks/%s/LastCheckTime", lfilename);
4279 days = missingPartWait;
4280 }
4281 lastCheck = (time_t)TCUserDefaults::longForKey(key, 0, false);
4282 if (days < 1)
4283 {
4284 days = 1;
4285 }
4286 if (now - lastCheck > 24 * 3600 * days)
4287 {
4288 retValue = true;
4289 }
4290 delete[] key;
4291 }
4292 return retValue;
4293 }
4294
unofficialPartNotFound(const char * lfilename)4295 void LDrawModelViewer::unofficialPartNotFound(const char *lfilename)
4296 {
4297 if (flags.checkPartTracker)
4298 {
4299 char *key = new char[strlen(lfilename) + 128];
4300 time_t now = time(NULL);
4301
4302 sprintf(key, "UnofficialPartChecks/%s/LastCheckTime", lfilename);
4303 TCUserDefaults::setLongForKey((long)now, key, false);
4304 delete[] key;
4305 }
4306 }
4307
4308 // NOTE: static function
cleanupFloats(TCFloat * array,int count)4309 void LDrawModelViewer::cleanupFloats(TCFloat *array, int count)
4310 {
4311 int i;
4312
4313 for (i = 0; i < count; i++)
4314 {
4315 if (fabs(array[i]) < 1e-6)
4316 {
4317 array[i] = 0.0f;
4318 }
4319 }
4320 }
4321
4322 // Note: static method
checkAspect(TCFloat width,TCFloat height,int aspectW,int aspectH)4323 bool LDrawModelViewer::checkAspect(
4324 TCFloat width,
4325 TCFloat height,
4326 int aspectW,
4327 int aspectH)
4328 {
4329 return fabs(width * aspectH / aspectW - height) < 1e-5;
4330 }
4331
4332 // Note: static method
getAspectString(TCFloat width,TCFloat height,CUCSTR separator,bool standardOnly)4333 ucstring LDrawModelViewer::getAspectString(
4334 TCFloat width,
4335 TCFloat height,
4336 CUCSTR separator,
4337 bool standardOnly /*= false*/)
4338 {
4339 ucstring aspect;
4340 ucstring denom;
4341
4342 if (checkAspect(width, height, 235, 100))
4343 {
4344 aspect = _UC("2.35");
4345 denom = _UC("1");
4346 }
4347 else if (checkAspect(width, height, 1920, 817) ||
4348 checkAspect(width, height, 1280, 544))
4349 {
4350 aspect = _UC("~2.35");
4351 denom = _UC("1");
4352 }
4353 else if (checkAspect(width, height, 16, 9))
4354 {
4355 aspect = _UC("16");
4356 denom = _UC("9");
4357 }
4358 else if (checkAspect(width, height, 1136, 640) ||
4359 checkAspect(width, height, 1334, 750))
4360 {
4361 aspect = _UC("~16");
4362 denom = _UC("9");
4363 }
4364 else if (checkAspect(width, height, 5, 3))
4365 {
4366 aspect = _UC("5");
4367 denom = _UC("3");
4368 }
4369 else if (checkAspect(width, height, 16, 10))
4370 {
4371 aspect = _UC("16");
4372 denom = _UC("10");
4373 }
4374 else if (checkAspect(width, height, 3, 2))
4375 {
4376 aspect = _UC("3");
4377 denom = _UC("2");
4378 }
4379 else if (checkAspect(width, height, 4, 3))
4380 {
4381 aspect = _UC("4");
4382 denom = _UC("3");
4383 }
4384 else if (checkAspect(width, height, 5, 4))
4385 {
4386 aspect = _UC("5");
4387 denom = _UC("4");
4388 }
4389 else if (checkAspect(width, height, 21, 9))
4390 {
4391 aspect = _UC("21");
4392 denom = _UC("9");
4393 }
4394 else if (checkAspect(width, height, 64, 27))
4395 {
4396 aspect = _UC("~21");
4397 denom = _UC("9");
4398 }
4399 else if (checkAspect(width, height, 256, 135))
4400 {
4401 aspect = _UC("~1.9");
4402 denom = _UC("1");
4403 }
4404 else if (standardOnly)
4405 {
4406 return _UC("");
4407 }
4408 else
4409 {
4410 aspect = ftoucstr(width / height);
4411 }
4412 if (denom.length() > 0)
4413 {
4414 aspect += separator;
4415 aspect += denom;
4416 }
4417 return aspect;
4418 }
4419
getPovCameraInfo(UCCHAR * & userMessage,char * & povCamera)4420 void LDrawModelViewer::getPovCameraInfo(UCCHAR *&userMessage, char *&povCamera)
4421 {
4422 TCFloat tmpMatrix[16];
4423 TCFloat matrix[16];
4424 TCFloat lrotationMatrix[16];
4425 TCFloat centerMatrix[16];
4426 TCFloat positionMatrix[16];
4427 TCFloat cameraMatrix[16];
4428 TCFloat otherMatrix[16] = {1,0,0,0,0,-1,0,0,0,0,-1,0,0,0,0,1};
4429 UCCHAR locationString[1024];
4430 UCCHAR lookAtString[1204];
4431 UCCHAR upString[1024];
4432 UCCHAR message[4096];
4433 TCVector directionVector = TCVector(0.0f, 0.0f, 1.0f);
4434 TCVector locationVector;
4435 TCVector lookAtVector;
4436 TCVector upVector = TCVector(0.0f, -1.0f, 0.0f);
4437 double direction[3];
4438 double up[3];
4439 double location[3];
4440 LDLFacing facing;
4441 UCCHAR cameraString[4096];
4442 double lookAt[3];
4443 double tempV[3];
4444 ucstring povAspect;
4445
4446 if (!mainTREModel)
4447 {
4448 userMessage = NULL;
4449 povCamera = NULL;
4450 return;
4451 }
4452 TCVector cameraPosition = camera.getPosition();
4453
4454 memcpy(lrotationMatrix, getRotationMatrix(), sizeof(lrotationMatrix));
4455 TCVector::initIdentityMatrix(positionMatrix);
4456 positionMatrix[12] = cameraPosition[0] - getXPan();
4457 positionMatrix[13] = -cameraPosition[1] + getYPan();
4458 positionMatrix[14] = -cameraPosition[2];
4459 TCVector::initIdentityMatrix(centerMatrix);
4460 if (getAutoCenter())
4461 {
4462 centerMatrix[12] = center[0];
4463 centerMatrix[13] = center[1];
4464 centerMatrix[14] = center[2];
4465 }
4466 TCVector::multMatrix(otherMatrix, lrotationMatrix, tmpMatrix);
4467 TCVector::invertMatrix(tmpMatrix, cameraMatrix);
4468 TCVector::multMatrix(centerMatrix, cameraMatrix, tmpMatrix);
4469 TCVector::multMatrix(tmpMatrix, positionMatrix, matrix);
4470
4471 facing = camera.getFacing();
4472 facing[0] = -facing[0];
4473 facing.getInverseMatrix(cameraMatrix);
4474 TCVector::multMatrix(matrix, cameraMatrix, tmpMatrix);
4475 memcpy(matrix, tmpMatrix, sizeof(matrix));
4476 cleanupFloats(matrix);
4477 locationVector = TCVector(matrix[12], matrix[13], matrix[14]);
4478 location[0] = (double)matrix[12];
4479 location[1] = (double)matrix[13];
4480 location[2] = (double)matrix[14];
4481 cleanupFloats(matrix);
4482 // Note that the location accuracy isn't nearly as important as the
4483 // directional accuracy, so we don't have to re-do this string prior
4484 // to putting it on the clipboard in the POV code copy.
4485 sucprintf(locationString, COUNT_OF(locationString), _UC("%s,%s,%s"),
4486 ftoucstr(location[0]).c_str(), ftoucstr(location[1]).c_str(),
4487 ftoucstr(location[2]).c_str());
4488
4489 matrix[12] = matrix[13] = matrix[14] = 0.0f;
4490 directionVector = directionVector.transformPoint(matrix);
4491 upVector = upVector.transformPoint(matrix);
4492 // Grab the values prior to normalization. That will make the
4493 // normalization more accurate in double precision.
4494 directionVector.upConvert(direction);
4495 lookAtVector = locationVector + directionVector *
4496 locationVector.length();
4497 upVector.upConvert(up);
4498 directionVector = directionVector.normalize();
4499 upVector = upVector.normalize();
4500 cleanupFloats(directionVector, 3);
4501 cleanupFloats(upVector, 3);
4502 // The following 3 strings will get re-done later at higher accuracy
4503 // for POV-Ray.
4504 sucprintf(lookAtString, COUNT_OF(lookAtString), _UC("%s"),
4505 lookAtVector.ucstring().c_str());
4506 sucprintf(upString, COUNT_OF(upString), _UC("%s"),
4507 upVector.ucstring().c_str());
4508 sucprintf(message, COUNT_OF(message), ls(_UC("PovCameraMessage")),
4509 locationString, lookAtString, upString);
4510 TCVector::doubleNormalize(up);
4511 TCVector::doubleNormalize(direction);
4512 TCVector::doubleMultiply(direction, tempV,
4513 TCVector::doubleLength(location));
4514 TCVector::doubleAdd(location, tempV, lookAt);
4515 // Re-do the strings with higher accuracy, so they'll be
4516 // accepted by POV-Ray.
4517 sucprintf(upString, COUNT_OF(upString), _UC("%s,%s,%s"),
4518 ftoucstr(up[0], 20).c_str(), ftoucstr(up[1], 20).c_str(),
4519 ftoucstr(up[2], 20).c_str());
4520 sucprintf(lookAtString, COUNT_OF(lookAtString), _UC("%s,%s,%s"),
4521 ftoucstr(lookAt[0], 20).c_str(), ftoucstr(lookAt[1], 20).c_str(),
4522 ftoucstr(lookAt[2], 20).c_str());
4523 if (flags.povCameraAspect)
4524 {
4525 povAspect = getAspectString(width, height, _UC("/"));
4526 }
4527 else
4528 {
4529 povAspect = _UC("4/3");
4530 }
4531 sucprintf(cameraString, COUNT_OF(cameraString),
4532 _UC("camera\n")
4533 _UC("{\n")
4534 _UC("\t#declare ASPECT = %s;\n")
4535 _UC("\tlocation < %s >\n")
4536 _UC("\tsky < %s >\n")
4537 _UC("\tright ASPECT * < -1,0,0 >\n")
4538 _UC("\tlook_at < %s >\n")
4539 _UC("\tangle %g\n")
4540 _UC("}\n"),
4541 povAspect.c_str(), locationString, upString, lookAtString, getHFov());
4542 userMessage = copyString(message);
4543 povCamera = ucstringtombs(cameraString);
4544 }
4545
mouseDown(LDVMouseMode mode,int x,int y)4546 bool LDrawModelViewer::mouseDown(LDVMouseMode mode, int x, int y)
4547 {
4548 if ((mouseMode != LDVMouseNone && mouseMode != mode) ||
4549 mode == LDVMouseNone)
4550 {
4551 return false;
4552 }
4553 if (mode != LDVMouseLight)
4554 {
4555 return false;
4556 }
4557 if (mode == LDVMouseLight && !flags.useLighting)
4558 {
4559 // Allowing this will likely just lead to confusion.
4560 return false;
4561 }
4562 lastMouseX = x;
4563 lastMouseY = y;
4564 mouseMode = mode;
4565 switch (mouseMode)
4566 {
4567 case LDVMouseLight:
4568 if (haveStandardLight())
4569 {
4570 flags.showLight = true;
4571 requestRedraw();
4572 }
4573 break;
4574 default:
4575 break;
4576 }
4577 return true;
4578 }
4579
setShowLightDir(bool value)4580 void LDrawModelViewer::setShowLightDir(bool value)
4581 {
4582 if (value != flags.showLight)
4583 {
4584 flags.showLight = value;
4585 if (!value && preferences != NULL)
4586 {
4587 preferences->setLightVector(lightVector, true);
4588 }
4589 }
4590 }
4591
mouseUp(int x,int y)4592 bool LDrawModelViewer::mouseUp(int x, int y)
4593 {
4594 int deltaX = x - lastMouseX;
4595 int deltaY = y - lastMouseY;
4596
4597 if (mouseMode != LDVMouseLight)
4598 {
4599 debugPrintf("LDVMouseLight is the only mode currently supported.\n");
4600 return false;
4601 }
4602 lastMouseX = x;
4603 lastMouseY = y;
4604 switch (mouseMode)
4605 {
4606 case LDVMouseLight:
4607 if (haveStandardLight())
4608 {
4609 mouseMoveLight(deltaX, deltaY);
4610 preferences->setLightVector(lightVector, true);
4611 flags.showLight = false;
4612 requestRedraw();
4613 }
4614 break;
4615 default:
4616 break;
4617 }
4618 mouseMode = LDVMouseNone;
4619 return true;
4620 }
4621
mouseMove(int x,int y)4622 bool LDrawModelViewer::mouseMove(int x, int y)
4623 {
4624 int deltaX = x - lastMouseX;
4625 int deltaY = y - lastMouseY;
4626
4627 if (mouseMode != LDVMouseLight)
4628 {
4629 //debugPrintf("LDVMouseLight is the only mode currently supported.\n");
4630 return false;
4631 }
4632 lastMouseX = x;
4633 lastMouseY = y;
4634 switch (mouseMode)
4635 {
4636 case LDVMouseLight:
4637 mouseMoveLight(deltaX, deltaY);
4638 requestRedraw();
4639 break;
4640 default:
4641 break;
4642 }
4643 return true;
4644 }
4645
mouseMoveLight(int deltaX,int deltaY)4646 void LDrawModelViewer::mouseMoveLight(int deltaX, int deltaY)
4647 {
4648 TCFloat matrix[16];
4649 double lightScale = std::min(scale(width), scale(height)) / 10.0;
4650 double angle = deltaX / lightScale;
4651 TCVector newLightVector;
4652
4653 TCVector::initIdentityMatrix(matrix);
4654 matrix[0] = (float)cos(angle);
4655 matrix[2] = (float)-sin(angle);
4656 matrix[8] = (float)sin(angle);
4657 matrix[10] = (float)cos(angle);
4658 newLightVector = lightVector.transformPoint(matrix);
4659 angle = deltaY / lightScale;
4660 TCVector::initIdentityMatrix(matrix);
4661 matrix[5] = (float)cos(angle);
4662 matrix[6] = (float)sin(angle);
4663 matrix[9] = (float)-sin(angle);
4664 matrix[10] = (float)cos(angle);
4665 setLightVector(newLightVector.transformPoint(matrix));
4666 }
4667
getOpenGLDriverInfo(int & numExtensions)4668 UCSTR LDrawModelViewer::getOpenGLDriverInfo(int &numExtensions)
4669 {
4670 UCSTR vendorString = mbstoucstring((const char*)glGetString(GL_VENDOR));
4671 UCSTR rendererString = mbstoucstring((const char*)glGetString(GL_RENDERER));
4672 UCSTR versionString = mbstoucstring((const char*)glGetString(GL_VERSION));
4673 const char *extensionsString = (const char*)glGetString(GL_EXTENSIONS);
4674 UCSTR extensionsList;
4675 size_t len;
4676 UCSTR message;
4677
4678 numExtensions = 0;
4679 if (!vendorString)
4680 {
4681 vendorString = copyString(ls(_UC("*Unknown*")));
4682 }
4683 if (!rendererString)
4684 {
4685 rendererString = copyString(ls(_UC("*Unknown*")));
4686 }
4687 if (!versionString)
4688 {
4689 versionString = copyString(ls(_UC("*Unknown*")));
4690 }
4691 if (extensionsString)
4692 {
4693 char *temp = stringByReplacingSubstring(extensionsString, " ",
4694 "\r\n");
4695
4696 stripCRLF(temp);
4697 numExtensions = countStringLines(temp);
4698 extensionsList = mbstoucstring(temp);
4699 delete[] temp;
4700 }
4701 else
4702 {
4703 extensionsList = copyString(ls(_UC("*None*")));
4704 }
4705 len = ucstrlen(vendorString) + ucstrlen(rendererString) +
4706 ucstrlen(versionString) + ucstrlen(extensionsList) + 128;
4707 message = new UCCHAR[len];
4708 sucprintf(message, len, ls(_UC("OpenGlInfo")), vendorString, rendererString,
4709 versionString, extensionsList);
4710 delete[] vendorString;
4711 delete[] rendererString;
4712 delete[] versionString;
4713 delete[] extensionsList;
4714 return message;
4715 }
4716
getViewInfo(ucstring & message,ucstring & commandLine)4717 bool LDrawModelViewer::getViewInfo(ucstring &message, ucstring &commandLine)
4718 {
4719 if (mainModel)
4720 {
4721 TCFloat matrix[16];
4722 TCFloat otherMatrix[16] = {1,0,0,0,0,-1,0,0,0,0,-1,0,0,0,0,1};
4723 UCCHAR matrixString[1024];
4724 UCCHAR zoomString[128];
4725 UCCHAR messageBuf[4096];
4726 UCCHAR commandLineBuf[1024];
4727 TCFloat cameraDistance;
4728
4729 TCVector::multMatrix(otherMatrix, rotationMatrix, matrix);
4730 cleanupFloats(matrix);
4731 sucprintf(matrixString, COUNT_OF(matrixString),
4732 _UC("%.6g,%.6g,%.6g,%.6g,%.6g,%.6g,%.6g,%.6g,%.6g"), matrix[0],
4733 matrix[4], matrix[8], matrix[1], matrix[5], matrix[9],
4734 matrix[2], matrix[6], matrix[10]);
4735 cameraDistance = camera.getPosition().length();
4736 if (distanceMultiplier == 0.0f || cameraDistance == 0.0f)
4737 {
4738 // If we don't have a model, we don't know the default zoom, so
4739 // just say 1.
4740 ucstrcpy(zoomString, _UC("1"));
4741 }
4742 else
4743 {
4744 sucprintf(zoomString, COUNT_OF(zoomString), _UC("%.6g"),
4745 defaultDistance / distanceMultiplier / cameraDistance);
4746 }
4747 sucprintf(messageBuf, COUNT_OF(messageBuf), ls(_UC("ViewInfoMessage")),
4748 matrixString, zoomString);
4749 sucprintf(commandLineBuf, COUNT_OF(commandLineBuf),
4750 _UC("-DefaultMatrix=%s -DefaultZoom=%s"), matrixString, zoomString);
4751 message = messageBuf;
4752 commandLine = commandLineBuf;
4753 return true;
4754 }
4755 else
4756 {
4757 return false;
4758 }
4759 }
4760
getWideLineMargin(void)4761 TCFloat LDrawModelViewer::getWideLineMargin(void)
4762 {
4763 TCFloat margin = 0.0f;
4764
4765 if (flags.showsHighlightLines)
4766 {
4767 if (getScaledHighlightLineWidth() >= 2.0f)
4768 {
4769 margin = getScaledHighlightLineWidth() / 2.0f;
4770 }
4771 else
4772 {
4773 margin = 1.0f;
4774 }
4775 }
4776 if (flags.drawWireframe && getScaledWireframeLineWidth() > 1.0)
4777 {
4778 if (getScaledWireframeLineWidth() / 2.0f > margin)
4779 {
4780 margin = getScaledWireframeLineWidth() / 2.0f;
4781 }
4782 }
4783 return margin;
4784 }
4785
4786 //static int _numPoints = 0;
4787
4788 // This is conversion of Lars Hassing's auto camera code from L3P. It computes
4789 // the correct distance and pan amount for the camera so that the viewing
4790 // pyramid will be positioned in the closest possible position, such that the
4791 // model just touches the edges of the view on either the top and bottom, the
4792 // left and right, or all four.
4793 // After processing all the model data for the current camera angle, it ends up
4794 // with 6 equations with 6 unknowns. It uses a matrix solving routine to solve
4795 // these. The 6 values seem to be the X, Y, and Z coordinates of two points.
4796 // Once it has the values, it decides which point is the correct point, and then
4797 // uses that as the camera location.
zoomToFit(void)4798 void LDrawModelViewer::zoomToFit(void)
4799 {
4800 if (mainTREModel)
4801 {
4802 LDLAutoCamera *autoCamera = new LDLAutoCamera;
4803 char *cameraGlobe = TCUserDefaults::stringForKey(CAMERA_GLOBE_KEY, NULL,
4804 false);
4805
4806 autoCamera->setModel(getCurModel());
4807 autoCamera->setModelCenter(center);
4808 autoCamera->setRotationMatrix(rotationMatrix);
4809 autoCamera->setCamera(camera);
4810 autoCamera->setCameraGlobe(cameraGlobe);
4811 autoCamera->setDistanceMultiplier(distanceMultiplier);
4812 autoCamera->setWidth(scale(width) * numXTiles / getStereoWidthModifier());
4813 autoCamera->setHeight(scale(height) * numYTiles);
4814 autoCamera->setMargin(getWideLineMargin() * 2.0f);
4815 autoCamera->setFov(fov);
4816 autoCamera->setStep(step);
4817 autoCamera->setScanConditionalControlPoints(
4818 getShowConditionalControlPoints());
4819
4820 autoCamera->zoomToFit();
4821 camera.setPosition(autoCamera->getCamera().getPosition());
4822 xPan = 0.0f;
4823 yPan = 0.0f;
4824 TCObject::release(autoCamera);
4825 }
4826 }
4827
setStep(int value)4828 void LDrawModelViewer::setStep(int value)
4829 {
4830 step = value - 1;
4831 if (mainTREModel)
4832 {
4833 mainTREModel->setStep(step);
4834 }
4835 }
4836
getNumSteps(void) const4837 int LDrawModelViewer::getNumSteps(void) const
4838 {
4839 if (mainTREModel)
4840 {
4841 return mainTREModel->getNumSteps();
4842 }
4843 else
4844 {
4845 return 0;
4846 }
4847 }
4848
setLatLon(float lat,float lon,float distance)4849 void LDrawModelViewer::setLatLon(
4850 float lat,
4851 float lon,
4852 float distance /*= -1.0f*/)
4853 {
4854 fixLongitude(lon);
4855 if (viewMode == VMExamine && examineMode == EMLatLong)
4856 {
4857 examineLatitude = lat;
4858 examineLongitude = lon;
4859 }
4860 else if (rotationMatrix)
4861 {
4862 TCVector::calcRotationMatrix(lat, lon, rotationMatrix);
4863 flags.needsRotationMatrixSetup = true;
4864 }
4865 if (distance >= 0.0f)
4866 {
4867 camera.setPosition(TCVector(0.0, 0.0, distance));
4868 flags.constrainZoom = false;
4869 }
4870 else
4871 {
4872 flags.constrainZoom = viewMode == VMExamine;
4873 }
4874 requestRedraw();
4875 }
4876
enable(GLenum cap)4877 void LDrawModelViewer::enable(GLenum cap)
4878 {
4879 if (getGl2ps())
4880 {
4881 GLint mode = GL2PS_BLEND;
4882
4883 if (cap == GL_POLYGON_OFFSET_FILL)
4884 {
4885 mode = GL2PS_POLYGON_OFFSET_FILL;
4886 }
4887 gl2psEnable(mode);
4888 }
4889 glEnable(cap);
4890 }
4891
disable(GLenum cap)4892 void LDrawModelViewer::disable(GLenum cap)
4893 {
4894 if (getGl2ps())
4895 {
4896 GLint mode = GL2PS_BLEND;
4897
4898 if (cap == GL_POLYGON_OFFSET_FILL)
4899 {
4900 mode = GL2PS_POLYGON_OFFSET_FILL;
4901 }
4902 gl2psDisable(mode);
4903 }
4904 glDisable(cap);
4905 }
4906
blendFunc(GLenum sfactor,GLenum dfactor)4907 void LDrawModelViewer::blendFunc(GLenum sfactor, GLenum dfactor)
4908 {
4909 if (getGl2ps())
4910 {
4911 gl2psBlendFunc(sfactor, dfactor);
4912 }
4913 glBlendFunc(sfactor, dfactor);
4914 }
4915
lineWidth(GLfloat lwidth)4916 void LDrawModelViewer::lineWidth(GLfloat lwidth)
4917 {
4918 if (getGl2ps())
4919 {
4920 gl2psLineWidth(lwidth);
4921 }
4922 glLineWidth(lwidth);
4923 }
4924
setMpdChildIndex(int index)4925 void LDrawModelViewer::setMpdChildIndex(int index)
4926 {
4927 if (index != mpdChildIndex)
4928 {
4929 mpdChildIndex = index;
4930 flags.needsReparse = true;
4931 flags.needsCalcSize = true;
4932 flags.needsViewReset = true;
4933 flags.needsResetStep = true;
4934 requestRedraw();
4935 }
4936 }
4937
getMpdChild(void)4938 LDLModel *LDrawModelViewer::getMpdChild(void)
4939 {
4940 if (mainModel == NULL)
4941 {
4942 return NULL;
4943 }
4944 LDLModelVector &mpdModels = mainModel->getMpdModels();
4945
4946 if (mpdModels.size() > 0 && mpdName.size() > 0)
4947 {
4948 for (size_t i = 0; i < mpdModels.size(); i++)
4949 {
4950 if (strcasecmp(mpdModels[i]->getName(), mpdName.c_str()) == 0)
4951 {
4952 mpdChildIndex = (int)i;
4953 mpdName = "";
4954 break;
4955 }
4956 }
4957 }
4958 if ((int)mpdModels.size() > mpdChildIndex && mpdChildIndex >= 0)
4959 {
4960 return mpdModels[mpdChildIndex];
4961 }
4962 else
4963 {
4964 return NULL;
4965 }
4966 }
4967
getMpdChild(void) const4968 const LDLModel *LDrawModelViewer::getMpdChild(void) const
4969 {
4970 return const_cast<LDrawModelViewer *>(this)->getMpdChild();
4971 }
4972
getCurModel(void)4973 LDLModel *LDrawModelViewer::getCurModel(void)
4974 {
4975 LDLModel *curModel = getMpdChild();
4976
4977 if (curModel == NULL)
4978 {
4979 curModel = mainModel;
4980 }
4981 return curModel;
4982 }
4983
getCurModel(void) const4984 const LDLModel *LDrawModelViewer::getCurModel(void) const
4985 {
4986 return const_cast<LDrawModelViewer *>(this)->getCurModel();
4987 }
4988
initExporter(void)4989 LDExporter *LDrawModelViewer::initExporter(void)
4990 {
4991 if (exporter == NULL)
4992 {
4993 switch (exportType)
4994 {
4995 case ETPov:
4996 exporter = new LDPovExporter;
4997 break;
4998 #ifdef EXPORT_LDR
4999 case ETLdr:
5000 exporter = new LDLdrExporter;
5001 break;
5002 #endif // EXPORT_LDR
5003 case ETStl:
5004 exporter = new LDStlExporter;
5005 break;
5006 #ifdef EXPORT_3DS
5007 case ET3ds:
5008 exporter = new LD3dsExporter;
5009 break;
5010 #endif // EXPORT_3DS
5011 default:
5012 exporter = NULL;
5013 break;
5014 }
5015 }
5016 return exporter;
5017 }
5018
setExportType(ExportType type,bool forceNew)5019 void LDrawModelViewer::setExportType(ExportType type, bool forceNew /*= false*/)
5020 {
5021 if (forceNew || (type != exportType && type != (ExportType)0))
5022 {
5023 if (type != (ExportType)0)
5024 {
5025 exportType = type;
5026 }
5027 TCObject::release(exporter);
5028 exporter = NULL;
5029 }
5030 initExporter();
5031 }
5032
getExporter(ExportType type,bool forceNew)5033 LDExporter *LDrawModelViewer::getExporter(
5034 ExportType type /*= (ExportType)0*/,
5035 bool forceNew /*= false*/)
5036 {
5037 setExportType(type, forceNew);
5038 return initExporter();
5039 }
5040
exportCurModel(const char * lfilename,const char * version,const char * copyright,ExportType type)5041 int LDrawModelViewer::exportCurModel(
5042 const char *lfilename,
5043 const char *version /*= NULL*/,
5044 const char *copyright /*= NULL*/,
5045 ExportType type /*= (ExportType)0*/)
5046 {
5047 LDLModel *model = getCurModel();
5048 int retValue = 1;
5049
5050 try
5051 {
5052 if (model != NULL)
5053 {
5054 setExportType(type);
5055 TCObject::release(exporter);
5056 exporter = NULL;
5057 if (initExporter() != NULL)
5058 {
5059 exporter->setBoundingBox(boundingMin, boundingMax);
5060 exporter->setCenter(center);
5061 exporter->setWidth(width);
5062 exporter->setHeight(height);
5063 exporter->setRadius(size / 2.0f);
5064 exporter->setBackgroundColor(backgroundR, backgroundG,
5065 backgroundB);
5066 exporter->setCamera(camera);
5067 exporter->setRotationMatrix(rotationMatrix);
5068 exporter->setFov(fov);
5069 exporter->setXPan(xPan);
5070 exporter->setYPan(yPan);
5071 exporter->setAppUrl("https://tcobbs.github.io/ldview/");
5072 exporter->setAppName("LDView");
5073 if (!sm_appVersion.empty())
5074 {
5075 exporter->setAppVersion(sm_appVersion.c_str());
5076 }
5077 else if (version != NULL)
5078 {
5079 exporter->setAppVersion(version);
5080 }
5081 if (!sm_appCopyright.empty())
5082 {
5083 exporter->setAppCopyright(sm_appCopyright.c_str());
5084 }
5085 else if (copyright)
5086 {
5087 exporter->setAppCopyright(copyright);
5088 }
5089 if (lfilename != NULL)
5090 {
5091 exporter->setFilename(lfilename);
5092 }
5093 if (exporter->usesLDLModel())
5094 {
5095 if (exporter->usesTREModel())
5096 {
5097 retValue = exporter->doExport(model, mainTREModel);
5098 }
5099 else
5100 {
5101 retValue = exporter->doExport(model);
5102 }
5103 }
5104 else
5105 {
5106 retValue = exporter->doExport(mainTREModel);
5107 }
5108 exporter->release();
5109 exporter = NULL;
5110 }
5111 }
5112 }
5113 catch (char const* exception)
5114 {
5115 debugPrintf("Exception during export: %s\n", exception);
5116 // Until we actually handle this, leave the exception alone.
5117 throw exception;
5118 }
5119 return retValue;
5120 }
5121
getCurFilename(void) const5122 std::string LDrawModelViewer::getCurFilename(void) const
5123 {
5124 const LDLModel *curModel = getCurModel();
5125 const char *lfilename = getFilename();
5126
5127 if (curModel == mainModel)
5128 {
5129 return lfilename;
5130 }
5131 else
5132 {
5133 const char *modelName = curModel->getName();
5134 std::string retValue = directoryFromPath(lfilename);
5135 char *temp;
5136
5137 retValue += '/';
5138 retValue += modelName;
5139 temp = cleanedUpPath(retValue.c_str());
5140 retValue = temp;
5141 delete[] temp;
5142 return retValue;
5143 }
5144 }
5145
5146 // Note: static method
addStandardSize(int width,int height,CUCSTR name)5147 void LDrawModelViewer::addStandardSize(int width, int height, CUCSTR name)
5148 {
5149 StandardSize standardSize;
5150 ucstring buf = ltoucstr(width);
5151 ucstring aspectString = getAspectString(width, height, _UC(":"), true);
5152
5153 standardSize.width = width;
5154 standardSize.height = height;
5155 standardSizes.push_back(standardSize);
5156 buf += _UC(" x ");
5157 buf += ltoucstr(height);
5158 if (aspectString.length() > 0)
5159 {
5160 buf += _UC(" (");
5161 buf += aspectString.c_str();
5162 if (name != NULL)
5163 {
5164 buf += _UC(" \"");
5165 buf += name;
5166 buf += _UC("\"");
5167 }
5168 buf += _UC(")");
5169 }
5170 standardSizes.back().name = buf;
5171 }
5172
5173 // Note: static method
initStandardSizes(void)5174 void LDrawModelViewer::initStandardSizes(void)
5175 {
5176 if (standardSizes.size() == 0)
5177 {
5178 addStandardSize(640, 480, _UC("VGA"));
5179 // Note: 720 width has all heights for "standard" aspect ratios.
5180 addStandardSize(720, 306); // 2.35:1
5181 addStandardSize(720, 405); // 16:9
5182 addStandardSize(720, 432); // 5:3
5183 addStandardSize(720, 450); // 16:10
5184 addStandardSize(720, 480); // 3:2
5185 addStandardSize(720, 540); // 4:3
5186 addStandardSize(720, 576); // 5:4
5187 addStandardSize(800, 450);
5188 addStandardSize(800, 480);
5189 addStandardSize(800, 600, _UC("SVGA"));
5190 addStandardSize(960, 640); // 3.5" Retina iPhone
5191 addStandardSize(1024, 576);
5192 addStandardSize(1024, 768, _UC("XGA"));
5193 addStandardSize(1136, 640); // 4" Retina iPhone
5194 addStandardSize(1152, 864);
5195 addStandardSize(1280, 544);
5196 addStandardSize(1280, 720, _UC("HD"));
5197 addStandardSize(1280, 768);
5198 addStandardSize(1280, 960);
5199 addStandardSize(1280, 1024);
5200 addStandardSize(1334, 750); // 4.7" Retina iPhone
5201 addStandardSize(1600, 1200);
5202 addStandardSize(1680, 1050);
5203 addStandardSize(1792, 828); // 6.1" Retina iPhone Xr
5204 addStandardSize(1920, 817);
5205 addStandardSize(1920, 1080, _UC("FHD")); // "1080p"
5206 addStandardSize(1920, 1200); // "WUXGA"
5207 addStandardSize(1920, 1440); // 4:3
5208 addStandardSize(2048, 1080, _UC("DCI 2K"));
5209 addStandardSize(2048, 1536); // Retina iPad
5210 addStandardSize(2160, 1440); // Surface Pro 3
5211 addStandardSize(2208, 1242); // 5.5" Retina iPhone
5212 addStandardSize(2436, 1125); // 5.8" Retina iPhone X
5213 addStandardSize(2520, 1080); // "2.5Kx1K"
5214 addStandardSize(2560, 1080); // "2.5Kx1K"
5215 addStandardSize(2560, 1440); // "1440p"
5216 addStandardSize(2560, 1600); // 13" Retina MacBook Pro
5217 addStandardSize(2560, 1920); // 4:3
5218 addStandardSize(2688, 1242); // 6.5" Retina iPhone Xs Max
5219 addStandardSize(2736, 1824); // Surface Pro 4
5220 addStandardSize(2880, 1800); // 15" Retina MacBook Pro
5221 addStandardSize(3840, 2160, _UC("UHD")); // "2160p" or "4K"
5222 addStandardSize(3840, 2400); // "WQUXGA"
5223 addStandardSize(4096, 2160, _UC("DCI 4K"));
5224 addStandardSize(5120, 2160, _UC("5Kx2K"));
5225 addStandardSize(5120, 2880, _UC("5K"));
5226 addStandardSize(7680, 4320, _UC("8K")); // "4320p"
5227 addStandardSize(8192, 4320, _UC("DCI 8K"));
5228 }
5229 }
5230
5231 // Note: static method
getStandardSizes(int maxWidth,int maxHeight,StandardSizeVector & sizes)5232 void LDrawModelViewer::getStandardSizes(
5233 int maxWidth,
5234 int maxHeight,
5235 StandardSizeVector &sizes)
5236 {
5237 initStandardSizes();
5238 sizes.clear();
5239 for (StandardSizeList::const_iterator it = standardSizes.begin();
5240 it != standardSizes.end(); ++it)
5241 {
5242 const StandardSize &standardSize = *it;
5243
5244 if (standardSize.width <= maxWidth && standardSize.height <= maxHeight)
5245 {
5246 sizes.push_back(standardSize);
5247 }
5248 }
5249 }
5250
getDistance(void) const5251 TCFloat LDrawModelViewer::getDistance(void) const
5252 {
5253 return camera.getPosition().length();
5254 }
5255
setCameraMotion(const TCVector & value)5256 void LDrawModelViewer::setCameraMotion(const TCVector &value)
5257 {
5258 cameraMotion = value;
5259 updateFrameTime();
5260 }
5261
setCameraXRotate(TCFloat value)5262 void LDrawModelViewer::setCameraXRotate(TCFloat value)
5263 {
5264 cameraXRotate = value;
5265 updateFrameTime();
5266 }
5267
setCameraYRotate(TCFloat value)5268 void LDrawModelViewer::setCameraYRotate(TCFloat value)
5269 {
5270 cameraYRotate = value;
5271 updateFrameTime();
5272 }
5273
setCameraZRotate(TCFloat value)5274 void LDrawModelViewer::setCameraZRotate(TCFloat value)
5275 {
5276 cameraZRotate = value;
5277 updateFrameTime();
5278 }
5279
setHighlightPaths(std::string value)5280 void LDrawModelViewer::setHighlightPaths(std::string value)
5281 {
5282 highlightPaths.clear();
5283 while (value.size() > 0)
5284 {
5285 size_t index = value.find('\n');
5286 std::string line;
5287
5288 if (index < value.size())
5289 {
5290 line = value.substr(0, index);
5291 value = value.substr(index + 1);
5292 }
5293 else
5294 {
5295 line = value;
5296 value = "";
5297 }
5298 if (line.size() > 0)
5299 {
5300 highlightPaths.push_back(line);
5301 }
5302 }
5303 highlightPathsChanged();
5304 }
5305
setHighlightPaths(const StringList & value)5306 void LDrawModelViewer::setHighlightPaths(const StringList &value)
5307 {
5308 highlightPaths = value;
5309 highlightPathsChanged();
5310 }
5311
attachFileLine(LDLFileLine * dstFileLine,LDLFileLineArray * dstFileLines,LDLModel * dstModel)5312 void LDrawModelViewer::attachFileLine(
5313 LDLFileLine *dstFileLine,
5314 LDLFileLineArray *dstFileLines,
5315 LDLModel *dstModel)
5316 {
5317 if (dstFileLine)
5318 {
5319 dstFileLine->setLineNumber(dstFileLines->getCount());
5320 dstFileLine->setParentModel(dstModel);
5321 dstFileLines->addObject(dstFileLine);
5322 dstModel->setActiveLineCount(dstModel->getActiveLineCount() + 1);
5323 dstFileLine->release();
5324 }
5325 }
5326
attachLineLine(LDLFileLineArray * dstFileLines,LDLModel * dstModel,const TCVector & pt0,const TCVector & pt1)5327 void LDrawModelViewer::attachLineLine(
5328 LDLFileLineArray *dstFileLines,
5329 LDLModel *dstModel,
5330 const TCVector &pt0,
5331 const TCVector &pt1)
5332 {
5333 char line[1024];
5334 LDLLineLine *dstFileLine;
5335
5336 sprintf(line, "2 16 %s %s", pt0.string().c_str(), pt1.string().c_str());
5337 dstFileLine = new LDLLineLine(dstModel, line, dstFileLines->getCount());
5338 dstFileLine->parse();
5339 attachFileLine(dstFileLine, dstFileLines, dstModel);
5340 }
5341
resetColors(LDLModel * model)5342 void LDrawModelViewer::resetColors(LDLModel *model)
5343 {
5344 if (model != NULL)
5345 {
5346 LDLFileLineArray *fileLines = model->getFileLines();
5347
5348 if (fileLines != NULL)
5349 {
5350 int count = model->getActiveLineCount();
5351
5352 for (int i = 0; i < count; i++)
5353 {
5354 resetColors((*fileLines)[i]);
5355 }
5356 }
5357 }
5358 }
5359
resetColors(LDLFileLine * fileLine)5360 void LDrawModelViewer::resetColors(LDLFileLine *fileLine)
5361 {
5362 if (fileLine != NULL && fileLine->isActionLine())
5363 {
5364 LDLActionLine *actionLine = (LDLActionLine *)fileLine;
5365
5366 if (actionLine->getColorNumber() != 24)
5367 {
5368 actionLine->setColorNumber(16);
5369 }
5370 if (actionLine->getLineType() == LDLLineTypeModel)
5371 {
5372 resetColors(((LDLModelLine *)actionLine)->getLowResModel());
5373 resetColors(((LDLModelLine *)actionLine)->getModel(true));
5374 }
5375 }
5376 }
5377
parseHighlightPath(const std::string & path,const LDLModel * srcModel,LDLModel * dstModel,const std::string & prePath,int pathNum)5378 void LDrawModelViewer::parseHighlightPath(
5379 const std::string &path,
5380 const LDLModel *srcModel,
5381 LDLModel *dstModel,
5382 const std::string &prePath,
5383 int pathNum)
5384 {
5385 int lineNum = atoi(&path[1]) - 1;
5386 const LDLFileLineArray *srcFileLines = srcModel->getFileLines();
5387
5388 if (lineNum < srcFileLines->getCount())
5389 {
5390 const LDLFileLine *srcFileLine = (*srcFileLines)[lineNum];
5391 LDLFileLineArray *dstFileLines = dstModel->getFileLines(true);
5392 LDLFileLine *dstFileLine = NULL;
5393 bool isModel = false;
5394 size_t nextSlash = path.find('/', 1);
5395
5396 switch (srcFileLine->getLineType())
5397 {
5398 case LDLLineTypeModel:
5399 isModel = true;
5400 if (nextSlash < path.size())
5401 {
5402 const LDLModelLine *srcModelLine =
5403 (const LDLModelLine *)srcFileLine;
5404 // The names need to be unique to prevent them from being
5405 // combined. Since we're not showing the actual model, we
5406 // use a combination of our pathNum and the path to now as
5407 // the model name.
5408 std::string name = ltostr(pathNum) + prePath + '/';
5409
5410 name += ltostr(lineNum + 1);
5411 dstFileLine = new LDLModelLine(dstModel, srcFileLine->getLine(),
5412 dstFileLines->getCount(), srcFileLine->getOriginalLine());
5413 LDLModelLine *dstModelLine = (LDLModelLine *)dstFileLine;
5414 dstModelLine->setMatrix(srcModelLine->getMatrix());
5415 if (srcModelLine->getLowResModel() != NULL)
5416 {
5417 dstModelLine->createLowResModel(dstModel->getMainModel(),
5418 name.c_str());
5419 }
5420 if (srcModelLine->getHighResModel() != NULL)
5421 {
5422 dstModelLine->createHighResModel(dstModel->getMainModel(),
5423 name.c_str());
5424 }
5425 const LDLModel *srcHighResModel =
5426 srcModelLine->getHighResModel();
5427 if (srcHighResModel->isPart() && !srcModel->isPart() &&
5428 seamWidth > 0.0)
5429 {
5430 // We have to apply seams manually here, because TRE, which
5431 // normally handles the seams, does so based on the actual
5432 // geometry, not the LDLModel's bounding box. Since only a
5433 // subset of the geometry makes it to TRE, we have to do the
5434 // seams adjustment here instead.
5435 TCFloat scaleMatrix[16];
5436 TCFloat newMatrix[16];
5437
5438 TCVector lboundingMin, lboundingMax;
5439 srcHighResModel->getBoundingBox(lboundingMin, lboundingMax);
5440 TCVector::calcScaleMatrix(seamWidth, scaleMatrix,
5441 lboundingMin, lboundingMax);
5442 TCVector::multMatrix(dstModelLine->getMatrix(), scaleMatrix,
5443 newMatrix);
5444 dstModelLine->setMatrix(newMatrix);
5445 dstModelLine->setColorNumber(16);
5446 }
5447 }
5448 else
5449 {
5450 dstFileLine = (LDLFileLine *)srcFileLine->copy();
5451 resetColors((LDLModelLine *)dstFileLine);
5452 }
5453 break;
5454 case LDLLineTypeLine:
5455 case LDLLineTypeTriangle:
5456 case LDLLineTypeQuad:
5457 dstFileLine = (LDLFileLine *)srcFileLine->copy();
5458 break;
5459 case LDLLineTypeConditionalLine:
5460 {
5461 LDLConditionalLineLine *condLine =
5462 (LDLConditionalLineLine *)srcFileLine;
5463
5464 attachLineLine(dstFileLines, dstModel, condLine->getPoints()[0],
5465 condLine->getPoints()[1]);
5466 attachLineLine(dstFileLines, dstModel, condLine->getPoints()[0],
5467 condLine->getControlPoints()[0]);
5468 attachLineLine(dstFileLines, dstModel, condLine->getPoints()[0],
5469 condLine->getControlPoints()[1]);
5470 }
5471 break;
5472 default:
5473 // Don't do anything for other line types.
5474 break;
5475 }
5476 if (!isModel)
5477 {
5478 if (nextSlash < path.size())
5479 {
5480 throw "Invalid Tree Path";
5481 }
5482 }
5483 attachFileLine(dstFileLine, dstFileLines, dstModel);
5484 if (isModel)
5485 {
5486 if (nextSlash < path.size())
5487 {
5488 LDLModelLine *dstModelLine = (LDLModelLine *)dstFileLine;
5489
5490 if (dstModelLine->getLowResModel())
5491 {
5492 parseHighlightPath(path.substr(nextSlash),
5493 ((LDLModelLine *)srcFileLine)->getModel(),
5494 dstModelLine->getLowResModel(),
5495 prePath + path.substr(0, nextSlash),
5496 pathNum);
5497 }
5498 if (dstModelLine->getHighResModel())
5499 {
5500 parseHighlightPath(path.substr(nextSlash),
5501 ((LDLModelLine *)srcFileLine)->getModel(),
5502 dstModelLine->getHighResModel(),
5503 prePath + path.substr(0, nextSlash),
5504 pathNum);
5505 }
5506 }
5507 }
5508 }
5509 }
5510
setHighlightColor(int r,int g,int b,bool redraw)5511 void LDrawModelViewer::setHighlightColor(
5512 int r,
5513 int g,
5514 int b,
5515 bool redraw /*= true*/)
5516 {
5517 highlightR = r & 0xFF;
5518 highlightG = g & 0xFF;
5519 highlightB = b & 0xFF;
5520 if (redraw)
5521 {
5522 highlightPathsChanged();
5523 }
5524 }
5525
adjustHighlightPath(std::string path,LDLModel * mpdChild)5526 std::string LDrawModelViewer::adjustHighlightPath(
5527 std::string path,
5528 LDLModel *mpdChild)
5529 {
5530 LDLModel *curModel = mainModel;
5531
5532 while (curModel != mpdChild && path.size() > 0)
5533 {
5534 int lineNum = atoi(&path[1]) - 1;
5535 const LDLFileLineArray *fileLines = curModel->getFileLines();
5536
5537 if (lineNum < fileLines->getCount())
5538 {
5539 const LDLFileLine *fileLine = (*fileLines)[lineNum];
5540 size_t index;
5541
5542 if (fileLine->getLineType() != LDLLineTypeModel)
5543 {
5544 return "";
5545 }
5546 curModel = ((LDLModelLine *)fileLine)->getModel();
5547 index = path.find('/', 1);
5548 if (index < path.size())
5549 {
5550 path = path.substr(index);
5551 }
5552 }
5553 else
5554 {
5555 return "";
5556 }
5557 }
5558 return path;
5559 }
5560
highlightPathsChanged(void)5561 void LDrawModelViewer::highlightPathsChanged(void)
5562 {
5563 TCObject::release(highlightModel);
5564 highlightModel = NULL;
5565 if (highlightPaths.size() > 0)
5566 {
5567 LDModelParser *modelParser = NULL;
5568 LDLMainModel *ldlModel = new LDLMainModel;
5569 int i = 0;
5570 LDLModel *mpdChild = getMpdChild();
5571 int highlightColorNumber = 0x3000000 | (highlightR << 16) |
5572 (highlightG << 8) | highlightB;
5573
5574 ldlModel->setMainModel(ldlModel);
5575 ldlModel->setForceHighlightColor(true);
5576 ldlModel->setHighlightColorNumber(highlightColorNumber);
5577 ldlModel->setLowResStuds(!flags.qualityStuds);
5578 ldlModel->setTexmaps(false);
5579 for (StringList::const_iterator it = highlightPaths.begin();
5580 it != highlightPaths.end(); ++it)
5581 {
5582 if (mpdChild != NULL)
5583 {
5584 std::string path = adjustHighlightPath(*it, mpdChild);
5585 if (path.size() > 0)
5586 {
5587 parseHighlightPath(path, mpdChild, ldlModel, "", i++);
5588 }
5589 }
5590 else
5591 {
5592 parseHighlightPath(*it, mainModel, ldlModel, "", i++);
5593 }
5594 }
5595 modelParser = new LDModelParser(this);
5596 modelParser->setTexmapsFlag(false);
5597 modelParser->setIsHighlightModel(true);
5598 modelParser->setDefaultColorNumber(highlightColorNumber);
5599 modelParser->setFlattenPartsFlag(false);
5600 if (modelParser->parseMainModel(ldlModel))
5601 {
5602 highlightModel = modelParser->getMainTREModel();
5603 if (TCUserDefaults::boolForKey(HIGHLIGHT_MODEL_EDGES_NO_DEPTH_KEY,
5604 true))
5605 {
5606 highlightModel->setNoDepthEdgeLinesFlag(true);
5607 }
5608 highlightModel->retain();
5609 highlightModel->setSaveAlphaFlag(false);
5610 }
5611 ldlModel->release();
5612 modelParser->release();
5613 }
5614 requestRedraw();
5615 }
5616
5617 // Note: static method
resetUnofficialDownloadTimes(void)5618 void LDrawModelViewer::resetUnofficialDownloadTimes(void)
5619 {
5620 TCUserDefaults::removeValueGroup("UnofficialPartChecks", false);
5621 }
5622
getCameraLocation(void)5623 TCVector LDrawModelViewer::getCameraLocation(void)
5624 {
5625 TCFloat transformationMatrix[16];
5626 TCVector::invertMatrix(rotationMatrix, transformationMatrix);
5627 TCVector cameraPosition = camera.getPosition().transformPoint(
5628 transformationMatrix) + center;
5629 for (int i = 0; i < 3; ++i)
5630 {
5631 if (fEq(cameraPosition[i], 0.0))
5632 {
5633 cameraPosition[i] = 0.0;
5634 }
5635 }
5636 return cameraPosition;
5637 }
5638
setCameraLocation(const TCVector & newLocation,LDVLookAt lookAt,bool shouldRequestRedraw)5639 void LDrawModelViewer::setCameraLocation(
5640 const TCVector& newLocation,
5641 LDVLookAt lookAt,
5642 bool shouldRequestRedraw)
5643 {
5644 TCVector transLoc = (newLocation - center).transformPoint(
5645 rotationMatrix);
5646 camera.setPosition(transLoc);
5647 if (lookAt != LDVLookAtNone)
5648 {
5649 TCVector lookAtPoint;
5650 if (lookAt == LDVLookAtOrigin)
5651 {
5652 // I know this seems backwards, but it is this way due to the fact
5653 // that center is always added to and subtracted from the camera
5654 // location before we get to the point of aiming the camera. Becaus
5655 // of that, we have to seemingly aim it at the center in order to
5656 // really aim it at the origin, and vice versa.
5657 lookAtPoint = center;
5658 }
5659 LDLFacing facing;
5660 facing.pointAt(transLoc - lookAtPoint);
5661 camera.setFacing(facing);
5662 rightSideUp(shouldRequestRedraw);
5663 }
5664 else if (shouldRequestRedraw)
5665 {
5666 requestRedraw();
5667 }
5668 }
5669
rotationCenterChanged(void)5670 void LDrawModelViewer::rotationCenterChanged(void)
5671 {
5672 flags.needsReparse = true;
5673 flags.needsCalcSize = true;
5674 flags.needsViewReset = true;
5675 requestRedraw();
5676 }
5677
setRotationCenter(const TCVector & value)5678 void LDrawModelViewer::setRotationCenter(const TCVector& value)
5679 {
5680 std::string key = getModelKey("RotationCenter");
5681 std::string valueString = value.defaultsString();
5682 TCUserDefaults::setStringForKey(valueString.c_str(), key.c_str(), false);
5683 rotationCenterChanged();
5684 }
5685
resetRotationCenter(void)5686 void LDrawModelViewer::resetRotationCenter(void)
5687 {
5688 std::string key = getModelKey("RotationCenter");
5689 TCUserDefaults::removeValue(key.c_str());
5690 rotationCenterChanged();
5691 }
5692
getModelKey(const std::string & keyName)5693 std::string LDrawModelViewer::getModelKey(const std::string& keyName)
5694 {
5695 std::string key = "Models/";
5696 if (filename == NULL)
5697 {
5698 key += "Unknown";
5699 }
5700 else
5701 {
5702 std::string mangledFilename = filename;
5703 replaceStringCharacter(&mangledFilename[0], '/', ':');
5704 key += mangledFilename;
5705 }
5706 return key + "/" + keyName;
5707 }
5708
getRotationCenter(TCVector & rotationCenter)5709 bool LDrawModelViewer::getRotationCenter(TCVector& rotationCenter)
5710 {
5711 std::string key = getModelKey("RotationCenter");
5712 char* centerString = TCUserDefaults::stringForKey(key.c_str(), NULL, false);
5713 bool result = false;
5714 if (centerString != NULL)
5715 {
5716 try {
5717 rotationCenter = TCVector(centerString);
5718 result = true;
5719 } catch (...) {
5720 // Ignore.
5721 }
5722 delete[] centerString;
5723 }
5724 return result;
5725 }
5726