ecere/gui/Window: Prevent uninitialized values if base Window methods not overridden...
[sdk] / ecere / src / gfx / Display.ec
1 namespace gfx;
2
3 #if (defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER)) && defined(__WIN32__)
4 #define ECERE_NOTRUETYPE
5 #endif
6
7 import "System"
8
9 import "Color"
10 import "Bitmap"
11 import "Surface"
12 import "DisplaySystem"
13 import "Resource"
14 import "FontResource"
15 import "BitmapResource"
16
17 import "LFBDisplayDriver"
18
19 // TOFIX: Temporary until we pass Display instead of DisplaySystem to FontExtent
20 #if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE)
21 import "GDIDisplayDriver"
22 #endif
23
24 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
25 import "Camera"
26 import "Plane"
27 import "Matrix"
28 import "Mesh"
29 import "Object"
30 import "Quaternion"
31 import "Vector3D"
32 #endif
33
34 #if (!defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER))
35 import "OpenGLDisplayDriver"
36
37 #define near _near
38 #define far _far
39 #include "gl123es.h"
40 #undef near
41 #undef far
42 #endif
43
44 public class GLCapabilities : uint
45 {
46 public:
47    // Expect reloading graphics
48    bool compatible      :1;
49    bool vertexBuffer    :1;
50    bool quads           :1;
51    bool intAndDouble    :1;
52    bool legacyFormats   :1;
53    bool nonPow2Textures :1;
54    bool vertexPointer   :1;
55
56    // Should be able to toggle without reloading
57    bool legacy          :1;
58    bool shaders         :1;
59    bool fixedFunction   :1;
60    bool immediate       :1;
61    bool frameBuffer     :1;
62    bool pointSize       :1;
63    bool vao             :1;
64    bool select          :1;
65    // bool mapBuffer       :1;
66
67    bool debug           :1;
68 };
69
70 public enum RenderState { fillMode = 1, depthTest, depthWrite, fogDensity, fogColor, blend, ambient, alphaWrite, antiAlias, vSync };
71
72 public union RenderStateFloat { float f; uint ui; };
73
74 public enum FillModeValue { solid, wireframe };
75
76 public class DisplayFlags
77 {
78    public bool fullScreen:1, flipping:1, alpha:1, memBackBuffer:1, text:1, scrolling:1, printer:1;
79 };
80 public class FontFlags
81 {
82    public bool bold:1, italic:1, underline:1;
83 };
84
85 __attribute__((unused)) static void DummyFunction()
86 {
87 #if !defined(__EMSCRIPTEN__)
88    Mutex { };
89 #endif
90 }
91
92 public class DisplayDriver
93 {
94 public:
95    class_data const char * name;
96    class_data bool textMode;
97    class_data bool printer;
98    class_data DisplaySystem displaySystem;
99
100    class_property DisplaySystem displaySystem
101    {
102       set { class_data(displaySystem) = value; }
103       get { return class_data(displaySystem); }
104    };
105
106    class_property const char * name
107    {
108       set { class_data(name) = value; }
109       get { return class_data(name); }
110    };
111
112    class_property bool printer
113    {
114       set { class_data(printer) = value; }
115       get { return class_data(printer); }
116    };
117
118    // Constructor / Destructor
119    virtual bool ::CreateDisplaySystem(DisplaySystem);
120    virtual void ::DestroyDisplaySystem(DisplaySystem);
121
122    virtual bool ::CreateDisplay(Display);
123    virtual void ::DestroyDisplay(Display);
124
125    // Display Position and Size
126    virtual bool ::DisplaySize(Display, int, int);
127    virtual void ::DisplayPosition(Display, int, int);
128
129    // Palettes
130    virtual void ::SetPalette(Display, ColorAlpha *, bool);
131    virtual void ::RestorePalette(Display);
132
133    // Display the back buffer content
134    virtual void ::StartUpdate(Display);
135    virtual void ::Scroll(Display, Box, int, int, Extent);
136    virtual void ::Update(Display, Box);
137    virtual void ::EndUpdate(Display);
138
139    // Allocate/free a bitmap
140    virtual bool ::AllocateBitmap(DisplaySystem, Bitmap, int, int, int, PixelFormat, bool);
141    virtual void ::FreeBitmap(DisplaySystem, Bitmap);
142
143    // Lock
144    virtual bool ::LockSystem(DisplaySystem displaySystem);
145    virtual void ::UnlockSystem(DisplaySystem displaySystem);
146
147    virtual bool ::Lock(Display);
148    virtual void ::Unlock(Display);
149
150    // Get/release a surface
151    virtual bool ::GetSurface(Display, Surface surface, int,int,Box);
152    virtual bool ::GetBitmapSurface(DisplaySystem displaySystem, Surface surface, Bitmap bitmap, int,int,Box);
153    virtual void ::ReleaseSurface(Display this, Surface);
154
155    // Clip a surface
156    virtual void ::Clip(Display, Surface, Box);
157
158    // Grab from the screen
159    virtual bool ::GrabScreen(Display, Bitmap, int, int, unsigned int, unsigned int);
160
161    // Converts a bitmap format
162    virtual bool ::ConvertBitmap(DisplaySystem, Bitmap, PixelFormat, ColorAlpha *);
163
164    // Converts an LFB bitmap into an offscreen bitmap for this device
165    virtual bool ::MakeDDBitmap(DisplaySystem, Bitmap, bool mipMaps, int cubeMapFace);
166
167    // Font loading
168    virtual Font ::LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade);
169    virtual void ::UnloadFont(DisplaySystem, Font);
170
171    // 2D Drawing
172    virtual void ::SetForeground(Display, Surface, ColorAlpha);
173    virtual void ::SetBackground(Display, Surface, ColorAlpha);
174    virtual void ::LineStipple(Display, Surface, uint pattern);
175    virtual ColorAlpha ::GetPixel(Display, Surface, int x, int y);
176    virtual void ::PutPixel(Display, Surface, int x, int y);
177    virtual void ::DrawLine(Display, Surface, int x1, int y1, int x2, int y2);
178    virtual void ::Rectangle(Display, Surface, int x1, int y1, int x2, int y2);
179    virtual void ::Area(Display, Surface, int x1, int y1, int x2, int y2);
180    virtual void ::Clear(Display, Surface, ClearType);
181    virtual void ::Blit(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h);
182    virtual void ::Stretch(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
183    virtual void ::Filter(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
184    virtual void ::BlitDI(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h);
185    virtual void ::StretchDI(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
186    virtual void ::FilterDI(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
187    virtual void ::TextFont(Display, Surface, Font);
188    virtual void ::TextOpacity(Display, Surface, bool);
189    virtual void ::WriteText(Display, Surface, int x, int y, const String text, int len, int prevGlyph, int * rPrevGlyph);
190    virtual void ::TextExtent(Display, Surface, const String text, int len, int * tw, int * th, int prevGlyph, int * rPrevGlyph, int * overHang);
191    virtual void ::FontExtent(DisplaySystem, Font, const String text, int len, int * tw, int * th, int prevGlyph, int * rPrevGlyph, int * overHang);
192    virtual void ::DrawingChar(Display, Surface, char ch);
193    virtual void ::NextPage(Display);
194 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
195    // 3D Graphics
196    virtual void ::SetRenderState(Display, RenderState, uint);
197    virtual void ::SetLight(Display, int, Light);
198    virtual void ::SetCamera(Display, Surface, Camera);
199    virtual bool ::AllocateMesh(DisplaySystem, Mesh, MeshFeatures, int nVertices);
200    virtual void ::FreeMesh(DisplaySystem, Mesh);
201    virtual bool ::LockMesh(DisplaySystem, Mesh, MeshFeatures flags);
202    virtual void ::UnlockMesh(DisplaySystem, Mesh, MeshFeatures flags);
203    virtual void * ::AllocateIndices(DisplaySystem, int nIndices, bool indices32bit);
204    virtual void ::FreeIndices(DisplaySystem, void * indices);
205    virtual uint16 * ::LockIndices(DisplaySystem, void * indices);
206    virtual void ::UnlockIndices(DisplaySystem, void * indices, bool indices32bit, int nIndices);
207    virtual void ::SelectMesh(Display, Mesh);
208    virtual void ::ApplyMaterial(Display, Material, Mesh);
209    virtual void ::DrawPrimitives(Display, PrimitiveSingle *, Mesh mesh);
210    virtual void ::PushMatrix(Display);
211    virtual void ::PopMatrix(Display, bool);
212    virtual void ::SetTransform(Display, Matrix, bool, bool);
213 #endif
214    virtual void ::SetBlitTint(Display, Surface, ColorAlpha);
215 };
216
217 public enum Alignment { left, right, center };
218 public enum ClearType { colorBuffer, depthBuffer, colorAndDepth };
219
220 define textCellW = 8;
221 define textCellH = 16;
222
223 public enum PixelFormat // : byte MESSES UP GuiApplication
224 {
225    pixelFormat4, pixelFormat8, pixelFormat444, pixelFormat555, pixelFormat565, pixelFormat888, pixelFormatAlpha, pixelFormatText, pixelFormatRGBA
226 };
227 public enum Resolution : int
228 {
229    resText80x25, res320x200, res320x240, res320x400, res360x480, res400x256, res400x300, res512x256, res512x384,
230    res640x200, res640x350, res640x400, res640x480,  res720x348, res800x600, res856x480, res960x720, res1024x768,
231    res1152x864, res1280x1024, res1600x1200, res768x480
232 };
233
234 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
235 public class LightFlags
236 {
237    public bool off:1, spot:1, omni:1, attenuation:1;
238 };
239
240 public struct Light
241 {
242    LightFlags flags;
243    ColorRGB ambient;
244    ColorRGB diffuse;
245    ColorRGB specular;
246    Vector3D direction;
247    Quaternion orientation;
248    Object lightObject;
249    Object target;
250    Degrees fallOff;
251    Degrees hotSpot;
252    float Kc;
253    float Kl;
254    float Kq;
255    Degrees start;
256    Degrees end;
257    float multiplier;
258 };
259
260 public define NumberOfLights = 8;
261
262 // Painter's algorithm
263
264 public class HitRecord : struct
265 {
266 public:
267    HitRecord prev, next;
268    uint pos;
269    uint numTags;
270    Vector3D center;
271    void * tags[1];   // More tags may follow
272
273    int Compare(HitRecord recordB, void * unused)
274    {
275       if(center.z > recordB.center.z)
276          return 1;
277       else if(center.z < recordB.center.z)
278          return -1;
279       else if(pos > recordB.pos)
280          return 1;
281       else if(pos < recordB.pos)
282          return -1;
283       else
284          return  0;
285    }
286 };
287
288 #define EPSILON 0.00001
289
290 struct SortPrimitive
291 {
292    PrimitiveSingle * triangle;
293    Object object;
294    Vector3Df middle;
295    Vector3Df min, max;
296    Plane plane;
297    bool marked;
298
299    int Compare(SortPrimitive primitive2)
300    {
301       double value;
302       if(ZOverlap(primitive2) && Sgn(plane.d) != Sgn(primitive2.plane.d))
303          value = plane.d - primitive2.plane.d;
304       else
305          value = middle.z - primitive2.middle.z;
306
307       if(value > EPSILON)
308          return 1;
309       else if(value<-EPSILON)
310          return -1;
311       else
312          return 0;
313    }
314
315    bool ZOverlap(SortPrimitive poly2)
316    {
317         if(min.z > poly2.max.z - EPSILON || poly2.min.z > max.z - EPSILON)
318          return false;
319         return true;
320    }
321
322    /*
323    bool XYOverlap(SortPrimitive poly2)
324    {
325         if(min.x > poly2.max.x - EPSILON || poly2.min.x > max.x - EPSILON )
326          return false;
327         if(min.y > poly2.max.y - EPSILON || poly2.min.y > max.y - EPSILON )
328          return false;
329         return true;
330    }
331
332    bool SurfaceOutside(SortPrimitive poly2)
333    {
334       bool result = true;
335
336       PrimitiveSingle * primitive = triangle;
337       Mesh mesh = object.mesh;
338       Matrix * matrix = &object.matrix;
339       int v;
340       double a = poly2.plane.a, b = poly2.plane.b, c = poly2.plane.c, d = poly2.plane.d;
341       if(d < 0)
342       {
343          a*=-1;
344          b*=-1;
345          c*=-1;
346          d = - (a * poly2.middle.x + b * poly2.middle.y + c * poly2.middle.z);
347       }
348
349       for(v = 0; v < primitive->nIndices; v++)
350       {
351          double surface;
352          Vector3Df * local = &mesh.vertices[primitive->indices[v]];
353          Vector3Df vertex;
354
355          vertex.MultMatrix(local, matrix);
356
357          surface = a * vertex.x + b * vertex.y + c * vertex.z + d;
358
359          if(surface < EPSILON)
360          {
361                         result = false;
362             break;
363          }
364         }
365
366       if(result == true)
367          return true;
368       else
369          return result;
370    }
371
372    bool SurfaceInside(SortPrimitive poly2)
373    {
374       bool result = true;
375
376       PrimitiveSingle * primitive = poly2.triangle;
377       Mesh mesh = poly2.object.mesh;
378       Matrix * matrix = &poly2.object.matrix;
379       int v;
380       double a = plane.a, b = plane.b, c = plane.c, d = plane.d;
381       if(d < 0)
382       {
383          a*=-1;
384          b*=-1;
385          c*=-1;
386          d = - (a * middle.x + b * middle.y + c * middle.z);
387       }
388
389       for(v = 0; v < primitive->nIndices; v++)
390       {
391          double surface;
392          Vector3Df * local = &mesh.vertices[primitive->indices[v]];
393          Vector3Df vertex;
394
395          vertex.MultMatrix(local, matrix);
396
397          surface = a * vertex.x + b * vertex.y + c * vertex.z + d;
398                 if(surface > -EPSILON)
399          {
400             result = false;
401             break;
402          }
403         }
404
405       if(result == true)
406          return true;
407       else
408         return result;
409    }
410
411    bool ShouldBeSwapped(SortPrimitive poly2)
412    {
413         if (!XYOverlap(poly2)) return false;
414         if (SurfaceOutside(poly2)) return false;
415         if (SurfaceInside(poly2)) return false;
416         return true;
417    }
418    */
419 };
420
421 #endif
422
423 #define MAX_CLIP_POINTS    50
424
425 public class Display
426 {
427 public:
428    ~Display()
429    {
430       if(displaySystem)
431       {
432          displaySystem.numDisplays--;
433          displaySystem.driver.DestroyDisplay(this);
434       }
435 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
436       delete display3D;
437 #endif
438    }
439
440    bool Create(DisplaySystem displaySystem, void * window)
441    {
442       bool result = false;
443       if(displaySystem)
444       {
445          this.displaySystem = displaySystem;
446          this.window = window;
447          displaySystem.numDisplays++;
448          if(displaySystem.driver.CreateDisplay(this))
449             result = true;
450          // if(!result) LogErrorCode(DisplayInitFailed, displaySystem.driver.name);
451       }
452       return result;
453    }
454
455    Surface GetSurface(int x, int y, Box clip)
456    {
457       Surface result = null;
458       Surface surface { _refCount = 1 };
459       if(surface)
460       {
461          Box box { -x, -y, -x + width - 1, -y + height - 1 };
462          box.Clip(clip);
463
464          surface.width = width - x;
465          surface.height = height - y;
466          surface.driver = displaySystem.driver;
467          surface.displaySystem = displaySystem;
468          surface.display = this;
469
470          if(displaySystem.driver.GetSurface(this, surface, x, y, box))
471             result = surface;
472          if(!result)
473             delete surface;
474       }
475       return result;
476    }
477
478    bool Resize(int width, int height)
479    {
480       return displaySystem.driver.DisplaySize(this, width, height);
481    }
482
483    void Position(int x, int y)
484    {
485       displaySystem.driver.DisplayPosition(this, x,y);
486    }
487
488    void StartUpdate(void)
489    {
490       displaySystem.driver.StartUpdate(this);
491    }
492
493    void Scroll(Box scroll, int x, int y, Extent dirty)
494    {
495       displaySystem.driver.Scroll(this, scroll, x, y, dirty);
496    }
497
498    void Update(Box updateBox)
499    {
500       displaySystem.driver.Update(this, updateBox);
501    }
502
503    void EndUpdate(void)
504    {
505       displaySystem.driver.EndUpdate(this);
506    }
507
508    void NextPage(void)
509    {
510       displaySystem.driver.NextPage(this);
511    }
512
513    bool Grab(Bitmap bitmap, int x, int y, int w, int h)
514    {
515       bool result = false;
516       if(bitmap && w > 0 && h > 0 &&
517          displaySystem.driver.GrabScreen(this, bitmap, x, y, w, h))
518          result = true;
519       else
520          bitmap.Free();
521       return result;
522    }
523
524    void FontExtent(Font font, const char * text, int len, int * width, int * height)
525    {
526       int overHang = 0;
527       FontExtent2(font, text, len, width, height, 0, null, &overHang);
528       if(width) *width += overHang;
529    }
530
531    void FontExtent2(Font font, const char * text, int len, int * width, int * height, int prevGlyph, int * rPrevGlyph, int * overHang)
532    {
533       // Fix for OnLoadGraphics time alpha blended window text extent on GDI
534 #if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE)
535       if(this && alphaBlend && pixelFormat == pixelFormat888 &&
536          displaySystem.driver == class(GDIDisplayDriver))
537       {
538          Surface s = GetSurface(0,0,null);
539          if(s)
540          {
541             s.font = font;
542             s.TextExtent2(text, len, width, height, prevGlyph, rPrevGlyph, overHang);
543             delete s;
544          }
545       }
546       else
547 #endif
548          // TODO: Should really pass display here...
549          DisplaySystem::FontExtent2(this ? displaySystem : null, font, text, len, width, height, prevGlyph, rPrevGlyph, overHang);
550    }
551
552    void SetPalette(ColorAlpha * palette, bool colorMatch)
553    {
554        displaySystem.driver.SetPalette(this, palette, colorMatch);
555    }
556
557    void RestorePalette(void)
558    {
559       displaySystem.driver.RestorePalette(this);
560    }
561
562    bool Lock(bool render)
563    {
564       bool result = false;
565       /*
566       int c;
567       for(c = 0; c<current; c++)
568          Log("   ");
569       Logf("Locking (%d)\n", current+1);
570       */
571
572       // TOCHECK: Why is displaySystem null with GISDesigner?
573       result = displaySystem && displaySystem.Lock();
574       if(result && render)
575       {
576 #if !defined(__EMSCRIPTEN__)
577          mutex.Wait();
578 #endif
579
580          if(!current)
581             result = displaySystem.driver.Lock(this);
582          else
583             result = true;
584          current++;
585       }
586       return result;
587    }
588
589    void Unlock(void)
590    {
591       if(current)
592       {
593          current--;
594          /*{
595             int c;
596
597             for(c = 0; c<current; c++)
598                Log("   ");
599             Logf("Unlocking (%d)\n", current);
600          }
601          */
602          if(!current && displaySystem)
603             displaySystem.driver.Unlock(this);
604 #if !defined(__EMSCRIPTEN__)
605          mutex.Release();
606 #endif
607       }
608       if(displaySystem)
609          displaySystem.Unlock();
610    }
611
612 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
613    // *** 3D GRAPHICS ***
614    void SetCamera(Surface surface, Camera camera)
615    {
616       if(!display3D)
617       {
618          display3D = Display3D { };
619       }
620       if(!display3D.selection)
621          DrawTranslucency();
622
623       if(!camera)
624       {
625          if(!display3D.selection)
626             displaySystem.driver.SelectMesh(this, null);
627
628          display3D.material = null;
629          display3D.mesh = null;
630       }
631       if(!display3D.selection)
632       {
633          displaySystem.driver.SetCamera(this, surface, camera);
634       }
635
636       this.display3D.camera = camera;
637
638       if(camera)
639       {
640          if(!camera.focalX)
641             camera.Setup(width, height, null);
642
643          // Always calling Update() here had broken interpolation in OrbitWithMouse!
644          if(!camera.cAngle.w && surface)
645             camera.Update();
646
647          if(display3D.selection)
648          {
649             // Compute Picking Planes
650             Vector3D normal;
651             Vector3D point { 0,0,0 };
652             Quaternion quat;
653             Angle fovLeft, fovRight, fovTop, fovBottom;
654             ClippingPlane c;
655
656             double l = camera.origin.x - (display3D.pickX - display3D.pickWidth/2.0f);
657             double r = camera.origin.x - (display3D.pickX + display3D.pickWidth/2.0f);
658             double t = (display3D.pickY - display3D.pickHeight/2.0f) - camera.origin.y;
659             double b = (display3D.pickY + display3D.pickHeight/2.0f) - camera.origin.y;
660
661             fovLeft   = atan(l / camera.focalX);
662             fovRight  = atan(r / camera.focalX);
663             fovTop    = atan(t / camera.focalY);
664             fovBottom = atan(b / camera.focalY);
665
666             // --- Left ---
667             quat.Yaw(fovLeft - Pi/2);
668             quat.ToDirection(normal);
669             display3D.viewPickingPlanes[left].FromPointNormal(normal, point);
670
671             // --- Right ---
672             quat.Yaw(fovRight + Pi/2);
673             quat.ToDirection(normal);
674             display3D.viewPickingPlanes[right].FromPointNormal(normal, point);
675
676             // --- Top ---
677             quat.Pitch(fovTop + Pi/2);
678             quat.ToDirection(normal);
679             display3D.viewPickingPlanes[top].FromPointNormal(normal, point);
680
681             // --- Bottom ---
682             quat.Pitch(fovBottom - Pi/2);
683             quat.ToDirection(normal);
684             display3D.viewPickingPlanes[bottom].FromPointNormal(normal, point);
685
686             // --- Near ---
687             normal.x = 0; normal.y = 0; normal.z = 1;
688             point.z = camera.zMin;
689             display3D.viewPickingPlanes[near].FromPointNormal(normal, point);
690
691             // --- Far ---
692             normal.x = 0; normal.y = 0; normal.z = -1;
693             point.z = camera.zMax;
694             display3D.viewPickingPlanes[far].FromPointNormal(normal, point);
695
696             for(c = 0; c<ClippingPlane::enumSize; c++)
697                display3D.worldPickingPlanes[c].MultMatrix(display3D.viewPickingPlanes[c], camera.inverseTranspose);
698
699             // Compute picking ray
700             {
701                Vector3D p;
702                display3D.rayView.p0 = { 0, 0, 0 };
703                p.x = display3D.pickX;
704                p.y = display3D.pickY;
705                p.z = 0.0f;
706                camera.Unproject(p, display3D.rayView.delta);
707
708                // Convert ray to world space
709                camera.Untransform(display3D.rayView.p0, display3D.rayWorld.p0);
710                camera.Untransform(display3D.rayView.delta, p);
711                display3D.rayWorld.delta.Subtract(p, display3D.rayWorld.p0);
712             }
713          }
714       }
715    }
716
717    // --- Lights ---
718    void SetLight(int id, Light light)
719    {
720       if(!display3D)
721       {
722          display3D = Display3D { };
723       }
724       displaySystem.driver.SetLight(this, id, light);
725    }
726
727    void SetLights(Object object)
728    {
729       if(object)
730          display3D._SetLights(this, object, 0);
731    }
732
733    // --- Transformations ---
734
735    void SetTransform(Matrix matrix, bool viewSpace)
736    {
737       if(display3D.selection)
738       {
739          ClippingPlane c;
740          Matrix transpose;
741          transpose.Transpose(matrix);
742
743          if(viewSpace)
744          {
745             for(c = 0; c<ClippingPlane::enumSize; c++)
746                display3D.localPickingPlanes[c].MultMatrix(display3D.viewPickingPlanes[c], transpose);
747          }
748          else
749          {
750             for(c = 0; c<ClippingPlane::enumSize; c++)
751                display3D.localPickingPlanes[c].MultMatrix(display3D.worldPickingPlanes[c], transpose);
752          }
753
754          // Transform ray
755          if(display3D.intersecting)
756          {
757             Vector3D p2, tp2;
758             if(viewSpace)
759                p2.Add(display3D.rayView.p0, display3D.rayView.delta);
760             else
761                p2.Add(display3D.rayWorld.p0, display3D.rayWorld.delta);
762
763             display3D.rayLocal.p0.DivideMatrix(display3D.rayWorld.p0, matrix);
764             tp2.DivideMatrix(p2, matrix);
765             display3D.rayLocal.delta.Subtract(tp2, display3D.rayLocal.p0);
766          }
767       }
768       else
769          displaySystem.driver.SetTransform(this, matrix, viewSpace, viewSpace ? false : true);
770    }
771
772    void PushMatrix(void)
773    {
774       displaySystem.driver.PushMatrix(this);
775    }
776
777    void PopMatrix(void)
778    {
779       displaySystem.driver.PopMatrix(this, true);
780    }
781
782    // --- Drawing ---
783    void ApplyMaterial(Material material, Mesh mesh)
784    {
785       if(material != display3D.material)
786       {
787          display3D.material = material;
788          displaySystem.driver.ApplyMaterial(this, material, mesh);
789       }
790    }
791
792    void DrawPrimitives(PrimitiveSingle primitive, Mesh mesh)
793    {
794       displaySystem.driver.DrawPrimitives(this, primitive, mesh);
795    }
796
797    void SelectMesh(Mesh mesh)
798    {
799       displaySystem.driver.SelectMesh(this, mesh);
800       if(display3D)
801          display3D.mesh = mesh;
802    }
803
804    bool DrawMesh(Object object)
805    {
806       bool result = false;
807       if(display3D.selection)
808          result = display3D.PickMesh(object, null);
809       else
810       {
811          Mesh mesh = object.mesh;
812          Material objectMaterial = object.material;
813
814          if(mesh.groups.first)
815          {
816             PrimitiveGroup group;
817             displaySystem.driver.SelectMesh(this, mesh);
818             display3D.mesh = mesh;
819
820             for(group = mesh.groups.first; group; group = group.next)
821             {
822                Material material = group.material ? group.material : objectMaterial;
823                if(!material) material = defaultMaterial;
824
825                if(material != display3D.material)
826                {
827                   display3D.material = material;
828                   displaySystem.driver.ApplyMaterial(this, material, mesh);
829                }
830
831                // *** Render Vertex Arrays ***
832                displaySystem.driver.DrawPrimitives(this, (PrimitiveSingle *)&group.type, mesh);
833             }
834          }
835
836          if(object.flags.translucent)
837          {
838             Matrix matrix;
839             Matrix inverse, inverseTranspose;
840             int c;
841
842             if(object.flags.viewSpace)
843                matrix = object.matrix;
844             else
845             {
846                Camera camera = display3D.camera;
847                Matrix temp = object.matrix;
848                temp.m[3][0] -= camera.cPosition.x;
849                temp.m[3][1] -= camera.cPosition.y;
850                temp.m[3][2] -= camera.cPosition.z;
851                matrix.Multiply(temp, camera.viewMatrix);
852             }
853
854             inverse.Inverse(matrix);
855             inverseTranspose.Transpose(inverse);
856
857             for(c = 0; c < mesh.nPrimitives; c++)
858             {
859                PrimitiveSingle * triangle = &mesh.primitives[c];
860                SortPrimitive * sort;
861                Plane * plane = &triangle->plane;
862                if(display3D.nTriangles >= display3D.maxTriangles)
863                {
864                   display3D.maxTriangles = display3D.maxTriangles ? (display3D.maxTriangles * 3 / 2) : 32768;
865                   display3D.triangles = renew display3D.triangles SortPrimitive[display3D.maxTriangles];
866                }
867                sort = &display3D.triangles[display3D.nTriangles++];
868                sort->object = object;
869                sort->triangle = triangle;
870                sort->middle.MultMatrix(triangle->middle, matrix);
871                sort->middle.z *= -1;
872                // sort->plane.MultMatrix(triangle->plane, inverseTranspose);
873                sort->plane.d = plane->a * inverseTranspose.m[0][3] +
874                                plane->b * inverseTranspose.m[1][3] +
875                                plane->c * inverseTranspose.m[2][3] +
876                                plane->d * inverseTranspose.m[3][3];
877             }
878          }
879          else
880          {
881             int c;
882             displaySystem.driver.SelectMesh(this, mesh);
883             display3D.mesh = mesh;
884
885             for(c = 0; c<mesh.nPrimitives; c++)
886             {
887                PrimitiveSingle * primitive = &mesh.primitives[c];
888
889                Material material = primitive->material ? primitive->material : objectMaterial;
890                if(!material) material = defaultMaterial;
891
892                if(material != display3D.material)
893                {
894                   display3D.material = material;
895                   displaySystem.driver.ApplyMaterial(this, material, mesh);
896                }
897
898                displaySystem.driver.DrawPrimitives(this, primitive, display3D.mesh);
899             }
900          }
901          result = true;
902       }
903       return result;
904    }
905
906    bool IsObjectVisible(Object object)
907    {
908       Plane * planes;
909       if(display3D.selection || !display3D.camera)
910          planes = object.flags.viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes;
911       else
912          planes = object.flags.viewSpace ? display3D.camera.viewClippingPlanes : display3D.camera.worldClippingPlanes;
913        return object.InsideFrustum(planes) != outside;
914    }
915
916    bool DrawObject(Object object)
917    {
918       bool result = false;
919       if(object && object.volume)
920       {
921          Object child;
922          FrustumPlacement visible;
923          Plane * planes;
924          Camera camera = display3D.camera;
925
926          if(display3D.selection || !camera)
927             planes = object.flags.viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes;
928          else
929             planes = object.flags.viewSpace ? camera.viewClippingPlanes : camera.worldClippingPlanes;
930
931          visible = object.InsideFrustum(planes);
932
933          if(visible || display3D.pickingPlanes)
934          {
935             if(display3D.collectingHits && object.tag)
936             {
937                /*if(object.flags.root)
938                   this.tags[display3D.tagIndex] = object.tag;
939                else if(object.tag)
940                   this.tags[++display3D.tagIndex] = object.tag;
941                   */
942                display3D.tags[display3D.tagIndex++] = object.tag;
943             }
944
945             if(object.flags.mesh && object.mesh)
946             {
947                if(!display3D.selection && displaySystem.driver.PushMatrix)
948                   displaySystem.driver.PushMatrix(this);
949
950 #if ENABLE_GL_FFP
951                if(object.mesh.tangents && object.mesh.normals && object.flags.computeLightVectors)
952                {
953                   Mesh mesh = object.mesh;
954                   if(!glCaps_shaders)
955                   {
956                      int count = mesh.nVertices;
957                      Vector3Df * normals = mesh.normals;
958                      Vector3Df * vertices = mesh.vertices;
959                      ColorRGB * lightVectors;
960                      Vector3Df * tangents = mesh.tangents;
961                      int i;
962                      float * l = display3D.light0Pos;
963                      Vector3Df light { l[0], l[1], l[2] };
964                      Matrix o = object.matrix;
965                      Matrix t, inv = camera.viewMatrix;
966                      Vector3D ot { };
967                      Vector3D cPos = camera.cPosition;
968                      Vector3D pos;
969                      bool positional = l[3] ? true : false;
970
971                      inv.Scale(1.0/nearPlane, -1.0/nearPlane,-1.0/nearPlane);
972
973                      pos.MultMatrix(cPos, camera.viewMatrix);
974
975                      ot.x = o.m[3][0] + pos.x;
976                      ot.y = o.m[3][1] + pos.y;
977                      ot.z = o.m[3][2] + pos.z;
978                      o.m[3][0] = 0;
979                      o.m[3][1] = 0;
980                      o.m[3][2] = 0;
981                      t.Multiply(o, inv);
982                      inv = t;
983                      t.Transpose(inv);
984                      inv.Inverse(t);
985
986                      mesh.Allocate({ lightVectors = true }, mesh.nVertices, displaySystem);
987                      mesh.Lock({ lightVectors = true });
988                      lightVectors = mesh.lightVectors;
989                      for(i = 0; i < count; i++)
990                      {
991                         Vector3Df tangent1 = tangents[i*2 + 0];
992                         Vector3Df tangent2 = tangents[i*2 + 1];
993                         Vector3Df normal = normals[i];
994                         Vector3Df tTangent1, tTangent2, tNormal;
995
996                         tTangent1.MultMatrix(tangent1, inv);
997                         tTangent2.MultMatrix(tangent2, inv);
998                         tNormal  .MultMatrix(normal,   inv);
999
1000                         tTangent1.Normalize(tTangent1);
1001                         tTangent2.Normalize(tTangent2);
1002                         tNormal  .Normalize(tNormal);
1003
1004                         {
1005                            Matrix tbn
1006                            { {
1007                                tTangent1.x, tTangent2.x, tNormal.x, 0,
1008                                tTangent1.y, tTangent2.y, tNormal.y, 0,
1009                                tTangent1.z, tTangent2.z, tNormal.z, 1
1010                            } };
1011                            Vector3Df n;
1012                            if(positional)
1013                            {
1014                               Vector3Df tPos = vertices[i];
1015                               tPos.x += ot.x, tPos.y += ot.y, tPos.z += ot.z;
1016
1017                               // Subtract vertex from light for positional lights
1018                               light.x = l[0] - tPos.x;
1019                               light.y = l[1] + tPos.y;
1020                               light.z = l[2] - tPos.z;
1021                            }
1022                            n.MultMatrix(light, tbn);
1023                            if(positional)
1024                               n.Normalize(n);
1025                            lightVectors[i] = { n.x / 2 + 0.5f, n.y / 2 + 0.5f, n.z / 2 + 0.5f };
1026                         }
1027                      }
1028                      mesh.Unlock({ lightVectors = true });
1029
1030                      // Create normalization cube map
1031                      /*
1032                      if(!mesh.normMap)
1033                         mesh.normMap = { };
1034                      {
1035                         int w = 256, h = 256, d = 256;
1036                         Vector3Df min = mesh.min, max = mesh.max;
1037                         Vector3Df delta
1038                         {
1039                            (max.x - min.x) / w,
1040                            (max.y - min.y) / h,
1041                            (max.z - min.z) / d
1042                         };
1043                         int i;
1044                         for(i = 0; i < 6; i++)
1045                         {
1046                            Bitmap face = i > 0 ? { } : mesh.normMap;
1047                            int x, y;
1048                            ColorAlpha * p;
1049                            face.Free();
1050                            face.Allocate(null, w, h, 0, pixelFormat888, false);
1051                            face.driverData = mesh.normMap.driverData;
1052                            p = (ColorAlpha *)face.picture;
1053                            for(y = 0; y < h; y++)
1054                            {
1055                               for(x = 0; x < w; x++, p++)
1056                               {
1057                                  Vector3Df v { min.x + x * delta.x, min.y + y * delta.y, min.z };
1058                                  v.Normalize(v);
1059                                  *p = ColorAlpha { 255, {
1060                                        (byte)((v.x / 2.0 + 0.5) * 255),
1061                                        (byte)((v.y / 2.0 + 0.5) * 255),
1062                                        (byte)((v.z / 2.0 + 0.5) * 255) } };
1063                              }
1064                            }
1065                            displaySystem.driver.MakeDDBitmap(displaySystem, face, true, (i + 1));
1066                            if(i > 0)
1067                            {
1068                               face.driverData = 0;
1069                               delete face;
1070                            }
1071                         }
1072                      }
1073                      */
1074                   }
1075                   else
1076                      mesh.Free({ lightVectors = true });
1077                }
1078 #endif
1079
1080                SetTransform(object.matrix, object.flags.viewSpace);
1081                if(display3D.selection)
1082                {
1083                   if(visible == intersecting || display3D.intersecting)
1084                   {
1085                      Vector3D rayIntersect;
1086                      if(display3D.PickMesh(object, rayIntersect))
1087                      {
1088                         if(display3D.intersecting)
1089                         {
1090                            Vector3D wresult, vresult;
1091                            wresult.MultMatrix(rayIntersect, object.matrix);
1092                            if(!object.flags.viewSpace)
1093                               camera.TransformPoint(vresult, wresult);
1094                            else
1095                               vresult = wresult;
1096
1097                            if(vresult.z < display3D.rayIntersect.z)
1098                               display3D.rayIntersect = vresult;
1099                            display3D.intersected = true;
1100                         }
1101                         result = true;
1102                      }
1103                   }
1104                   else
1105                      result = true;
1106                }
1107                else
1108                {
1109                   result |= DrawMesh(object);
1110                   if(displaySystem.driver.PopMatrix)
1111                      displaySystem.driver.PopMatrix(this, true);
1112                }
1113                if(display3D.collectingHits && result /*&& object.tag*/)
1114                {
1115                   int c;
1116                   HitRecord hit = (HitRecord)new0 byte[sizeof(class HitRecord) + sizeof(void *) * (display3D.tagIndex/*+1*/)];
1117                   display3D.hitList.Add(hit);
1118                   hit.pos = display3D.hitList.count-1;
1119                   hit.numTags = display3D.tagIndex /*+ 1*/;
1120                   for(c = 0; c</*=*/display3D.tagIndex; c++)
1121                   {
1122                      hit.tags[c] = display3D.tags[c];
1123                   }
1124
1125                   if(!object.flags.viewSpace)
1126                      camera.TransformPoint(hit.center, object.wcenter);
1127                   else
1128                      hit.center = object.wcenter;
1129                }
1130             }
1131
1132             for(child = object.children.first; child; child = child.next)
1133                result |= DrawObject(child);
1134
1135             if(display3D.collectingHits && /*!object.flags.root && */object.tag)
1136                display3D.tagIndex--;
1137          }
1138       }
1139       return result;
1140    }
1141
1142    void DrawTranslucency(void)
1143    {
1144       if(display3D && display3D.camera)
1145       {
1146          // *** Render translucent primitives ***
1147          if(display3D.nTriangles)
1148          {
1149             Matrix * matrix = null;
1150             int c;
1151
1152             blend = true;
1153
1154             display3D.SortTriangles();
1155
1156             depthWrite = false;
1157
1158             displaySystem.driver.PushMatrix(this);
1159             for(c=0; c<display3D.nTriangles; c++)
1160             {
1161                SortPrimitive * sort = &display3D.triangles[c];
1162                Mesh mesh = sort->object.mesh;
1163                PrimitiveSingle * primitive = sort->triangle;
1164                Material material;
1165
1166                if(&sort->object.matrix != matrix)
1167                {
1168                   matrix = &sort->object.matrix;
1169
1170                   displaySystem.driver.PopMatrix(this, false);
1171                   displaySystem.driver.PushMatrix(this);
1172                   SetTransform(matrix, sort->object.flags.viewSpace);
1173                }
1174                if(mesh != display3D.mesh)
1175                {
1176                   displaySystem.driver.SelectMesh(this, mesh);
1177                   display3D.mesh = mesh;
1178                }
1179
1180                material = primitive->material ? primitive->material : sort->object.material;
1181                if(!material) material = defaultMaterial;
1182
1183                if(material != display3D.material)
1184                {
1185                   displaySystem.driver.ApplyMaterial(this, material, display3D.mesh);
1186                   display3D.material = material;
1187                }
1188
1189                /*
1190                {
1191                   Material testMaterial { };
1192                   float amount;
1193
1194                   amount = (display3D.triangles[0].middle.z - display3D.triangles[c].middle.z) /
1195                      (display3D.triangles[0].middle.z - display3D.triangles[display3D.nTriangles-1].middle.z);
1196
1197                   testMaterial.flags.doubleSided = { doubleSided = true, translucent = true };
1198                   testMaterial.diffuse.a = 1;
1199                   testMaterial.emissive.r = testMaterial.emissive.g = testMaterial.emissive.b = amount;
1200                   testMaterial.baseMap = material->baseMap;
1201
1202                   displaySystem.driver.ApplyMaterial(this, testMaterial, display3D.mesh);
1203                }
1204                */
1205
1206                // *** Render primitive ***
1207                // if(sort->plane.d > 0)
1208                displaySystem.driver.DrawPrimitives(this, primitive, display3D.mesh);
1209             }
1210             displaySystem.driver.PopMatrix(this, true);
1211
1212             display3D.nTriangles = 0;
1213             blend = false;
1214          }
1215       }
1216    }
1217
1218    // --- Picking ---
1219    void StartSelection(int pickX, int pickY, int pickW, int pickH)
1220    {
1221       if(!display3D)
1222       {
1223          display3D = Display3D { };
1224       }
1225       display3D.pickX = (float)pickX;
1226       display3D.pickY = (float)pickY;
1227       display3D.pickWidth = (float)pickW;
1228       display3D.pickHeight = (float)pickH;
1229       display3D.selection = true;
1230    }
1231
1232    void CollectHits(void)
1233    {
1234       display3D.collectingHits = true;
1235    }
1236
1237    int GetHits(OldList list)
1238    {
1239       display3D.collectingHits = false;
1240       display3D.hitList.Sort(HitRecord::Compare, null);
1241       list = display3D.hitList;
1242       display3D.hitList.Clear();
1243       return list.count;
1244    }
1245
1246    void IntersectPolygons(void)
1247    {
1248       display3D.rayIntersect = { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1249       display3D.intersected = false;
1250       display3D.intersecting = true;
1251    }
1252
1253    bool GetIntersect(Vector3D intersect)
1254    {
1255       intersect = display3D.rayIntersect;
1256       display3D.intersecting = false;
1257       return display3D.intersected;
1258    }
1259
1260    void StopSelection(void)
1261    {
1262       display3D.selection = false;
1263    }
1264
1265    // --- Rendering States ---
1266    property FillModeValue fillMode { set { displaySystem.driver.SetRenderState(this, fillMode, value); } };
1267    property bool depthTest    { set { displaySystem.driver.SetRenderState(this, depthTest, value); } };
1268    property bool depthWrite   { set { displaySystem.driver.SetRenderState(this, depthWrite, value); } };
1269    property float fogDensity  { set { displaySystem.driver.SetRenderState(this, fogDensity, RenderStateFloat { value }.ui); } };
1270    property Color fogColor    { set { displaySystem.driver.SetRenderState(this, fogColor, value); } };
1271    property bool blend        { set { displaySystem.driver.SetRenderState(this, blend, value); } };
1272    property Color ambient     { set { displaySystem.driver.SetRenderState(this, ambient, value); } };
1273    property bool alphaWrite   { set { displaySystem.driver.SetRenderState(this, alphaWrite, value); } };
1274    property bool antiAlias    { set { displaySystem.driver.SetRenderState(this, antiAlias, value); } };
1275    property bool vSync        { set { displaySystem.driver.SetRenderState(this, vSync, value); } };
1276
1277    property bool pickingPlanes { set { display3D.pickingPlanes = value; } };
1278 #endif
1279    property DisplayFlags flags { get { return displaySystem.flags; } }
1280    property PixelFormat pixelFormat { get { return /*alphaBlend ? pixelFormat888 : */displaySystem.pixelFormat; } }
1281    property bool alphaBlend { set { alphaBlend = value; } get { return alphaBlend; } };
1282    property bool useSharedMemory { set { useSharedMemory = value; } get { return useSharedMemory; } };
1283    property void * systemWindow { get { return window; } };
1284    property DisplaySystem displaySystem { get { return displaySystem; } };
1285 #if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER)
1286    property GLCapabilities glCapabilities
1287    {
1288       get { return ((OGLDisplay)driverData).capabilities; }
1289       set
1290       {
1291          glCapabilities = value;
1292          if(driverData && displaySystem.driver == class(OpenGLDisplayDriver))
1293          {
1294             OGLDisplay oglDisplay = driverData;
1295             if(!oglDisplay.originalCapabilities.fixedFunction)
1296                value.shaders = true;
1297             if(!oglDisplay.originalCapabilities.shaders)
1298                value.fixedFunction = true;
1299             // Disable things that don't work with shaders
1300             if(value.shaders)
1301             {
1302                value.fixedFunction = false;
1303                value.legacy = false;
1304                value.immediate = false;
1305             }
1306             oglDisplay.capabilities = oglDisplay.originalCapabilities & value;
1307
1308             Lock(true);
1309             OpenGLDisplayDriver::initialDisplaySetup(this, true, false);
1310             Unlock();
1311          }
1312       }
1313    };
1314 #endif
1315
1316    int width, height;
1317    void * driverData;
1318
1319 private:
1320
1321    DisplaySystem displaySystem;
1322    void * window;
1323
1324 #if !defined(__EMSCRIPTEN__)
1325    Mutex mutex { };
1326 #endif
1327    int current;
1328
1329 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1330    Display3D display3D;
1331 #endif
1332    bool alphaBlend;
1333    void * windowDriverData;
1334    bool useSharedMemory;
1335    GLCapabilities glCapabilities;
1336    glCapabilities = { true, true, true, true, true, true, true, true };
1337 };
1338
1339 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1340 private class Display3D : struct
1341 {
1342    // 3D Display
1343    int nTriangles;
1344    SortPrimitive * triangles;
1345    int maxTriangles;
1346    Vector3D points[MAX_CLIP_POINTS];
1347    Vector3D newPoints[MAX_CLIP_POINTS];
1348    byte goodPoints[MAX_CLIP_POINTS];
1349
1350    Material material;
1351    Mesh mesh;
1352    Camera camera;
1353    Plane viewPickingPlanes[ClippingPlane], worldPickingPlanes[ClippingPlane];
1354    Plane localPickingPlanes[ClippingPlane];
1355
1356    bool collectingHits, selection, intersecting, intersected, pickingPlanes;
1357    float pickX, pickY, pickWidth, pickHeight;
1358    OldList hitList;
1359    void * tags[64];
1360    int tagIndex;
1361
1362    Line rayView, rayWorld, rayLocal;
1363    Vector3D rayIntersect;
1364    float light0Pos[4];
1365
1366    ~Display3D()
1367    {
1368       delete triangles;
1369    }
1370
1371    int _SetLights(Display display, Object object, int id)
1372    {
1373       if(id < NumberOfLights)
1374       {
1375          Object child;
1376
1377          if(object.flags.light && !object.light.flags.off)
1378             display.SetLight(id++, object.light);
1379
1380          for(child = object.children.first; child; child = child.next)
1381          {
1382             id = _SetLights(display, child, id);
1383          }
1384       }
1385       return id;
1386    }
1387
1388    //#define TRESHOLD           -1
1389    //#define TRESHOLD           -0.25
1390    #define TRESHOLD           -0.0025
1391
1392    bool PickPrimitives(Mesh mesh, PrimitiveSingle primitive, Vector3D rayDiff, Vector3D rayIntersect)
1393    {
1394       Plane * planes = localPickingPlanes;
1395       int c = 0;
1396       int nIndex = 1, nPoints = 1;
1397       int offset = 0;
1398       bool result = false;
1399       Vector3D * points = this.points;
1400       Vector3D * newPoints = this.newPoints;
1401       byte * goodPoints = this.goodPoints;
1402       int nVertices = primitive.type.vertexRange ? primitive.nVertices : primitive.nIndices;
1403       int strip = 1;
1404       Vector3Df tmp;
1405       bool i32bit = primitive.type.indices32bit;
1406       uint32 * indices32 = primitive.indices32;
1407       uint16 * indices16 = primitive.indices;
1408
1409       switch(primitive.type.primitiveType)
1410       {
1411          case triangles: nIndex = 3; nPoints = 3; break;
1412          case quads: nIndex = 4; nPoints = 4; break;
1413          case triStrip:
1414          case triFan:
1415             nIndex = 1; nPoints = 3;
1416             offset = 2;
1417             tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first] : mesh.vertices[(i32bit ? indices32[0] : indices16[0])];
1418             points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1419             tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first+1] : mesh.vertices[(i32bit ? indices32[1] : indices16[1])];
1420             points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1421             break;
1422       }
1423
1424       for(c = offset; c<nVertices; c += nIndex)
1425       {
1426          bool outside = false;
1427          if(!pickingPlanes)
1428          {
1429             int p;
1430             int n = nPoints;
1431             int i;
1432
1433             if(primitive.type.vertexRange)
1434             {
1435                if(primitive.type.primitiveType == triStrip)
1436                {
1437                   tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 1) : (c - 2)];
1438                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1439                   tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 2) : (c - 1)];
1440                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1441                }
1442                else if(primitive.type.primitiveType == triFan)
1443                {
1444                   tmp = mesh.vertices[primitive.first + 0];
1445                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1446                   tmp = mesh.vertices[primitive.first + c - 1];
1447                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1448                }
1449                for(i = 0; i<nIndex; i++)
1450                {
1451                   tmp = mesh.vertices[primitive.first + c+i];
1452                   points[i + offset] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1453                }
1454             }
1455             else
1456             {
1457                if(primitive.type.primitiveType == triStrip)
1458                {
1459                   i = (c & 1) ? (c - 1) : (c - 2);
1460                   tmp = mesh.vertices[(i32bit ? indices32[i] : indices16[i])];
1461                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1462
1463                   i = (c & 1) ? (c - 2) : (c - 1);
1464                   tmp = mesh.vertices[(i32bit ? indices32[i] : indices16[i])];
1465                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1466                }
1467                else if(primitive.type.primitiveType == triFan)
1468                {
1469                   tmp = mesh.vertices[(i32bit ? indices32[0] : indices16[0])];
1470                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1471                   tmp = mesh.vertices[(i32bit ? indices32[c-1] : indices16[c-1])];
1472                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1473                }
1474                for(i = 0; i<nIndex; i++)
1475                {
1476                   tmp = mesh.vertices[(i32bit ? indices32[c+i] : indices16[c+i])];
1477                   points[i + offset] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1478                }
1479             }
1480
1481             for(p = 0; p < 6; p++)
1482             {
1483                Plane * plane = &planes[p];
1484                int i;
1485                int numGoodPoints = 0;
1486
1487                memset(goodPoints, 0, n);
1488                for(i = 0; i < n; i++)
1489                {
1490                   double dot = plane->normal.DotProduct(points[i]);
1491                   double distance = dot + plane->d;
1492                               if(distance > TRESHOLD)
1493                   {
1494                      numGoodPoints++;
1495                      goodPoints[i] = 1;
1496                   }
1497                       }
1498                if(!numGoodPoints)
1499                {
1500                   outside = true;
1501                   break;
1502                }
1503                if(numGoodPoints < n)
1504                {
1505                   // Clip the polygon
1506                   int newN = 0;
1507                   int lastGood = -1;
1508                   int j;
1509
1510                   for(j = 0; j<n; )
1511                   {
1512                      if(goodPoints[j])
1513                      {
1514                         newPoints[newN++] = points[j];
1515                         lastGood = j++;
1516                      }
1517                      else
1518                      {
1519                         Line edge;
1520                         int next;
1521
1522                         if(lastGood == -1)
1523                            for(lastGood = n-1; !goodPoints[lastGood]; lastGood--);
1524
1525                         edge.p0 = points[lastGood];
1526                         edge.delta.Subtract(points[j], edge.p0);
1527                         plane->IntersectLine(edge, newPoints[newN++]);
1528
1529                         for(next = j+1; next != j; next++)
1530                         {
1531                            if(next == n) next = 0;
1532                            if(goodPoints[next])
1533                            {
1534                               int prev = next - 1;
1535                               if(prev < 0) prev = n-1;
1536
1537                               edge.p0 = points[prev];
1538                               edge.delta.Subtract(points[next], edge.p0);
1539                               plane->IntersectLine(edge, newPoints[newN++]);
1540                               break;
1541                            }
1542                         }
1543                         if(next <= j)
1544                            break;
1545                         else
1546                            j = next;
1547                      }
1548                   }
1549                   // Use the new points
1550                   memcpy(points, newPoints, newN * sizeof(Vector3D));
1551                   n = newN;
1552                }
1553               }
1554          }
1555          if(!outside)
1556          {
1557                    result = true;
1558
1559             // TODO: Implement intersection with TriStrip, TriFan...
1560             if(intersecting)
1561             {
1562                // Intersect primitives
1563                Plane plane;
1564                Vector3D intersect, diff;
1565                int i0 = c, i1 = c+1, i2 = c+2;
1566
1567                if(primitive.type.primitiveType == triStrip)
1568                {
1569                   i0 = (c & 1) ? (c - 1) : (c - 2);
1570                   i1 = (c & 1) ? (c - 2) : (c - 1);
1571                   i2 = c;
1572                }
1573                else if(primitive.type.primitiveType == triFan)
1574                {
1575                   i0 = 0;
1576                   i1 = c - 1;
1577                   i2 = c;
1578                }
1579
1580                if(primitive.type.vertexRange)
1581                   plane.FromPointsf(
1582                      mesh.vertices[primitive.first + i0],
1583                      mesh.vertices[primitive.first + i1],
1584                      mesh.vertices[primitive.first + i2]);
1585                else
1586                   plane.FromPointsf(
1587                      mesh.vertices[(i32bit ? indices32[i0] : indices16[i0])],
1588                      mesh.vertices[(i32bit ? indices32[i1] : indices16[i1])],
1589                      mesh.vertices[(i32bit ? indices32[i2] : indices16[i2])]);
1590
1591                plane.IntersectLine(rayLocal, intersect);
1592                diff.Subtract(intersect, rayLocal.p0);
1593                diff.x /= rayLocal.delta.x;
1594                diff.y /= rayLocal.delta.y;
1595                diff.z /= rayLocal.delta.z;
1596                if(diff.x < rayDiff.x || diff.y < rayDiff.y || diff.z < rayDiff.z)
1597                {
1598                   rayDiff = diff;
1599                   rayIntersect = intersect;
1600                }
1601             }
1602             else
1603                break;
1604          }
1605
1606          switch(primitive.type)
1607          {
1608             case triStrip:
1609                points[strip] = points[2];
1610                strip ^= 1;
1611                break;
1612             case triFan:
1613                points[1] = points[2];
1614                break;
1615          }
1616       }
1617       return result;
1618    }
1619
1620    bool PickMesh(Object object, Vector3D rayIntersect)
1621    {
1622       Mesh mesh = object.mesh;
1623       bool result = false;
1624       Vector3D rayDiff { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1625       if(rayIntersect != null)
1626          rayIntersect = { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1627
1628       if(mesh.groups.first)
1629       {
1630          PrimitiveGroup group;
1631
1632          for(group = mesh.groups.first; group; group = group.next)
1633          {
1634             if(PickPrimitives(mesh, (PrimitiveSingle *)&group.type, rayDiff, rayIntersect))
1635             {
1636                result = true;
1637                if(!intersecting)
1638                   break;
1639             }
1640          }
1641       }
1642       else
1643       {
1644          int c;
1645          for(c = 0; c < mesh.nPrimitives; c++)
1646          {
1647             if(PickPrimitives(mesh, mesh.primitives[c], rayDiff, rayIntersect))
1648             {
1649                result = true;
1650                if(!intersecting)
1651                   break;
1652             }
1653          }
1654       }
1655       return result;
1656    }
1657
1658    void SortTriangles(void)
1659    {
1660       Matrix matrix;
1661       Object object = null;
1662       int c;
1663       for(c=0; c<nTriangles; c++)
1664       {
1665          SortPrimitive * sort = &triangles[c];
1666          Mesh mesh = sort->object.mesh;
1667          PrimitiveSingle * primitive = sort->triangle;
1668                 Vector3Df min { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1669          Vector3Df max { -MAXFLOAT, -MAXFLOAT, -MAXFLOAT };
1670          int v;
1671          bool ix32 = primitive->type.indices32bit;
1672          if(object != sort->object)
1673          {
1674             object = sort->object;
1675             if(object.flags.viewSpace)
1676                matrix = object.matrix;
1677             else
1678             {
1679                Camera camera = this.camera;
1680                Matrix temp = object.matrix;
1681                temp.m[3][0] -= camera.cPosition.x;
1682                temp.m[3][1] -= camera.cPosition.y;
1683                temp.m[3][2] -= camera.cPosition.z;
1684                matrix.Multiply(temp, camera.viewMatrix);
1685             }
1686          }
1687
1688          for(v = 0; v<primitive->nIndices; v++)
1689          {
1690             Vector3Df * local = &mesh.vertices[ix32 ? primitive->indices32[v] : primitive->indices[v]];
1691             Vector3Df vertex;
1692
1693             vertex.MultMatrix(local, &matrix);
1694
1695                         if(vertex.x > max.x) max.x = vertex.x;
1696             if(vertex.y > max.y) max.y = vertex.y;
1697             if(vertex.z > max.z) max.z = vertex.z;
1698                         if(vertex.x < min.x) min.x = vertex.x;
1699             if(vertex.y < min.y) min.y = vertex.y;
1700             if(vertex.z < min.z) min.z = vertex.z;
1701                 }
1702
1703          sort->min = min;
1704          sort->max = max;
1705
1706          sort->marked = false;
1707         }
1708
1709    /*
1710       Logf("========= Before Sort ==========\n");
1711       for(c=0; c<nTriangles; c++)
1712       {
1713          SortPrimitive * sort = &triangles[c];
1714          int v;
1715          Mesh mesh = sort->mesh;
1716          PrimitiveSingle * primitive = sort->triangle;
1717
1718          Logf("Triangle %d (%s):\n", c, primitive->material->name);
1719
1720          for(v = 0; v<primitive->nIndices; v++)
1721          {
1722             Vector3Df * local = &mesh.vertices[primitive->indices[v]];
1723             Vector3Df vertex;
1724
1725
1726             vertex.<MultMatrix(local, sort->matrix);
1727
1728             Logf("Vertex %d:", v);
1729
1730             // Logf("   Local %f, %f, %f:\n", local->x, local->y, local->z);
1731             Logf("   View  %f, %f, %f:\n", vertex.x, vertex.y, vertex.z);
1732
1733                 }
1734
1735          Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z);
1736          Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z);
1737          Logf("\n", c);
1738       }*/
1739
1740       // *** Sort translucent primitives ***
1741       qsort((void*) triangles, nTriangles, sizeof(SortPrimitive), SortPrimitive::Compare);
1742    /*
1743       Logf("\n\n========= After Sort ==========\n");
1744       for(c=0; c<nTriangles; c++)
1745       {
1746          SortPrimitive * sort = &triangles[c];
1747          int v;
1748          Mesh mesh = sort->mesh;
1749          PrimitiveSingle * primitive = sort->triangle;
1750
1751          Logf("Triangle %d (%s):\n", c, primitive->material->name);
1752
1753          for(v = 0; v<primitive->nIndices; v++)
1754          {
1755             Vector3Df * local = &mesh.vertices[primitive->indices[v]];
1756             Vector3Df vertex;
1757
1758
1759             vertex.MultMatrix(local, sort->matrix);
1760
1761             Logf("Vertex %d:", v);
1762
1763             // Logf("   Local %f, %f, %f:\n", local->x, local->y, local->z);
1764             Logf("   View  %f, %f, %f:\n", vertex.x, vertex.y, vertex.z);
1765
1766                 }
1767
1768          Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z);
1769          Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z);
1770          Logf("\n", c);
1771       }
1772    */
1773       //exit(0);
1774
1775    /*
1776    If all five tests fail for a particular Q,
1777    then P might obscure Q. Now Q must be tested.
1778    First, the algorithm checks if Q has been "marked."
1779    If Q is marked, then Q was "moved around" in the list
1780    during a previous iteration of the loop. The algorithm
1781    only allows a polygon to be moved once, to avoid the possibility
1782    of infinite loops. If Q is not marked, it is tested to see
1783    if it might obscure P. If Q cannot obscure P, then Q is possibly
1784    behind P and so it is good candidate to be drawn next.
1785    Therefore, the algorithm "abandons" the current P (that is, it
1786    stops testing Q's against the current P) and moves the current
1787    Q to the end of the list to become the next P.
1788    */
1789    /*
1790       {
1791          int p;
1792          for(p = 0; p<nTriangles; p++)
1793          {
1794             SortPrimitive * poly1 = &triangles[p];
1795             int q;
1796
1797             for(q = p+1; q<nTriangles; q++)
1798             {
1799                if(q != p)
1800                {
1801                   SortPrimitive * poly2 = &triangles[q];
1802                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1803                   {
1804                      if(poly1->ShouldBeSwapped(poly2))
1805                      {
1806                         if(!poly2->ShouldBeSwapped(poly1))
1807                         {
1808                            SortPrimitive temp = *poly2;
1809                            memmove(triangles+1, triangles, sizeof(SortPrimitive)*q);
1810                            triangles[0] = temp;
1811                            triangles[0].marked = true;
1812                            p = 0;
1813                            break;
1814                         }
1815                      }
1816                   }
1817                }
1818             }
1819          }
1820       }
1821
1822       {
1823          int p;
1824          for(p = 0; p<nTriangles; p++)
1825          {
1826             SortPrimitive * poly1 = &triangles[p];
1827             int q;
1828
1829             // for(q = p+1; q<nTriangles; q++)
1830             for(q = 0; q<nTriangles; q++)
1831             {
1832                if(q != p)
1833                {
1834                   SortPrimitive * poly2 = &triangles[q];
1835                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1836                   {
1837                      if(poly1->ShouldBeSwapped(poly2))
1838                      {
1839                         if(!poly2->ShouldBeSwapped(poly1))
1840                         {
1841                            SortPrimitive temp = *poly2;
1842                            memmove(triangles+1, triangles, sizeof(SortPrimitive)*q);
1843                            triangles[0] = temp;
1844                            triangles[0].marked = true;
1845                            p = -1;
1846                            break;
1847                         }
1848                      }
1849                   }
1850                }
1851             }
1852          }
1853       }
1854
1855       {
1856          int p;
1857          for(p = nTriangles-1; p>=0; p--)
1858          {
1859             SortPrimitive * poly1 = &triangles[p];
1860             int q;
1861
1862             for(q = nTriangles-1; q>=0; q--)
1863             {
1864                if(q != p)
1865                {
1866                   SortPrimitive * poly2 = &triangles[q];
1867                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1868                   {
1869                      if(poly1->ShouldBeSwapped(poly2))
1870                      {
1871                         if(!poly2->ShouldBeSwapped(poly1))
1872                         {
1873                            SortPrimitive temp = *poly2;
1874                            memmove(triangles + q, triangles + q + 1, sizeof(SortPrimitive)*q);
1875                            temp.marked = true;
1876                            triangles[nTriangles-1] = temp;
1877                            p = nTriangles;
1878                            break;
1879                         }
1880                      }
1881                   }
1882                }
1883             }
1884          }
1885       }
1886
1887
1888       {
1889          for(c=0; c<nTriangles; c++)
1890          {
1891             int b;
1892             SortPrimitive * poly1 = &triangles[c];
1893
1894             // for(b=0; b<nTriangles; b++)
1895             //for(b=c+1; b<nTriangles; b++)
1896             b = c+1;
1897             if(b<this.nTriangles)
1898             {
1899                SortPrimitive * poly2 = &this.triangles[b];
1900
1901                if(poly1->ZOverlap(poly2) && poly1->ShouldBeSwapped(poly2))
1902                {
1903                   SortPrimitive temp = *poly1;
1904                   *poly1 = *poly2;
1905                   *poly2 = temp;
1906                }
1907             }
1908            }
1909       }
1910       */
1911
1912    }
1913 };
1914 #endif
1915
1916 bool IsDriverTextMode(const char * driverName)
1917 {
1918    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
1919    return driver ? driver.textMode : false;
1920 }
1921
1922 bool IsDriverPrinter(const char * driverName)
1923 {
1924    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
1925    return driver ? driver.printer : false;
1926 }
1927
1928 static ColorAlpha defaultPalette[256] =
1929 {
1930    // 16 Colors
1931 /*
1932    0xFF000000,0xFF000080,0xFF008000,0xFF008080,0xFF800000,0xFF800080,0xFF808000,0xFFC0C0C0,
1933    0xFF808080,0xFF0000FF,0xFF00FF00,0xFF00FFFF,0xFFFF0000,0xFFFF00FF,0xFFFFFF00,0xFFFFFFFF,
1934 */
1935    0xFF000000,0xFF0000AA,0xFF00AA00,0xFF00AAAA,0xFFAA0000,0xFFAA00AA,0xFFAAAA00,0xFFABABAB,
1936    0xFF555555,0xFF5555FF,0xFF55FF55,0xFF55FFFF,0xFFFF5555,0xFFFF55FF,0xFFFFFF55,0xFFFFFFFF,
1937
1938    // 6 x 6 x 6 Color Cube
1939    0xFF000000, 0xFF000033, 0xFF000066, 0xFF000099, 0xFF0000CC, 0xFF0000FF,
1940    0xFF003300, 0xFF003333, 0xFF003366, 0xFF003399, 0xFF0033CC, 0xFF0033FF,
1941    0xFF006600, 0xFF006633, 0xFF006666, 0xFF006699, 0xFF0066CC, 0xFF0066FF,
1942    0xFF009900, 0xFF009933, 0xFF009966, 0xFF009999, 0xFF0099CC, 0xFF0099FF,
1943    0xFF00CC00, 0xFF00CC33, 0xFF00CC66, 0xFF00CC99, 0xFF00CCCC, 0xFF00CCFF,
1944    0xFF00FF00, 0xFF00FF33, 0xFF00FF66, 0xFF00FF99, 0xFF00FFCC, 0xFF00FFFF,
1945
1946    0xFF330000, 0xFF330033, 0xFF330066, 0xFF330099, 0xFF3300CC, 0xFF3300FF,
1947    0xFF333300, 0xFF333333, 0xFF333366, 0xFF333399, 0xFF3333CC, 0xFF3333FF,
1948    0xFF336600, 0xFF336633, 0xFF336666, 0xFF336699, 0xFF3366CC, 0xFF3366FF,
1949    0xFF339900, 0xFF339933, 0xFF339966, 0xFF339999, 0xFF3399CC, 0xFF3399FF,
1950    0xFF33CC00, 0xFF33CC33, 0xFF33CC66, 0xFF33CC99, 0xFF33CCCC, 0xFF33CCFF,
1951    0xFF33FF00, 0xFF33FF33, 0xFF33FF66, 0xFF33FF99, 0xFF33FFCC, 0xFF33FFFF,
1952
1953    0xFF660000, 0xFF660033, 0xFF660066, 0xFF660099, 0xFF6600CC, 0xFF6600FF,
1954    0xFF663300, 0xFF663333, 0xFF663366, 0xFF663399, 0xFF6633CC, 0xFF6633FF,
1955    0xFF666600, 0xFF666633, 0xFF666666, 0xFF666699, 0xFF6666CC, 0xFF6666FF,
1956    0xFF669900, 0xFF669933, 0xFF669966, 0xFF669999, 0xFF6699CC, 0xFF6699FF,
1957    0xFF66CC00, 0xFF66CC33, 0xFF66CC66, 0xFF66CC99, 0xFF66CCCC, 0xFF66CCFF,
1958    0xFF66FF00, 0xFF66FF33, 0xFF66FF66, 0xFF66FF99, 0xFF66FFCC, 0xFF66FFFF,
1959
1960    0xFF990000, 0xFF990033, 0xFF990066, 0xFF990099, 0xFF9900CC, 0xFF9900FF,
1961    0xFF993300, 0xFF993333, 0xFF993366, 0xFF993399, 0xFF9933CC, 0xFF9933FF,
1962    0xFF996600, 0xFF996633, 0xFF996666, 0xFF996699, 0xFF9966CC, 0xFF9966FF,
1963    0xFF999900, 0xFF999933, 0xFF999966, 0xFF999999, 0xFF9999CC, 0xFF9999FF,
1964    0xFF99CC00, 0xFF99CC33, 0xFF99CC66, 0xFF99CC99, 0xFF99CCCC, 0xFF99CCFF,
1965    0xFF99FF00, 0xFF99FF33, 0xFF99FF66, 0xFF99FF99, 0xFF99FFCC, 0xFF99FFFF,
1966
1967    0xFFCC0000, 0xFFCC0033, 0xFFCC0066, 0xFFCC0099, 0xFFCC00CC, 0xFFCC00FF,
1968    0xFFCC3300, 0xFFCC3333, 0xFFCC3366, 0xFFCC3399, 0xFFCC33CC, 0xFFCC33FF,
1969    0xFFCC6600, 0xFFCC6633, 0xFFCC6666, 0xFFCC6699, 0xFFCC66CC, 0xFFCC66FF,
1970    0xFFCC9900, 0xFFCC9933, 0xFFCC9966, 0xFFCC9999, 0xFFCC99CC, 0xFFCC99FF,
1971    0xFFCCCC00, 0xFFCCCC33, 0xFFCCCC66, 0xFFCCCC99, 0xFFCCCCCC, 0xFFCCCCFF,
1972    0xFFCCFF00, 0xFFCCFF33, 0xFFCCFF66, 0xFFCCFF99, 0xFFCCFFCC, 0xFFCCFFFF,
1973
1974    0xFFFF0000, 0xFFFF0033, 0xFFFF0066, 0xFFFF0099, 0xFFFF00CC, 0xFFFF00FF,
1975    0xFFFF3300, 0xFFFF3333, 0xFFFF3366, 0xFFFF3399, 0xFFFF33CC, 0xFFFF33FF,
1976    0xFFFF6600, 0xFFFF6633, 0xFFFF6666, 0xFFFF6699, 0xFFFF66CC, 0xFFFF66FF,
1977    0xFFFF9900, 0xFFFF9933, 0xFFFF9966, 0xFFFF9999, 0xFFFF99CC, 0xFFFF99FF,
1978    0xFFFFCC00, 0xFFFFCC33, 0xFFFFCC66, 0xFFFFCC99, 0xFFFFCCCC, 0xFFFFCCFF,
1979    0xFFFFFF00, 0xFFFFFF33, 0xFFFFFF66, 0xFFFFFF99, 0xFFFFFFCC, 0xFFFFFFFF,
1980
1981    // 16 Shades of gray
1982    0xFF000000,0xFF101010,0xFF202020,0xFF303030,0xFF404040,0xFF505050,0xFF606060,0xFF707070,
1983    0xFF808080,0xFF909090,0xFFA0A0A0,0xFFB0B0B0,0xFFC0C0C0,0xFFD0D0D0,0xFFE0E0E0,0xFFF0F0F0,
1984
1985    // Some more grays
1986    0xFF080808,0xFF101010,0xFF585858,0xFF606060,0xFFA8A8A8,0xFFB0B0B0,0xFFF8F8F8,0xFFFFFFFF
1987
1988 /*
1989    // Red
1990    0xFF080000,0xFF100000,0xFF180000,0xFF200000,0xFF280000,0xFF300000,0xFF380000,0xFF400000,
1991    0xFF480000,0xFF500000,0xFF580000,0xFF600000,0xFF680000,0xFF700000,0xFF780000,0xFF800000,
1992    0xFF880000,0xFF900000,0xFF980000,0xFFA00000,0xFFA80000,0xFFB00000,0xFFB80000,0xFFC00000,
1993    0xFFC80000,0xFFD00000,0xFFD80000,0xFFE00000,0xFFE80000,0xFFF00000,0xFFF80000,0xFFFF0000,
1994    // Green
1995    0xFF000800,0xFF001000,0xFF001800,0xFF002000,0xFF002800,0xFF003000,0xFF003800,0xFF004000,
1996    0xFF004800,0xFF005000,0xFF005800,0xFF006000,0xFF006800,0xFF007000,0xFF007800,0xFF008000,
1997    0xFF008800,0xFF009000,0xFF009800,0xFF00A000,0xFF00A800,0xFF00B000,0xFF00B800,0xFF00C000,
1998    0xFF00C800,0xFF00D000,0xFF00D800,0xFF00E000,0xFF00E800,0xFF00F000,0xFF00F800,0xFF00FF00,
1999    // Cyan
2000    0xFF000808,0xFF001010,0xFF001818,0xFF002020,0xFF002828,0xFF003030,0xFF003838,0xFF004040,
2001    0xFF004848,0xFF005050,0xFF005858,0xFF006060,0xFF006868,0xFF007070,0xFF007878,0xFF008080,
2002    0xFF008888,0xFF009090,0xFF009898,0xFF00A0A0,0xFF00A8A8,0xFF00B0B0,0xFF00B8B8,0xFF00C0C0,
2003    0xFF00C8C8,0xFF00D0D0,0xFF00D8D8,0xFF00E0E0,0xFF00E8E8,0xFF00F0F0,0xFF00F8F8,0xFF00FFFF,
2004    // Blue
2005    0xFF000008,0xFF000010,0xFF000018,0xFF000020,0xFF000028,0xFF000030,0xFF000038,0xFF000040,
2006    0xFF000048,0xFF000050,0xFF000058,0xFF000060,0xFF000068,0xFF000070,0xFF000078,0xFF000080,
2007    0xFF000088,0xFF000090,0xFF000098,0xFF0000A0,0xFF0000A8,0xFF0000B0,0xFF0000B8,0xFF0000C0,
2008    0xFF0000C8,0xFF0000D0,0xFF0000D8,0xFF0000E0,0xFF0000E8,0xFF0000F0,0xFF0000F8,0xFF0000FF,
2009    // Magenta
2010    0xFF080008,0xFF100010,0xFF180018,0xFF200020,0xFF280028,0xFF300030,0xFF380038,0xFF400040,
2011    0xFF480048,0xFF500050,0xFF580058,0xFF600060,0xFF680068,0xFF700070,0xFF780078,0xFF800080,
2012    0xFF880088,0xFF900090,0xFF980098,0xFFA000A0,0xFFA800A8,0xFFB000B0,0xFFB800B8,0xFFC000C0,
2013    0xFFC800C8,0xFFD000D0,0xFFD800D8,0xFFE000E0,0xFFE800E8,0xFFF000F0,0xFFF800F8,0xFFFF00FF,
2014    // Yellow
2015    0xFF080800,0xFF101000,0xFF181800,0xFF202000,0xFF282800,0xFF303000,0xFF383800,0xFF404000,
2016    0xFF484800,0xFF505000,0xFF585800,0xFF606000,0xFF686800,0xFF707000,0xFF787800,0xFF808000,
2017    0xFF888800,0xFF909000,0xFF989800,0xFFA0A000,0xFFA8A800,0xFFB0B000,0xFFB8B800,0xFFC0C000,
2018    0xFFC8C800,0xFFD0D000,0xFFD8D800,0xFFE0E000,0xFFE8E800,0xFFF0F000,0xFFF8F800,0xFFFFFF00,
2019    // White
2020    0xFF080808,0xFF101010,0xFF181818,0xFF202020,0xFF282828,0xFF303030,0xFF383838,0xFF404040,
2021    0xFF484848,0xFF505050,0xFF585858,0xFF606060,0xFF686868,0xFF707070,0xFF787878,0xFF808080,
2022    0xFF888888,0xFF909090,0xFF989898,0xFFA0A0A0,0xFFA8A8A8,0xFFB0B0B0,0xFFB8B8B8,0xFFC0C0C0,
2023    0xFFC8C8C8,0xFFD0D0D0,0xFFD8D8D8,0xFFE0E0E0,0xFFE8E8E8,0xFFF0F0F0,0xFFF8F8F8,0xFFFFFFFF
2024 */
2025 };
2026
2027 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
2028 static Material defaultMaterial
2029 {
2030    opacity = 1.0f,
2031    diffuse = { 1.0f, 1.0f, 1.0f },
2032    ambient = { 1.0f, 1.0f, 1.0f },
2033    flags = { doubleSided = true, noFog = true };
2034 };
2035 #endif
2036
2037 static byte colorDepthShifts[PixelFormat] = { 0,0,1,1,1,2,0,1,2 };
2038 static Size resolutions[Resolution] =
2039 {
2040    {80,25},
2041    {320,200},{320,240},{320,400},{360,480},
2042    {400,256},{400,300},{512,256},{512,384},
2043    {640,200},{640,350},{640,400},{640,480},
2044    {720,348},{800,600},{856,480},{960,720},
2045    {1024,768},{1152,864},{1280,1024},{1600,1200},
2046    {768,480}
2047 };
2048 static int colorDepths[PixelFormat] = {4,8,12,15,16,32,8,16,32};
2049
2050 // --- Query utilities ---
2051
2052 public int GetResolutionWidth(Resolution resolution)
2053 {
2054    return resolutions[resolution].w;
2055 }
2056
2057 public int GetResolutionHeight(Resolution resolution)
2058 {
2059    return resolutions[resolution].h;
2060 }
2061
2062 public int GetDepthBits(PixelFormat colorDepth)
2063 {
2064    return colorDepths[colorDepth];
2065 }
2066
2067 public byte GetColorDepthShifts(PixelFormat format)
2068 {
2069    return colorDepthShifts[format];
2070 }
2071
2072 public ColorAlpha * GetDefaultPalette()
2073 {
2074    return (ColorAlpha *)defaultPalette;
2075 }
2076 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
2077 public Material GetDefaultMaterial()
2078 {
2079    return defaultMaterial;
2080 }
2081 #endif
2082 public int BestColorMatch(ColorAlpha * palette, int start, int end, Color rgb)
2083 {
2084    int best = 0;
2085    if(palette)
2086    {
2087       int c;
2088       int bestscore = MAXINT,score;
2089       byte r = rgb.r, g = rgb.g, b = rgb.b;
2090       Color current;
2091
2092       for(c = start; c <= end; c++)
2093       {
2094          int dr,dg,db;
2095          current = palette[c];
2096          if(rgb && !c) continue;
2097          dr = r - current.r;
2098          dg = g - current.g;
2099          db = b - current.b;
2100          score = Abs(dr) + Abs(dg) + Abs(db);
2101          if(score <= bestscore)
2102          {
2103             bestscore = score;
2104             best = c;
2105          }
2106       }
2107    }
2108    return best;
2109 }
2110
2111 // had to move this here due to compiler ordering issue for "get property" symbol
2112 subclass(DisplayDriver) GetDisplayDriver(const char * driverName)
2113 {
2114    if(driverName)
2115    {
2116       OldLink link;
2117       for(link = class(DisplayDriver).derivatives.first; link; link = link.next)
2118       {
2119          subclass(DisplayDriver) displayDriver = link.data;
2120          if(displayDriver && displayDriver.name && !strcmp(displayDriver.name, driverName))
2121             return displayDriver;
2122       }
2123    }
2124    return null;
2125 }
2126
2127 DisplaySystem GetDisplaySystem(const char * driverName)
2128 {
2129    subclass(DisplayDriver) displayDriver = GetDisplayDriver(driverName);
2130    return displayDriver ? displayDriver.displaySystem : null;
2131 }