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