f6a4a5fd022c0c0d7993f5211556f70b1619389c
[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       display3D.mesh = mesh;
801    }
802
803    bool DrawMesh(Object object)
804    {
805       bool result = false;
806       if(display3D.selection)
807          result = display3D.PickMesh(object, null);
808       else
809       {
810          Mesh mesh = object.mesh;
811          Material objectMaterial = object.material;
812
813          if(mesh.groups.first)
814          {
815             PrimitiveGroup group;
816             displaySystem.driver.SelectMesh(this, mesh);
817             display3D.mesh = mesh;
818
819             for(group = mesh.groups.first; group; group = group.next)
820             {
821                Material material = group.material ? group.material : objectMaterial;
822                if(!material) material = defaultMaterial;
823
824                if(material != display3D.material)
825                {
826                   display3D.material = material;
827                   displaySystem.driver.ApplyMaterial(this, material, mesh);
828                }
829
830                // *** Render Vertex Arrays ***
831                displaySystem.driver.DrawPrimitives(this, (PrimitiveSingle *)&group.type, mesh);
832             }
833          }
834
835          if(object.flags.translucent)
836          {
837             Matrix matrix;
838             Matrix inverse, inverseTranspose;
839             int c;
840
841             if(object.flags.viewSpace)
842                matrix = object.matrix;
843             else
844             {
845                Camera camera = display3D.camera;
846                Matrix temp = object.matrix;
847                temp.m[3][0] -= camera.cPosition.x;
848                temp.m[3][1] -= camera.cPosition.y;
849                temp.m[3][2] -= camera.cPosition.z;
850                matrix.Multiply(temp, camera.viewMatrix);
851             }
852
853             inverse.Inverse(matrix);
854             inverseTranspose.Transpose(inverse);
855
856             for(c = 0; c < mesh.nPrimitives; c++)
857             {
858                PrimitiveSingle * triangle = &mesh.primitives[c];
859                SortPrimitive * sort;
860                Plane * plane = &triangle->plane;
861                if(display3D.nTriangles >= display3D.maxTriangles)
862                {
863                   display3D.maxTriangles = display3D.maxTriangles ? (display3D.maxTriangles * 3 / 2) : 32768;
864                   display3D.triangles = renew display3D.triangles SortPrimitive[display3D.maxTriangles];
865                }
866                sort = &display3D.triangles[display3D.nTriangles++];
867                sort->object = object;
868                sort->triangle = triangle;
869                sort->middle.MultMatrix(triangle->middle, matrix);
870                sort->middle.z *= -1;
871                // sort->plane.MultMatrix(triangle->plane, inverseTranspose);
872                sort->plane.d = plane->a * inverseTranspose.m[0][3] +
873                                plane->b * inverseTranspose.m[1][3] +
874                                plane->c * inverseTranspose.m[2][3] +
875                                plane->d * inverseTranspose.m[3][3];
876             }
877          }
878          else
879          {
880             int c;
881             displaySystem.driver.SelectMesh(this, mesh);
882             display3D.mesh = mesh;
883
884             for(c = 0; c<mesh.nPrimitives; c++)
885             {
886                PrimitiveSingle * primitive = &mesh.primitives[c];
887
888                Material material = primitive->material ? primitive->material : objectMaterial;
889                if(!material) material = defaultMaterial;
890
891                if(material != display3D.material)
892                {
893                   display3D.material = material;
894                   displaySystem.driver.ApplyMaterial(this, material, mesh);
895                }
896
897                displaySystem.driver.DrawPrimitives(this, primitive, display3D.mesh);
898             }
899          }
900          result = true;
901       }
902       return result;
903    }
904
905    bool IsObjectVisible(Object object)
906    {
907       Plane * planes;
908       if(display3D.selection || !display3D.camera)
909          planes = object.flags.viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes;
910       else
911          planes = object.flags.viewSpace ? display3D.camera.viewClippingPlanes : display3D.camera.worldClippingPlanes;
912        return object.InsideFrustum(planes) != outside;
913    }
914
915    bool DrawObject(Object object)
916    {
917       bool result = false;
918       if(object && object.volume)
919       {
920          Object child;
921          FrustumPlacement visible;
922          Plane * planes;
923          Camera camera = display3D.camera;
924
925          if(display3D.selection || !camera)
926             planes = object.flags.viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes;
927          else
928             planes = object.flags.viewSpace ? camera.viewClippingPlanes : camera.worldClippingPlanes;
929
930          visible = object.InsideFrustum(planes);
931
932          if(visible || display3D.pickingPlanes)
933          {
934             if(display3D.collectingHits && object.tag)
935             {
936                /*if(object.flags.root)
937                   this.tags[display3D.tagIndex] = object.tag;
938                else if(object.tag)
939                   this.tags[++display3D.tagIndex] = object.tag;
940                   */
941                display3D.tags[display3D.tagIndex++] = object.tag;
942             }
943
944             if(object.flags.mesh && object.mesh)
945             {
946                if(!display3D.selection && displaySystem.driver.PushMatrix)
947                   displaySystem.driver.PushMatrix(this);
948
949 #if ENABLE_GL_FFP
950                if(object.mesh.tangents && object.mesh.normals)
951                {
952                   Mesh mesh = object.mesh;
953                   if(!glCaps_shaders)
954                   {
955                      int count = mesh.nVertices;
956                      Vector3Df * normals = mesh.normals;
957                      Vector3Df * vertices = mesh.vertices;
958                      ColorRGB * lightVectors;
959                      Vector3Df * tangents = mesh.tangents;
960                      int i;
961                      float * l = display3D.light0Pos;
962                      Vector3Df light { l[0], l[1], l[2] };
963                      Matrix o = object.matrix;
964                      Matrix t, inv = camera.viewMatrix;
965                      Vector3D ot { };
966                      Vector3D cPos = camera.cPosition;
967                      Vector3D pos;
968                      bool positional = l[3] ? true : false;
969
970                      inv.Scale(1.0/nearPlane, -1.0/nearPlane,-1.0/nearPlane);
971
972                      pos.MultMatrix(cPos, camera.viewMatrix);
973
974                      ot.x = o.m[3][0] + pos.x;
975                      ot.y = o.m[3][1] + pos.y;
976                      ot.z = o.m[3][2] + pos.z;
977                      o.m[3][0] = 0;
978                      o.m[3][1] = 0;
979                      o.m[3][2] = 0;
980                      t.Multiply(o, inv);
981                      inv = t;
982                      t.Transpose(inv);
983                      inv.Inverse(t);
984
985                      mesh.Allocate({ lightVectors = true }, mesh.nVertices, displaySystem);
986                      mesh.Lock({ lightVectors = true });
987                      lightVectors = mesh.lightVectors;
988                      for(i = 0; i < count; i++)
989                      {
990                         Vector3Df tangent1 = tangents[i*2 + 0];
991                         Vector3Df tangent2 = tangents[i*2 + 1];
992                         Vector3Df normal = normals[i];
993                         Vector3Df tTangent1, tTangent2, tNormal;
994
995                         tTangent1.MultMatrix(tangent1, inv);
996                         tTangent2.MultMatrix(tangent2, inv);
997                         tNormal  .MultMatrix(normal,   inv);
998
999                         tTangent1.Normalize(tTangent1);
1000                         tTangent2.Normalize(tTangent2);
1001                         tNormal  .Normalize(tNormal);
1002
1003                         {
1004                            Matrix tbn
1005                            { {
1006                                tTangent1.x, tTangent2.x, tNormal.x, 0,
1007                                tTangent1.y, tTangent2.y, tNormal.y, 0,
1008                                tTangent1.z, tTangent2.z, tNormal.z, 1
1009                            } };
1010                            Vector3Df n;
1011                            if(positional)
1012                            {
1013                               Vector3Df tPos = vertices[i];
1014                               tPos.x += ot.x, tPos.y += ot.y, tPos.z += ot.z;
1015
1016                               // Subtract vertex from light for positional lights
1017                               light.x = l[0] - tPos.x;
1018                               light.y = l[1] + tPos.y;
1019                               light.z = l[2] - tPos.z;
1020                            }
1021                            n.MultMatrix(light, tbn);
1022                            if(positional)
1023                               n.Normalize(n);
1024                            lightVectors[i] = { n.x / 2 + 0.5f, n.y / 2 + 0.5f, n.z / 2 + 0.5f };
1025                         }
1026                      }
1027                      mesh.Unlock({ lightVectors = true });
1028
1029                      // Create normalization cube map
1030                      /*
1031                      if(!mesh.normMap)
1032                         mesh.normMap = { };
1033                      {
1034                         int w = 256, h = 256, d = 256;
1035                         Vector3Df min = mesh.min, max = mesh.max;
1036                         Vector3Df delta
1037                         {
1038                            (max.x - min.x) / w,
1039                            (max.y - min.y) / h,
1040                            (max.z - min.z) / d
1041                         };
1042                         int i;
1043                         for(i = 0; i < 6; i++)
1044                         {
1045                            Bitmap face = i > 0 ? { } : mesh.normMap;
1046                            int x, y;
1047                            ColorAlpha * p;
1048                            face.Free();
1049                            face.Allocate(null, w, h, 0, pixelFormat888, false);
1050                            face.driverData = mesh.normMap.driverData;
1051                            p = (ColorAlpha *)face.picture;
1052                            for(y = 0; y < h; y++)
1053                            {
1054                               for(x = 0; x < w; x++, p++)
1055                               {
1056                                  Vector3Df v { min.x + x * delta.x, min.y + y * delta.y, min.z };
1057                                  v.Normalize(v);
1058                                  *p = ColorAlpha { 255, {
1059                                        (byte)((v.x / 2.0 + 0.5) * 255),
1060                                        (byte)((v.y / 2.0 + 0.5) * 255),
1061                                        (byte)((v.z / 2.0 + 0.5) * 255) } };
1062                              }
1063                            }
1064                            displaySystem.driver.MakeDDBitmap(displaySystem, face, true, (i + 1));
1065                            if(i > 0)
1066                            {
1067                               face.driverData = 0;
1068                               delete face;
1069                            }
1070                         }
1071                      }
1072                      */
1073                   }
1074                   else
1075                      mesh.Free({ lightVectors = true });
1076                }
1077 #endif
1078
1079                SetTransform(object.matrix, object.flags.viewSpace);
1080                if(display3D.selection)
1081                {
1082                   if(visible == intersecting || display3D.intersecting)
1083                   {
1084                      Vector3D rayIntersect;
1085                      if(display3D.PickMesh(object, rayIntersect))
1086                      {
1087                         if(display3D.intersecting)
1088                         {
1089                            Vector3D wresult, vresult;
1090                            wresult.MultMatrix(rayIntersect, object.matrix);
1091                            if(!object.flags.viewSpace)
1092                               camera.TransformPoint(vresult, wresult);
1093                            else
1094                               vresult = wresult;
1095
1096                            if(vresult.z < display3D.rayIntersect.z)
1097                               display3D.rayIntersect = vresult;
1098                            display3D.intersected = true;
1099                         }
1100                         result = true;
1101                      }
1102                   }
1103                   else
1104                      result = true;
1105                }
1106                else
1107                {
1108                   result |= DrawMesh(object);
1109                   if(displaySystem.driver.PopMatrix)
1110                      displaySystem.driver.PopMatrix(this, true);
1111                }
1112                if(display3D.collectingHits && result /*&& object.tag*/)
1113                {
1114                   int c;
1115                   HitRecord hit = (HitRecord)new0 byte[sizeof(class HitRecord) + sizeof(void *) * (display3D.tagIndex/*+1*/)];
1116                   display3D.hitList.Add(hit);
1117                   hit.pos = display3D.hitList.count-1;
1118                   hit.numTags = display3D.tagIndex /*+ 1*/;
1119                   for(c = 0; c</*=*/display3D.tagIndex; c++)
1120                   {
1121                      hit.tags[c] = display3D.tags[c];
1122                   }
1123
1124                   if(!object.flags.viewSpace)
1125                      camera.TransformPoint(hit.center, object.wcenter);
1126                   else
1127                      hit.center = object.wcenter;
1128                }
1129             }
1130
1131             for(child = object.children.first; child; child = child.next)
1132                result |= DrawObject(child);
1133
1134             if(display3D.collectingHits && /*!object.flags.root && */object.tag)
1135                display3D.tagIndex--;
1136          }
1137       }
1138       return result;
1139    }
1140
1141    void DrawTranslucency(void)
1142    {
1143       if(display3D.camera)
1144       {
1145          // *** Render translucent primitives ***
1146          if(display3D.nTriangles)
1147          {
1148             Matrix * matrix = null;
1149             int c;
1150
1151             blend = true;
1152
1153             display3D.SortTriangles();
1154
1155             depthWrite = false;
1156
1157             displaySystem.driver.PushMatrix(this);
1158             for(c=0; c<display3D.nTriangles; c++)
1159             {
1160                SortPrimitive * sort = &display3D.triangles[c];
1161                Mesh mesh = sort->object.mesh;
1162                PrimitiveSingle * primitive = sort->triangle;
1163                Material material;
1164
1165                if(&sort->object.matrix != matrix)
1166                {
1167                   matrix = &sort->object.matrix;
1168
1169                   displaySystem.driver.PopMatrix(this, false);
1170                   displaySystem.driver.PushMatrix(this);
1171                   SetTransform(matrix, sort->object.flags.viewSpace);
1172                }
1173                if(mesh != display3D.mesh)
1174                {
1175                   displaySystem.driver.SelectMesh(this, mesh);
1176                   display3D.mesh = mesh;
1177                }
1178
1179                material = primitive->material ? primitive->material : sort->object.material;
1180                if(!material) material = defaultMaterial;
1181
1182                if(material != display3D.material)
1183                {
1184                   displaySystem.driver.ApplyMaterial(this, material, display3D.mesh);
1185                   display3D.material = material;
1186                }
1187
1188                /*
1189                {
1190                   Material testMaterial { };
1191                   float amount;
1192
1193                   amount = (display3D.triangles[0].middle.z - display3D.triangles[c].middle.z) /
1194                      (display3D.triangles[0].middle.z - display3D.triangles[display3D.nTriangles-1].middle.z);
1195
1196                   testMaterial.flags.doubleSided = { doubleSided = true, translucent = true };
1197                   testMaterial.diffuse.a = 1;
1198                   testMaterial.emissive.r = testMaterial.emissive.g = testMaterial.emissive.b = amount;
1199                   testMaterial.baseMap = material->baseMap;
1200
1201                   displaySystem.driver.ApplyMaterial(this, testMaterial, display3D.mesh);
1202                }
1203                */
1204
1205                // *** Render primitive ***
1206                // if(sort->plane.d > 0)
1207                displaySystem.driver.DrawPrimitives(this, primitive, display3D.mesh);
1208             }
1209             displaySystem.driver.PopMatrix(this, true);
1210
1211             display3D.nTriangles = 0;
1212             blend = false;
1213          }
1214       }
1215    }
1216
1217    // --- Picking ---
1218    void StartSelection(int pickX, int pickY, int pickW, int pickH)
1219    {
1220       if(!display3D)
1221       {
1222          display3D = Display3D { };
1223       }
1224       display3D.pickX = (float)pickX;
1225       display3D.pickY = (float)pickY;
1226       display3D.pickWidth = (float)pickW;
1227       display3D.pickHeight = (float)pickH;
1228       display3D.selection = true;
1229    }
1230
1231    void CollectHits(void)
1232    {
1233       display3D.collectingHits = true;
1234    }
1235
1236    int GetHits(OldList list)
1237    {
1238       display3D.collectingHits = false;
1239       display3D.hitList.Sort(HitRecord::Compare, null);
1240       list = display3D.hitList;
1241       display3D.hitList.Clear();
1242       return list.count;
1243    }
1244
1245    void IntersectPolygons(void)
1246    {
1247       display3D.rayIntersect = { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1248       display3D.intersected = false;
1249       display3D.intersecting = true;
1250    }
1251
1252    bool GetIntersect(Vector3D intersect)
1253    {
1254       intersect = display3D.rayIntersect;
1255       display3D.intersecting = false;
1256       return display3D.intersected;
1257    }
1258
1259    void StopSelection(void)
1260    {
1261       display3D.selection = false;
1262    }
1263
1264    // --- Rendering States ---
1265    property FillModeValue fillMode { set { displaySystem.driver.SetRenderState(this, fillMode, value); } };
1266    property bool depthTest    { set { displaySystem.driver.SetRenderState(this, depthTest, value); } };
1267    property bool depthWrite   { set { displaySystem.driver.SetRenderState(this, depthWrite, value); } };
1268    property float fogDensity  { set { displaySystem.driver.SetRenderState(this, fogDensity, RenderStateFloat { value }.ui); } };
1269    property Color fogColor    { set { displaySystem.driver.SetRenderState(this, fogColor, value); } };
1270    property bool blend        { set { displaySystem.driver.SetRenderState(this, blend, value); } };
1271    property Color ambient     { set { displaySystem.driver.SetRenderState(this, ambient, value); } };
1272    property bool alphaWrite   { set { displaySystem.driver.SetRenderState(this, alphaWrite, value); } };
1273    property bool antiAlias    { set { displaySystem.driver.SetRenderState(this, antiAlias, value); } };
1274    property bool vSync        { set { displaySystem.driver.SetRenderState(this, vSync, value); } };
1275
1276    property bool pickingPlanes { set { display3D.pickingPlanes = value; } };
1277 #endif
1278    property DisplayFlags flags { get { return displaySystem.flags; } }
1279    property PixelFormat pixelFormat { get { return /*alphaBlend ? pixelFormat888 : */displaySystem.pixelFormat; } }
1280    property bool alphaBlend { set { alphaBlend = value; } get { return alphaBlend; } };
1281    property bool useSharedMemory { set { useSharedMemory = value; } get { return useSharedMemory; } };
1282    property void * systemWindow { get { return window; } };
1283    property DisplaySystem displaySystem { get { return displaySystem; } };
1284 #ifndef ECERE_VANILLA
1285    property GLCapabilities glCapabilities
1286    {
1287       get { return ((OGLDisplay)driverData).capabilities; }
1288       set
1289       {
1290          glCapabilities = value;
1291          if(driverData && displaySystem.driver == class(OpenGLDisplayDriver))
1292          {
1293             OGLDisplay oglDisplay = driverData;
1294             if(!oglDisplay.originalCapabilities.fixedFunction)
1295                value.shaders = true;
1296             if(!oglDisplay.originalCapabilities.shaders)
1297                value.fixedFunction = true;
1298             // Disable things that don't work with shaders
1299             if(value.shaders)
1300             {
1301                value.fixedFunction = false;
1302                value.legacy = false;
1303                value.immediate = false;
1304             }
1305             oglDisplay.capabilities = oglDisplay.originalCapabilities & value;
1306
1307             Lock(true);
1308             OpenGLDisplayDriver::initialDisplaySetup(this, true, false);
1309             Unlock();
1310          }
1311       }
1312    };
1313 #endif
1314
1315    int width, height;
1316    void * driverData;
1317
1318 private:
1319
1320    DisplaySystem displaySystem;
1321    void * window;
1322
1323 #if !defined(__EMSCRIPTEN__)
1324    Mutex mutex { };
1325 #endif
1326    int current;
1327
1328 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1329    Display3D display3D;
1330 #endif
1331    bool alphaBlend;
1332    void * windowDriverData;
1333    bool useSharedMemory;
1334    GLCapabilities glCapabilities;
1335    glCapabilities = { true, true, true, true, true, true, true, true };
1336 };
1337
1338 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1339 private class Display3D : struct
1340 {
1341    // 3D Display
1342    int nTriangles;
1343    SortPrimitive * triangles;
1344    int maxTriangles;
1345    Vector3D points[MAX_CLIP_POINTS];
1346    Vector3D newPoints[MAX_CLIP_POINTS];
1347    byte goodPoints[MAX_CLIP_POINTS];
1348
1349    Material material;
1350    Mesh mesh;
1351    Camera camera;
1352    Plane viewPickingPlanes[ClippingPlane], worldPickingPlanes[ClippingPlane];
1353    Plane localPickingPlanes[ClippingPlane];
1354
1355    bool collectingHits, selection, intersecting, intersected, pickingPlanes;
1356    float pickX, pickY, pickWidth, pickHeight;
1357    OldList hitList;
1358    void * tags[64];
1359    int tagIndex;
1360
1361    Line rayView, rayWorld, rayLocal;
1362    Vector3D rayIntersect;
1363    float light0Pos[4];
1364
1365    ~Display3D()
1366    {
1367       delete triangles;
1368    }
1369
1370    int _SetLights(Display display, Object object, int id)
1371    {
1372       if(id < NumberOfLights)
1373       {
1374          Object child;
1375
1376          if(object.flags.light && !object.light.flags.off)
1377             display.SetLight(id++, object.light);
1378
1379          for(child = object.children.first; child; child = child.next)
1380          {
1381             id = _SetLights(display, child, id);
1382          }
1383       }
1384       return id;
1385    }
1386
1387    //#define TRESHOLD           -1
1388    //#define TRESHOLD           -0.25
1389    #define TRESHOLD           -0.0025
1390
1391    bool PickPrimitives(Mesh mesh, PrimitiveSingle primitive, Vector3D rayDiff, Vector3D rayIntersect)
1392    {
1393       Plane * planes = localPickingPlanes;
1394       int c = 0;
1395       int nIndex = 1, nPoints = 1;
1396       int offset = 0;
1397       bool result = false;
1398       Vector3D * points = this.points;
1399       Vector3D * newPoints = this.newPoints;
1400       byte * goodPoints = this.goodPoints;
1401       int nVertices = primitive.type.vertexRange ? primitive.nVertices : primitive.nIndices;
1402       int strip = 1;
1403       Vector3Df tmp;
1404       bool i32bit = primitive.type.indices32bit;
1405       uint32 * indices32 = primitive.indices32;
1406       uint16 * indices16 = primitive.indices;
1407
1408       switch(primitive.type.primitiveType)
1409       {
1410          case triangles: nIndex = 3; nPoints = 3; break;
1411          case quads: nIndex = 4; nPoints = 4; break;
1412          case triStrip:
1413          case triFan:
1414             nIndex = 1; nPoints = 3;
1415             offset = 2;
1416             tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first] : mesh.vertices[(i32bit ? indices32[0] : indices16[0])];
1417             points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1418             tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first+1] : mesh.vertices[(i32bit ? indices32[1] : indices16[1])];
1419             points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1420             break;
1421       }
1422
1423       for(c = offset; c<nVertices; c += nIndex)
1424       {
1425          bool outside = false;
1426          if(!pickingPlanes)
1427          {
1428             int p;
1429             int n = nPoints;
1430             int i;
1431
1432             if(primitive.type.vertexRange)
1433             {
1434                if(primitive.type.primitiveType == triStrip)
1435                {
1436                   tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 1) : (c - 2)];
1437                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1438                   tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 2) : (c - 1)];
1439                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1440                }
1441                else if(primitive.type.primitiveType == triFan)
1442                {
1443                   tmp = mesh.vertices[primitive.first + 0];
1444                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1445                   tmp = mesh.vertices[primitive.first + c - 1];
1446                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1447                }
1448                for(i = 0; i<nIndex; i++)
1449                {
1450                   tmp = mesh.vertices[primitive.first + c+i];
1451                   points[i + offset] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1452                }
1453             }
1454             else
1455             {
1456                if(primitive.type.primitiveType == triStrip)
1457                {
1458                   i = (c & 1) ? (c - 1) : (c - 2);
1459                   tmp = mesh.vertices[(i32bit ? indices32[i] : indices16[i])];
1460                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1461
1462                   i = (c & 1) ? (c - 2) : (c - 1);
1463                   tmp = mesh.vertices[(i32bit ? indices32[i] : indices16[i])];
1464                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1465                }
1466                else if(primitive.type.primitiveType == triFan)
1467                {
1468                   tmp = mesh.vertices[(i32bit ? indices32[0] : indices16[0])];
1469                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1470                   tmp = mesh.vertices[(i32bit ? indices32[c-1] : indices16[c-1])];
1471                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1472                }
1473                for(i = 0; i<nIndex; i++)
1474                {
1475                   tmp = mesh.vertices[(i32bit ? indices32[c+i] : indices16[c+i])];
1476                   points[i + offset] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1477                }
1478             }
1479
1480             for(p = 0; p < 6; p++)
1481             {
1482                Plane * plane = &planes[p];
1483                int i;
1484                int numGoodPoints = 0;
1485
1486                memset(goodPoints, 0, n);
1487                for(i = 0; i < n; i++)
1488                {
1489                   double dot = plane->normal.DotProduct(points[i]);
1490                   double distance = dot + plane->d;
1491                               if(distance > TRESHOLD)
1492                   {
1493                      numGoodPoints++;
1494                      goodPoints[i] = 1;
1495                   }
1496                       }
1497                if(!numGoodPoints)
1498                {
1499                   outside = true;
1500                   break;
1501                }
1502                if(numGoodPoints < n)
1503                {
1504                   // Clip the polygon
1505                   int newN = 0;
1506                   int lastGood = -1;
1507                   int j;
1508
1509                   for(j = 0; j<n; )
1510                   {
1511                      if(goodPoints[j])
1512                      {
1513                         newPoints[newN++] = points[j];
1514                         lastGood = j++;
1515                      }
1516                      else
1517                      {
1518                         Line edge;
1519                         int next;
1520
1521                         if(lastGood == -1)
1522                            for(lastGood = n-1; !goodPoints[lastGood]; lastGood--);
1523
1524                         edge.p0 = points[lastGood];
1525                         edge.delta.Subtract(points[j], edge.p0);
1526                         plane->IntersectLine(edge, newPoints[newN++]);
1527
1528                         for(next = j+1; next != j; next++)
1529                         {
1530                            if(next == n) next = 0;
1531                            if(goodPoints[next])
1532                            {
1533                               int prev = next - 1;
1534                               if(prev < 0) prev = n-1;
1535
1536                               edge.p0 = points[prev];
1537                               edge.delta.Subtract(points[next], edge.p0);
1538                               plane->IntersectLine(edge, newPoints[newN++]);
1539                               break;
1540                            }
1541                         }
1542                         if(next <= j)
1543                            break;
1544                         else
1545                            j = next;
1546                      }
1547                   }
1548                   // Use the new points
1549                   memcpy(points, newPoints, newN * sizeof(Vector3D));
1550                   n = newN;
1551                }
1552               }
1553          }
1554          if(!outside)
1555          {
1556                    result = true;
1557
1558             // TODO: Implement intersection with TriStrip, TriFan...
1559             if(intersecting)
1560             {
1561                // Intersect primitives
1562                Plane plane;
1563                Vector3D intersect, diff;
1564                int i0 = c, i1 = c+1, i2 = c+2;
1565
1566                if(primitive.type.primitiveType == triStrip)
1567                {
1568                   i0 = (c & 1) ? (c - 1) : (c - 2);
1569                   i1 = (c & 1) ? (c - 2) : (c - 1);
1570                   i2 = c;
1571                }
1572                else if(primitive.type.primitiveType == triFan)
1573                {
1574                   i0 = 0;
1575                   i1 = c - 1;
1576                   i2 = c;
1577                }
1578
1579                if(primitive.type.vertexRange)
1580                   plane.FromPointsf(
1581                      mesh.vertices[primitive.first + i0],
1582                      mesh.vertices[primitive.first + i1],
1583                      mesh.vertices[primitive.first + i2]);
1584                else
1585                   plane.FromPointsf(
1586                      mesh.vertices[(i32bit ? indices32[i0] : indices16[i0])],
1587                      mesh.vertices[(i32bit ? indices32[i1] : indices16[i1])],
1588                      mesh.vertices[(i32bit ? indices32[i2] : indices16[i2])]);
1589
1590                plane.IntersectLine(rayLocal, intersect);
1591                diff.Subtract(intersect, rayLocal.p0);
1592                diff.x /= rayLocal.delta.x;
1593                diff.y /= rayLocal.delta.y;
1594                diff.z /= rayLocal.delta.z;
1595                if(diff.x < rayDiff.x || diff.y < rayDiff.y || diff.z < rayDiff.z)
1596                {
1597                   rayDiff = diff;
1598                   rayIntersect = intersect;
1599                }
1600             }
1601             else
1602                break;
1603          }
1604
1605          switch(primitive.type)
1606          {
1607             case triStrip:
1608                points[strip] = points[2];
1609                strip ^= 1;
1610                break;
1611             case triFan:
1612                points[1] = points[2];
1613                break;
1614          }
1615       }
1616       return result;
1617    }
1618
1619    bool PickMesh(Object object, Vector3D rayIntersect)
1620    {
1621       Mesh mesh = object.mesh;
1622       bool result = false;
1623       Vector3D rayDiff { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1624       if(rayIntersect != null)
1625          rayIntersect = { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1626
1627       if(mesh.groups.first)
1628       {
1629          PrimitiveGroup group;
1630
1631          for(group = mesh.groups.first; group; group = group.next)
1632          {
1633             if(PickPrimitives(mesh, (PrimitiveSingle *)&group.type, rayDiff, rayIntersect))
1634             {
1635                result = true;
1636                if(!intersecting)
1637                   break;
1638             }
1639          }
1640       }
1641       else
1642       {
1643          int c;
1644          for(c = 0; c < mesh.nPrimitives; c++)
1645          {
1646             if(PickPrimitives(mesh, mesh.primitives[c], rayDiff, rayIntersect))
1647             {
1648                result = true;
1649                if(!intersecting)
1650                   break;
1651             }
1652          }
1653       }
1654       return result;
1655    }
1656
1657    void SortTriangles(void)
1658    {
1659       Matrix matrix;
1660       Object object = null;
1661       int c;
1662       for(c=0; c<nTriangles; c++)
1663       {
1664          SortPrimitive * sort = &triangles[c];
1665          Mesh mesh = sort->object.mesh;
1666          PrimitiveSingle * primitive = sort->triangle;
1667                 Vector3Df min { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1668          Vector3Df max { -MAXFLOAT, -MAXFLOAT, -MAXFLOAT };
1669          int v;
1670          bool ix32 = primitive->type.indices32bit;
1671          if(object != sort->object)
1672          {
1673             object = sort->object;
1674             if(object.flags.viewSpace)
1675                matrix = object.matrix;
1676             else
1677             {
1678                Camera camera = this.camera;
1679                Matrix temp = object.matrix;
1680                temp.m[3][0] -= camera.cPosition.x;
1681                temp.m[3][1] -= camera.cPosition.y;
1682                temp.m[3][2] -= camera.cPosition.z;
1683                matrix.Multiply(temp, camera.viewMatrix);
1684             }
1685          }
1686
1687          for(v = 0; v<primitive->nIndices; v++)
1688          {
1689             Vector3Df * local = &mesh.vertices[ix32 ? primitive->indices32[v] : primitive->indices[v]];
1690             Vector3Df vertex;
1691
1692             vertex.MultMatrix(local, &matrix);
1693
1694                         if(vertex.x > max.x) max.x = vertex.x;
1695             if(vertex.y > max.y) max.y = vertex.y;
1696             if(vertex.z > max.z) max.z = vertex.z;
1697                         if(vertex.x < min.x) min.x = vertex.x;
1698             if(vertex.y < min.y) min.y = vertex.y;
1699             if(vertex.z < min.z) min.z = vertex.z;
1700                 }
1701
1702          sort->min = min;
1703          sort->max = max;
1704
1705          sort->marked = false;
1706         }
1707
1708    /*
1709       Logf("========= Before Sort ==========\n");
1710       for(c=0; c<nTriangles; c++)
1711       {
1712          SortPrimitive * sort = &triangles[c];
1713          int v;
1714          Mesh mesh = sort->mesh;
1715          PrimitiveSingle * primitive = sort->triangle;
1716
1717          Logf("Triangle %d (%s):\n", c, primitive->material->name);
1718
1719          for(v = 0; v<primitive->nIndices; v++)
1720          {
1721             Vector3Df * local = &mesh.vertices[primitive->indices[v]];
1722             Vector3Df vertex;
1723
1724
1725             vertex.<MultMatrix(local, sort->matrix);
1726
1727             Logf("Vertex %d:", v);
1728
1729             // Logf("   Local %f, %f, %f:\n", local->x, local->y, local->z);
1730             Logf("   View  %f, %f, %f:\n", vertex.x, vertex.y, vertex.z);
1731
1732                 }
1733
1734          Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z);
1735          Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z);
1736          Logf("\n", c);
1737       }*/
1738
1739       // *** Sort translucent primitives ***
1740       qsort((void*) triangles, nTriangles, sizeof(SortPrimitive), SortPrimitive::Compare);
1741    /*
1742       Logf("\n\n========= After Sort ==========\n");
1743       for(c=0; c<nTriangles; c++)
1744       {
1745          SortPrimitive * sort = &triangles[c];
1746          int v;
1747          Mesh mesh = sort->mesh;
1748          PrimitiveSingle * primitive = sort->triangle;
1749
1750          Logf("Triangle %d (%s):\n", c, primitive->material->name);
1751
1752          for(v = 0; v<primitive->nIndices; v++)
1753          {
1754             Vector3Df * local = &mesh.vertices[primitive->indices[v]];
1755             Vector3Df vertex;
1756
1757
1758             vertex.MultMatrix(local, sort->matrix);
1759
1760             Logf("Vertex %d:", v);
1761
1762             // Logf("   Local %f, %f, %f:\n", local->x, local->y, local->z);
1763             Logf("   View  %f, %f, %f:\n", vertex.x, vertex.y, vertex.z);
1764
1765                 }
1766
1767          Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z);
1768          Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z);
1769          Logf("\n", c);
1770       }
1771    */
1772       //exit(0);
1773
1774    /*
1775    If all five tests fail for a particular Q,
1776    then P might obscure Q. Now Q must be tested.
1777    First, the algorithm checks if Q has been "marked."
1778    If Q is marked, then Q was "moved around" in the list
1779    during a previous iteration of the loop. The algorithm
1780    only allows a polygon to be moved once, to avoid the possibility
1781    of infinite loops. If Q is not marked, it is tested to see
1782    if it might obscure P. If Q cannot obscure P, then Q is possibly
1783    behind P and so it is good candidate to be drawn next.
1784    Therefore, the algorithm "abandons" the current P (that is, it
1785    stops testing Q's against the current P) and moves the current
1786    Q to the end of the list to become the next P.
1787    */
1788    /*
1789       {
1790          int p;
1791          for(p = 0; p<nTriangles; p++)
1792          {
1793             SortPrimitive * poly1 = &triangles[p];
1794             int q;
1795
1796             for(q = p+1; q<nTriangles; q++)
1797             {
1798                if(q != p)
1799                {
1800                   SortPrimitive * poly2 = &triangles[q];
1801                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1802                   {
1803                      if(poly1->ShouldBeSwapped(poly2))
1804                      {
1805                         if(!poly2->ShouldBeSwapped(poly1))
1806                         {
1807                            SortPrimitive temp = *poly2;
1808                            memmove(triangles+1, triangles, sizeof(SortPrimitive)*q);
1809                            triangles[0] = temp;
1810                            triangles[0].marked = true;
1811                            p = 0;
1812                            break;
1813                         }
1814                      }
1815                   }
1816                }
1817             }
1818          }
1819       }
1820
1821       {
1822          int p;
1823          for(p = 0; p<nTriangles; p++)
1824          {
1825             SortPrimitive * poly1 = &triangles[p];
1826             int q;
1827
1828             // for(q = p+1; q<nTriangles; q++)
1829             for(q = 0; q<nTriangles; q++)
1830             {
1831                if(q != p)
1832                {
1833                   SortPrimitive * poly2 = &triangles[q];
1834                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1835                   {
1836                      if(poly1->ShouldBeSwapped(poly2))
1837                      {
1838                         if(!poly2->ShouldBeSwapped(poly1))
1839                         {
1840                            SortPrimitive temp = *poly2;
1841                            memmove(triangles+1, triangles, sizeof(SortPrimitive)*q);
1842                            triangles[0] = temp;
1843                            triangles[0].marked = true;
1844                            p = -1;
1845                            break;
1846                         }
1847                      }
1848                   }
1849                }
1850             }
1851          }
1852       }
1853
1854       {
1855          int p;
1856          for(p = nTriangles-1; p>=0; p--)
1857          {
1858             SortPrimitive * poly1 = &triangles[p];
1859             int q;
1860
1861             for(q = nTriangles-1; q>=0; q--)
1862             {
1863                if(q != p)
1864                {
1865                   SortPrimitive * poly2 = &triangles[q];
1866                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1867                   {
1868                      if(poly1->ShouldBeSwapped(poly2))
1869                      {
1870                         if(!poly2->ShouldBeSwapped(poly1))
1871                         {
1872                            SortPrimitive temp = *poly2;
1873                            memmove(triangles + q, triangles + q + 1, sizeof(SortPrimitive)*q);
1874                            temp.marked = true;
1875                            triangles[nTriangles-1] = temp;
1876                            p = nTriangles;
1877                            break;
1878                         }
1879                      }
1880                   }
1881                }
1882             }
1883          }
1884       }
1885
1886
1887       {
1888          for(c=0; c<nTriangles; c++)
1889          {
1890             int b;
1891             SortPrimitive * poly1 = &triangles[c];
1892
1893             // for(b=0; b<nTriangles; b++)
1894             //for(b=c+1; b<nTriangles; b++)
1895             b = c+1;
1896             if(b<this.nTriangles)
1897             {
1898                SortPrimitive * poly2 = &this.triangles[b];
1899
1900                if(poly1->ZOverlap(poly2) && poly1->ShouldBeSwapped(poly2))
1901                {
1902                   SortPrimitive temp = *poly1;
1903                   *poly1 = *poly2;
1904                   *poly2 = temp;
1905                }
1906             }
1907            }
1908       }
1909       */
1910
1911    }
1912 };
1913 #endif
1914
1915 bool IsDriverTextMode(const char * driverName)
1916 {
1917    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
1918    return driver ? driver.textMode : false;
1919 }
1920
1921 bool IsDriverPrinter(const char * driverName)
1922 {
1923    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
1924    return driver ? driver.printer : false;
1925 }
1926
1927 static ColorAlpha defaultPalette[256] =
1928 {
1929    // 16 Colors
1930 /*
1931    0xFF000000,0xFF000080,0xFF008000,0xFF008080,0xFF800000,0xFF800080,0xFF808000,0xFFC0C0C0,
1932    0xFF808080,0xFF0000FF,0xFF00FF00,0xFF00FFFF,0xFFFF0000,0xFFFF00FF,0xFFFFFF00,0xFFFFFFFF,
1933 */
1934    0xFF000000,0xFF0000AA,0xFF00AA00,0xFF00AAAA,0xFFAA0000,0xFFAA00AA,0xFFAAAA00,0xFFABABAB,
1935    0xFF555555,0xFF5555FF,0xFF55FF55,0xFF55FFFF,0xFFFF5555,0xFFFF55FF,0xFFFFFF55,0xFFFFFFFF,
1936
1937    // 6 x 6 x 6 Color Cube
1938    0xFF000000, 0xFF000033, 0xFF000066, 0xFF000099, 0xFF0000CC, 0xFF0000FF,
1939    0xFF003300, 0xFF003333, 0xFF003366, 0xFF003399, 0xFF0033CC, 0xFF0033FF,
1940    0xFF006600, 0xFF006633, 0xFF006666, 0xFF006699, 0xFF0066CC, 0xFF0066FF,
1941    0xFF009900, 0xFF009933, 0xFF009966, 0xFF009999, 0xFF0099CC, 0xFF0099FF,
1942    0xFF00CC00, 0xFF00CC33, 0xFF00CC66, 0xFF00CC99, 0xFF00CCCC, 0xFF00CCFF,
1943    0xFF00FF00, 0xFF00FF33, 0xFF00FF66, 0xFF00FF99, 0xFF00FFCC, 0xFF00FFFF,
1944
1945    0xFF330000, 0xFF330033, 0xFF330066, 0xFF330099, 0xFF3300CC, 0xFF3300FF,
1946    0xFF333300, 0xFF333333, 0xFF333366, 0xFF333399, 0xFF3333CC, 0xFF3333FF,
1947    0xFF336600, 0xFF336633, 0xFF336666, 0xFF336699, 0xFF3366CC, 0xFF3366FF,
1948    0xFF339900, 0xFF339933, 0xFF339966, 0xFF339999, 0xFF3399CC, 0xFF3399FF,
1949    0xFF33CC00, 0xFF33CC33, 0xFF33CC66, 0xFF33CC99, 0xFF33CCCC, 0xFF33CCFF,
1950    0xFF33FF00, 0xFF33FF33, 0xFF33FF66, 0xFF33FF99, 0xFF33FFCC, 0xFF33FFFF,
1951
1952    0xFF660000, 0xFF660033, 0xFF660066, 0xFF660099, 0xFF6600CC, 0xFF6600FF,
1953    0xFF663300, 0xFF663333, 0xFF663366, 0xFF663399, 0xFF6633CC, 0xFF6633FF,
1954    0xFF666600, 0xFF666633, 0xFF666666, 0xFF666699, 0xFF6666CC, 0xFF6666FF,
1955    0xFF669900, 0xFF669933, 0xFF669966, 0xFF669999, 0xFF6699CC, 0xFF6699FF,
1956    0xFF66CC00, 0xFF66CC33, 0xFF66CC66, 0xFF66CC99, 0xFF66CCCC, 0xFF66CCFF,
1957    0xFF66FF00, 0xFF66FF33, 0xFF66FF66, 0xFF66FF99, 0xFF66FFCC, 0xFF66FFFF,
1958
1959    0xFF990000, 0xFF990033, 0xFF990066, 0xFF990099, 0xFF9900CC, 0xFF9900FF,
1960    0xFF993300, 0xFF993333, 0xFF993366, 0xFF993399, 0xFF9933CC, 0xFF9933FF,
1961    0xFF996600, 0xFF996633, 0xFF996666, 0xFF996699, 0xFF9966CC, 0xFF9966FF,
1962    0xFF999900, 0xFF999933, 0xFF999966, 0xFF999999, 0xFF9999CC, 0xFF9999FF,
1963    0xFF99CC00, 0xFF99CC33, 0xFF99CC66, 0xFF99CC99, 0xFF99CCCC, 0xFF99CCFF,
1964    0xFF99FF00, 0xFF99FF33, 0xFF99FF66, 0xFF99FF99, 0xFF99FFCC, 0xFF99FFFF,
1965
1966    0xFFCC0000, 0xFFCC0033, 0xFFCC0066, 0xFFCC0099, 0xFFCC00CC, 0xFFCC00FF,
1967    0xFFCC3300, 0xFFCC3333, 0xFFCC3366, 0xFFCC3399, 0xFFCC33CC, 0xFFCC33FF,
1968    0xFFCC6600, 0xFFCC6633, 0xFFCC6666, 0xFFCC6699, 0xFFCC66CC, 0xFFCC66FF,
1969    0xFFCC9900, 0xFFCC9933, 0xFFCC9966, 0xFFCC9999, 0xFFCC99CC, 0xFFCC99FF,
1970    0xFFCCCC00, 0xFFCCCC33, 0xFFCCCC66, 0xFFCCCC99, 0xFFCCCCCC, 0xFFCCCCFF,
1971    0xFFCCFF00, 0xFFCCFF33, 0xFFCCFF66, 0xFFCCFF99, 0xFFCCFFCC, 0xFFCCFFFF,
1972
1973    0xFFFF0000, 0xFFFF0033, 0xFFFF0066, 0xFFFF0099, 0xFFFF00CC, 0xFFFF00FF,
1974    0xFFFF3300, 0xFFFF3333, 0xFFFF3366, 0xFFFF3399, 0xFFFF33CC, 0xFFFF33FF,
1975    0xFFFF6600, 0xFFFF6633, 0xFFFF6666, 0xFFFF6699, 0xFFFF66CC, 0xFFFF66FF,
1976    0xFFFF9900, 0xFFFF9933, 0xFFFF9966, 0xFFFF9999, 0xFFFF99CC, 0xFFFF99FF,
1977    0xFFFFCC00, 0xFFFFCC33, 0xFFFFCC66, 0xFFFFCC99, 0xFFFFCCCC, 0xFFFFCCFF,
1978    0xFFFFFF00, 0xFFFFFF33, 0xFFFFFF66, 0xFFFFFF99, 0xFFFFFFCC, 0xFFFFFFFF,
1979
1980    // 16 Shades of gray
1981    0xFF000000,0xFF101010,0xFF202020,0xFF303030,0xFF404040,0xFF505050,0xFF606060,0xFF707070,
1982    0xFF808080,0xFF909090,0xFFA0A0A0,0xFFB0B0B0,0xFFC0C0C0,0xFFD0D0D0,0xFFE0E0E0,0xFFF0F0F0,
1983
1984    // Some more grays
1985    0xFF080808,0xFF101010,0xFF585858,0xFF606060,0xFFA8A8A8,0xFFB0B0B0,0xFFF8F8F8,0xFFFFFFFF
1986
1987 /*
1988    // Red
1989    0xFF080000,0xFF100000,0xFF180000,0xFF200000,0xFF280000,0xFF300000,0xFF380000,0xFF400000,
1990    0xFF480000,0xFF500000,0xFF580000,0xFF600000,0xFF680000,0xFF700000,0xFF780000,0xFF800000,
1991    0xFF880000,0xFF900000,0xFF980000,0xFFA00000,0xFFA80000,0xFFB00000,0xFFB80000,0xFFC00000,
1992    0xFFC80000,0xFFD00000,0xFFD80000,0xFFE00000,0xFFE80000,0xFFF00000,0xFFF80000,0xFFFF0000,
1993    // Green
1994    0xFF000800,0xFF001000,0xFF001800,0xFF002000,0xFF002800,0xFF003000,0xFF003800,0xFF004000,
1995    0xFF004800,0xFF005000,0xFF005800,0xFF006000,0xFF006800,0xFF007000,0xFF007800,0xFF008000,
1996    0xFF008800,0xFF009000,0xFF009800,0xFF00A000,0xFF00A800,0xFF00B000,0xFF00B800,0xFF00C000,
1997    0xFF00C800,0xFF00D000,0xFF00D800,0xFF00E000,0xFF00E800,0xFF00F000,0xFF00F800,0xFF00FF00,
1998    // Cyan
1999    0xFF000808,0xFF001010,0xFF001818,0xFF002020,0xFF002828,0xFF003030,0xFF003838,0xFF004040,
2000    0xFF004848,0xFF005050,0xFF005858,0xFF006060,0xFF006868,0xFF007070,0xFF007878,0xFF008080,
2001    0xFF008888,0xFF009090,0xFF009898,0xFF00A0A0,0xFF00A8A8,0xFF00B0B0,0xFF00B8B8,0xFF00C0C0,
2002    0xFF00C8C8,0xFF00D0D0,0xFF00D8D8,0xFF00E0E0,0xFF00E8E8,0xFF00F0F0,0xFF00F8F8,0xFF00FFFF,
2003    // Blue
2004    0xFF000008,0xFF000010,0xFF000018,0xFF000020,0xFF000028,0xFF000030,0xFF000038,0xFF000040,
2005    0xFF000048,0xFF000050,0xFF000058,0xFF000060,0xFF000068,0xFF000070,0xFF000078,0xFF000080,
2006    0xFF000088,0xFF000090,0xFF000098,0xFF0000A0,0xFF0000A8,0xFF0000B0,0xFF0000B8,0xFF0000C0,
2007    0xFF0000C8,0xFF0000D0,0xFF0000D8,0xFF0000E0,0xFF0000E8,0xFF0000F0,0xFF0000F8,0xFF0000FF,
2008    // Magenta
2009    0xFF080008,0xFF100010,0xFF180018,0xFF200020,0xFF280028,0xFF300030,0xFF380038,0xFF400040,
2010    0xFF480048,0xFF500050,0xFF580058,0xFF600060,0xFF680068,0xFF700070,0xFF780078,0xFF800080,
2011    0xFF880088,0xFF900090,0xFF980098,0xFFA000A0,0xFFA800A8,0xFFB000B0,0xFFB800B8,0xFFC000C0,
2012    0xFFC800C8,0xFFD000D0,0xFFD800D8,0xFFE000E0,0xFFE800E8,0xFFF000F0,0xFFF800F8,0xFFFF00FF,
2013    // Yellow
2014    0xFF080800,0xFF101000,0xFF181800,0xFF202000,0xFF282800,0xFF303000,0xFF383800,0xFF404000,
2015    0xFF484800,0xFF505000,0xFF585800,0xFF606000,0xFF686800,0xFF707000,0xFF787800,0xFF808000,
2016    0xFF888800,0xFF909000,0xFF989800,0xFFA0A000,0xFFA8A800,0xFFB0B000,0xFFB8B800,0xFFC0C000,
2017    0xFFC8C800,0xFFD0D000,0xFFD8D800,0xFFE0E000,0xFFE8E800,0xFFF0F000,0xFFF8F800,0xFFFFFF00,
2018    // White
2019    0xFF080808,0xFF101010,0xFF181818,0xFF202020,0xFF282828,0xFF303030,0xFF383838,0xFF404040,
2020    0xFF484848,0xFF505050,0xFF585858,0xFF606060,0xFF686868,0xFF707070,0xFF787878,0xFF808080,
2021    0xFF888888,0xFF909090,0xFF989898,0xFFA0A0A0,0xFFA8A8A8,0xFFB0B0B0,0xFFB8B8B8,0xFFC0C0C0,
2022    0xFFC8C8C8,0xFFD0D0D0,0xFFD8D8D8,0xFFE0E0E0,0xFFE8E8E8,0xFFF0F0F0,0xFFF8F8F8,0xFFFFFFFF
2023 */
2024 };
2025
2026 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
2027 static Material defaultMaterial
2028 {
2029    opacity = 1.0f,
2030    diffuse = { 1.0f, 1.0f, 1.0f },
2031    ambient = { 1.0f, 1.0f, 1.0f },
2032    flags = { doubleSided = true, noFog = true };
2033 };
2034 #endif
2035
2036 static byte colorDepthShifts[PixelFormat] = { 0,0,1,1,1,2,0,1,2 };
2037 static Size resolutions[Resolution] =
2038 {
2039    {80,25},
2040    {320,200},{320,240},{320,400},{360,480},
2041    {400,256},{400,300},{512,256},{512,384},
2042    {640,200},{640,350},{640,400},{640,480},
2043    {720,348},{800,600},{856,480},{960,720},
2044    {1024,768},{1152,864},{1280,1024},{1600,1200},
2045    {768,480}
2046 };
2047 static int colorDepths[PixelFormat] = {4,8,12,15,16,32,8,16,32};
2048
2049 // --- Query utilities ---
2050
2051 public int GetResolutionWidth(Resolution resolution)
2052 {
2053    return resolutions[resolution].w;
2054 }
2055
2056 public int GetResolutionHeight(Resolution resolution)
2057 {
2058    return resolutions[resolution].h;
2059 }
2060
2061 public int GetDepthBits(PixelFormat colorDepth)
2062 {
2063    return colorDepths[colorDepth];
2064 }
2065
2066 public byte GetColorDepthShifts(PixelFormat format)
2067 {
2068    return colorDepthShifts[format];
2069 }
2070
2071 public ColorAlpha * GetDefaultPalette()
2072 {
2073    return (ColorAlpha *)defaultPalette;
2074 }
2075 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
2076 public Material GetDefaultMaterial()
2077 {
2078    return defaultMaterial;
2079 }
2080 #endif
2081 public int BestColorMatch(ColorAlpha * palette, int start, int end, Color rgb)
2082 {
2083    int best = 0;
2084    if(palette)
2085    {
2086       int c;
2087       int bestscore = MAXINT,score;
2088       byte r = rgb.r, g = rgb.g, b = rgb.b;
2089       Color current;
2090
2091       for(c = start; c <= end; c++)
2092       {
2093          int dr,dg,db;
2094          current = palette[c];
2095          if(rgb && !c) continue;
2096          dr = r - current.r;
2097          dg = g - current.g;
2098          db = b - current.b;
2099          score = Abs(dr) + Abs(dg) + Abs(db);
2100          if(score <= bestscore)
2101          {
2102             bestscore = score;
2103             best = c;
2104          }
2105       }
2106    }
2107    return best;
2108 }
2109
2110 // had to move this here due to compiler ordering issue for "get property" symbol
2111 subclass(DisplayDriver) GetDisplayDriver(const char * driverName)
2112 {
2113    if(driverName)
2114    {
2115       OldLink link;
2116       for(link = class(DisplayDriver).derivatives.first; link; link = link.next)
2117       {
2118          subclass(DisplayDriver) displayDriver = link.data;
2119          if(displayDriver && displayDriver.name && !strcmp(displayDriver.name, driverName))
2120             return displayDriver;
2121       }
2122    }
2123    return null;
2124 }
2125
2126 DisplaySystem GetDisplaySystem(const char * driverName)
2127 {
2128    subclass(DisplayDriver) displayDriver = GetDisplayDriver(driverName);
2129    return displayDriver ? displayDriver.displaySystem : null;
2130 }