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