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