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