ecere/com; gfx: Improvements to cross-compiling to 32 bit
[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    int width, height;
1122    void * driverData;
1123
1124 private:
1125
1126    DisplaySystem displaySystem;
1127    void * window;
1128
1129    Mutex mutex { };
1130    int current;
1131
1132 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1133    Display3D display3D;
1134 #endif
1135    bool alphaBlend;
1136    void * windowDriverData;
1137    bool useSharedMemory;
1138 };
1139
1140 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1141 private class Display3D
1142 {
1143    // 3D Display
1144    int nTriangles;
1145    SortPrimitive * triangles;
1146    int maxTriangles;
1147    Vector3D points[MAX_CLIP_POINTS];
1148    Vector3D newPoints[MAX_CLIP_POINTS];
1149    byte goodPoints[MAX_CLIP_POINTS];
1150
1151    Material material;
1152    Mesh mesh;
1153    Camera camera;
1154    Plane viewPickingPlanes[ClippingPlane], worldPickingPlanes[ClippingPlane];
1155    Plane localPickingPlanes[ClippingPlane];
1156
1157    bool collectingHits, selection, intersecting, intersected, pickingPlanes;
1158    float pickX, pickY, pickWidth, pickHeight;
1159    OldList hitList;
1160    void * tags[64];
1161    int tagIndex;
1162
1163    Line rayView, rayWorld, rayLocal;
1164    Vector3D rayIntersect;
1165
1166    ~Display3D()
1167    {
1168       delete triangles;
1169    }
1170
1171    int _SetLights(Display display, Object object, int id)
1172    {
1173       if(id < NumberOfLights)
1174       {
1175          Object child;
1176
1177          if(object.flags.light && !object.light.flags.off)
1178             display.SetLight(id++, object.light);
1179          
1180          for(child = object.children.first; child; child = child.next)
1181          {
1182             id = _SetLights(display, child, id);
1183          }   
1184       }
1185       return id;
1186    }
1187
1188    //#define TRESHOLD           -1
1189    //#define TRESHOLD           -0.25
1190    #define TRESHOLD           -0.0025
1191
1192    bool PickPrimitives(Mesh mesh, PrimitiveSingle primitive, Vector3D rayDiff, Vector3D rayIntersect)
1193    {
1194       Plane * planes = localPickingPlanes;
1195       int c = 0;
1196       int nIndex = 1, nPoints = 1;
1197       int offset = 0;
1198       bool result = false;      
1199       Vector3D * points = this.points;
1200       Vector3D * newPoints = this.newPoints;
1201       byte * goodPoints = this.goodPoints;
1202       int nVertices = primitive.type.vertexRange ? primitive.nVertices : primitive.nIndices;
1203       int strip = 1;
1204       Vector3Df tmp;
1205       bool i32bit = primitive.type.indices32bit;
1206       uint32 * indices32 = primitive.indices;
1207       uint16 * indices16 = primitive.indices;
1208       
1209       switch(primitive.type.primitiveType)
1210       {
1211          case triangles: nIndex = 3; nPoints = 3; break;
1212          case quads: nIndex = 4; nPoints = 4; break;
1213          case triStrip:
1214          case triFan:
1215             nIndex = 1; nPoints = 3;
1216             offset = 2;
1217             tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first] : mesh.vertices[(i32bit ? indices32[0] : indices16[0])];
1218             points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1219             tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first+1] : mesh.vertices[(i32bit ? indices32[1] : indices16[1])];
1220             points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1221             break;
1222       }
1223       
1224       for(c = offset; c<nVertices; c += nIndex)
1225       {
1226          bool outside = false;
1227          if(!pickingPlanes)
1228          {
1229             int p;
1230             int n = nPoints;
1231             int i;
1232
1233             if(primitive.type.vertexRange)
1234             {
1235                if(primitive.type.primitiveType == triStrip)
1236                {
1237                   tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 1) : (c - 2)];
1238                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1239                   tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 2) : (c - 1)];
1240                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1241                }
1242                else if(primitive.type.primitiveType == triFan)
1243                {
1244                   tmp = mesh.vertices[primitive.first + 0];
1245                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1246                   tmp = mesh.vertices[primitive.first + c - 1];
1247                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1248                }
1249                for(i = 0; i<nIndex; i++)
1250                {
1251                   tmp = mesh.vertices[primitive.first + c+i];
1252                   points[i + offset] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1253                }
1254             }
1255             else
1256             {
1257                if(primitive.type.primitiveType == triStrip)
1258                {
1259                   i = (c & 1) ? (c - 1) : (c - 2);
1260                   tmp = mesh.vertices[(i32bit ? indices32[i] : indices16[i])];
1261                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1262
1263                   i = (c & 1) ? (c - 2) : (c - 1);
1264                   tmp = mesh.vertices[(i32bit ? indices32[i] : indices16[i])];
1265                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1266                }
1267                else if(primitive.type.primitiveType == triFan)
1268                {
1269                   tmp = mesh.vertices[(i32bit ? indices32[0] : indices16[0])];
1270                   points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1271                   tmp = mesh.vertices[(i32bit ? indices32[c-1] : indices16[c-1])];
1272                   points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1273                }
1274                for(i = 0; i<nIndex; i++)
1275                {
1276                   tmp = mesh.vertices[(i32bit ? indices32[c+i] : indices16[c+i])];
1277                   points[i + offset] = { (double)tmp.x, (double)tmp.y, (double)tmp.z };
1278                }
1279             }
1280
1281               for(p = 0; p < 6; p++) 
1282             {
1283                Plane * plane = &planes[p];
1284                int i;
1285                int numGoodPoints = 0;
1286        
1287                memset(goodPoints, 0, n);
1288                       for(i = 0; i < n; i++) 
1289                {
1290                   double dot = plane->normal.DotProduct(points[i]);
1291                   double distance = dot + plane->d;
1292                               if(distance > TRESHOLD)
1293                   {
1294                      numGoodPoints++;
1295                      goodPoints[i] = 1;
1296                   }
1297                       }
1298                if(!numGoodPoints)
1299                {
1300                   outside = true;
1301                   break;
1302                }
1303                if(numGoodPoints < n)
1304                {
1305                   // Clip the polygon
1306                   int newN = 0;
1307                   int lastGood = -1;
1308                   int j;
1309
1310                   for(j = 0; j<n; )
1311                   {
1312                      if(goodPoints[j])
1313                      {
1314                         newPoints[newN++] = points[j];
1315                         lastGood = j++;
1316                      }
1317                      else
1318                      {
1319                         Line edge; 
1320                         int next;
1321
1322                         if(lastGood == -1)
1323                            for(lastGood = n-1; !goodPoints[lastGood]; lastGood--);
1324
1325                         edge.p0 = points[lastGood];
1326                         edge.delta.Subtract(points[j], edge.p0);
1327                         plane->IntersectLine(edge, newPoints[newN++]);
1328
1329                         for(next = j+1; next != j; next++)
1330                         {
1331                            if(next == n) next = 0;
1332                            if(goodPoints[next])
1333                            {
1334                               int prev = next - 1;
1335                               if(prev < 0) prev = n-1;
1336
1337                               edge.p0 = points[prev];
1338                               edge.delta.Subtract(points[next], edge.p0);
1339                               plane->IntersectLine(edge, newPoints[newN++]);
1340                               break;
1341                            }
1342                         }
1343                         if(next <= j)
1344                            break;
1345                         else
1346                            j = next;
1347                      }
1348                   }
1349                   // Use the new points
1350                   memcpy(points, newPoints, newN * sizeof(Vector3D));
1351                   n = newN;
1352                }
1353               }
1354          }
1355          if(!outside)
1356          {
1357                    result = true;
1358
1359             // TODO: Implement intersection with TriStrip, TriFan...
1360             if(intersecting)
1361             {
1362                // Intersect primitives
1363                Plane plane;
1364                Vector3D intersect, diff;
1365                int i0 = c, i1 = c+1, i2 = c+2;
1366
1367                if(primitive.type.primitiveType == triStrip)
1368                {
1369                   i0 = (c & 1) ? (c - 1) : (c - 2);
1370                   i1 = (c & 1) ? (c - 2) : (c - 1);
1371                   i2 = c;
1372                }
1373                else if(primitive.type.primitiveType == triFan)
1374                {
1375                   i0 = 0;
1376                   i1 = c - 1;
1377                   i2 = c;
1378                }
1379
1380                if(primitive.type.vertexRange)
1381                   plane.FromPointsf(
1382                      mesh.vertices[primitive.first + i0],
1383                      mesh.vertices[primitive.first + i1],
1384                      mesh.vertices[primitive.first + i2]);
1385                else
1386                   plane.FromPointsf(
1387                      mesh.vertices[(i32bit ? indices32[i0] : indices16[i0])],
1388                      mesh.vertices[(i32bit ? indices32[i1] : indices16[i1])],
1389                      mesh.vertices[(i32bit ? indices32[i2] : indices16[i2])]);
1390
1391                plane.IntersectLine(rayLocal, intersect);
1392                diff.Subtract(intersect, rayLocal.p0);
1393                diff.x /= rayLocal.delta.x;
1394                diff.y /= rayLocal.delta.y;
1395                diff.z /= rayLocal.delta.z;
1396                if(diff.x < rayDiff.x || diff.y < rayDiff.y || diff.z < rayDiff.z)
1397                {
1398                   rayDiff = diff;
1399                   rayIntersect = intersect;
1400                }
1401             }
1402             else
1403                break;
1404          }
1405
1406          switch(primitive.type)
1407          {
1408             case triStrip:
1409                points[strip] = points[2];
1410                strip ^= 1;
1411                break;
1412             case triFan:
1413                points[1] = points[2];
1414                break;
1415          }
1416       }
1417       return result;
1418    }
1419
1420    bool PickMesh(Object object, Vector3D rayIntersect)
1421    {
1422       Mesh mesh = object.mesh;
1423       bool result = false;
1424       Vector3D rayDiff { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1425       if(rayIntersect != null)
1426          rayIntersect = { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1427
1428       if(mesh.groups.first)
1429       {
1430          PrimitiveGroup group;
1431
1432          for(group = mesh.groups.first; group; group = group.next)
1433          {
1434             if(PickPrimitives(mesh, (PrimitiveSingle *)&group.type, rayDiff, rayIntersect))
1435             {
1436                result = true;
1437                if(!intersecting)
1438                   break;
1439             }
1440          }
1441       }
1442       else
1443       {
1444          int c;
1445          for(c = 0; c < mesh.nPrimitives; c++)
1446          {
1447             if(PickPrimitives(mesh, mesh.primitives[c], rayDiff, rayIntersect))
1448             {
1449                result = true;
1450                if(!intersecting)
1451                   break;
1452             }
1453          }
1454       }
1455       return result;
1456    }
1457
1458    void SortTriangles(void)
1459    {
1460       Matrix matrix;
1461       Object object = null;
1462       int c;
1463       for(c=0; c<nTriangles; c++)
1464       {
1465          SortPrimitive * sort = &triangles[c];
1466          Mesh mesh = sort->object.mesh;
1467          PrimitiveSingle * primitive = sort->triangle;
1468                 Vector3Df min { MAXFLOAT, MAXFLOAT, MAXFLOAT };
1469          Vector3Df max { -MAXFLOAT, -MAXFLOAT, -MAXFLOAT };
1470          int v;
1471          if(object != sort->object)
1472          {
1473             object = sort->object;
1474             if(object.flags.viewSpace)
1475                matrix = object.matrix;
1476             else
1477             {  
1478                Camera camera = this.camera;
1479                Matrix temp = object.matrix;
1480                temp.m[3][0] -= camera.cPosition.x;
1481                temp.m[3][1] -= camera.cPosition.y;
1482                temp.m[3][2] -= camera.cPosition.z;
1483                matrix.Multiply(temp, camera.viewMatrix);
1484             }
1485          }
1486
1487            for(v = 0; v<primitive->nIndices; v++) 
1488          {
1489             Vector3Df * local = &mesh.vertices[primitive->indices[v]];
1490             Vector3Df vertex;
1491
1492             vertex.MultMatrix(local, &matrix);
1493
1494                         if(vertex.x > max.x) max.x = vertex.x;
1495             if(vertex.y > max.y) max.y = vertex.y;
1496             if(vertex.z > max.z) max.z = vertex.z;
1497                         if(vertex.x < min.x) min.x = vertex.x;
1498             if(vertex.y < min.y) min.y = vertex.y;
1499             if(vertex.z < min.z) min.z = vertex.z;
1500                 }
1501
1502          sort->min = min;
1503          sort->max = max;
1504
1505          sort->marked = false;
1506         }
1507
1508    /*
1509       Logf("========= Before Sort ==========\n");
1510       for(c=0; c<nTriangles; c++)
1511       {
1512          SortPrimitive * sort = &triangles[c];
1513          int v;
1514          Mesh mesh = sort->mesh;
1515          PrimitiveSingle * primitive = sort->triangle;
1516
1517          Logf("Triangle %d (%s):\n", c, primitive->material->name);
1518
1519            for(v = 0; v<primitive->nIndices; v++) 
1520          {
1521             Vector3Df * local = &mesh.vertices[primitive->indices[v]];
1522             Vector3Df vertex;
1523
1524
1525             vertex.<MultMatrix(local, sort->matrix);
1526
1527             Logf("Vertex %d:", v);
1528
1529             // Logf("   Local %f, %f, %f:\n", local->x, local->y, local->z);
1530             Logf("   View  %f, %f, %f:\n", vertex.x, vertex.y, vertex.z);
1531
1532                 }
1533
1534          Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z);
1535          Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z);
1536          Logf("\n", c);
1537       }*/
1538
1539       // *** Sort translucent primitives ***
1540       qsort((void*) triangles, nTriangles, sizeof(SortPrimitive), SortPrimitive::Compare);
1541    /*
1542       Logf("\n\n========= After Sort ==========\n");
1543       for(c=0; c<nTriangles; c++)
1544       {
1545          SortPrimitive * sort = &triangles[c];
1546          int v;
1547          Mesh mesh = sort->mesh;
1548          PrimitiveSingle * primitive = sort->triangle;
1549
1550          Logf("Triangle %d (%s):\n", c, primitive->material->name);
1551
1552            for(v = 0; v<primitive->nIndices; v++) 
1553          {
1554             Vector3Df * local = &mesh.vertices[primitive->indices[v]];
1555             Vector3Df vertex;
1556
1557
1558             vertex.MultMatrix(local, sort->matrix);
1559
1560             Logf("Vertex %d:", v);
1561
1562             // Logf("   Local %f, %f, %f:\n", local->x, local->y, local->z);
1563             Logf("   View  %f, %f, %f:\n", vertex.x, vertex.y, vertex.z);
1564
1565                 }
1566
1567          Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z);
1568          Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z);
1569          Logf("\n", c);
1570       }
1571    */
1572       //exit(0);
1573
1574    /*
1575    If all five tests fail for a particular Q, 
1576    then P might obscure Q. Now Q must be tested. 
1577    First, the algorithm checks if Q has been "marked." 
1578    If Q is marked, then Q was "moved around" in the list 
1579    during a previous iteration of the loop. The algorithm 
1580    only allows a polygon to be moved once, to avoid the possibility 
1581    of infinite loops. If Q is not marked, it is tested to see 
1582    if it might obscure P. If Q cannot obscure P, then Q is possibly 
1583    behind P and so it is good candidate to be drawn next. 
1584    Therefore, the algorithm "abandons" the current P (that is, it 
1585    stops testing Q's against the current P) and moves the current 
1586    Q to the end of the list to become the next P.
1587    */
1588    /*
1589       {
1590          int p;
1591          for(p = 0; p<nTriangles; p++)
1592          {
1593             SortPrimitive * poly1 = &triangles[p];
1594             int q;
1595
1596             for(q = p+1; q<nTriangles; q++)
1597             {
1598                if(q != p)
1599                {
1600                   SortPrimitive * poly2 = &triangles[q];
1601                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1602                   {
1603                      if(poly1->ShouldBeSwapped(poly2))
1604                      {
1605                         if(!poly2->ShouldBeSwapped(poly1))
1606                         {
1607                            SortPrimitive temp = *poly2;
1608                            memmove(triangles+1, triangles, sizeof(SortPrimitive)*q);
1609                            triangles[0] = temp;
1610                            triangles[0].marked = true;
1611                            p = 0;
1612                            break;
1613                         }
1614                      }
1615                   }
1616                }
1617             }
1618          }
1619       }
1620       */
1621    /*
1622       {
1623          int p;
1624          for(p = 0; p<nTriangles; p++)
1625          {
1626             SortPrimitive * poly1 = &triangles[p];
1627             int q;
1628
1629             // for(q = p+1; q<nTriangles; q++)
1630             for(q = 0; q<nTriangles; q++)
1631             {
1632                if(q != p)
1633                {
1634                   SortPrimitive * poly2 = &triangles[q];
1635                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1636                   {
1637                      if(poly1->ShouldBeSwapped(poly2))
1638                      {
1639                         if(!poly2->ShouldBeSwapped(poly1))
1640                         {
1641                            SortPrimitive temp = *poly2;
1642                            memmove(triangles+1, triangles, sizeof(SortPrimitive)*q);
1643                            triangles[0] = temp;
1644                            triangles[0].marked = true;
1645                            p = -1;
1646                            break;
1647                         }
1648                      }
1649                   }
1650                }
1651             }
1652          }
1653       }*/
1654       /*
1655       {
1656          int p;
1657          for(p = nTriangles-1; p>=0; p--)
1658          {
1659             SortPrimitive * poly1 = &triangles[p];
1660             int q;
1661
1662             for(q = nTriangles-1; q>=0; q--)
1663             {
1664                if(q != p)
1665                {
1666                   SortPrimitive * poly2 = &triangles[q];
1667                   if(poly1->ZOverlap(poly2) && !poly2->marked)
1668                   {
1669                      if(poly1->ShouldBeSwapped(poly2))
1670                      {
1671                         if(!poly2->ShouldBeSwapped(poly1))
1672                         {
1673                            SortPrimitive temp = *poly2;
1674                            memmove(triangles + q, triangles + q + 1, sizeof(SortPrimitive)*q);
1675                            temp.marked = true;
1676                            triangles[nTriangles-1] = temp;
1677                            p = nTriangles;
1678                            break;
1679                         }
1680                      }
1681                   }
1682                }
1683             }
1684          }
1685       }*/
1686
1687    /*
1688       {
1689            for(c=0; c<nTriangles; c++) 
1690          {
1691             int b;
1692             SortPrimitive * poly1 = &triangles[c];
1693
1694             // for(b=0; b<nTriangles; b++) 
1695             //for(b=c+1; b<nTriangles; b++) 
1696             b = c+1;
1697             if(b<this.nTriangles)
1698             {
1699                SortPrimitive * poly2 = &this.triangles[b];
1700
1701                if(poly1->ZOverlap(poly2) && poly1->ShouldBeSwapped(poly2))
1702                {
1703                   SortPrimitive temp = *poly1;
1704                   *poly1 = *poly2;
1705                   *poly2 = temp;
1706                }
1707             }
1708            }
1709       }
1710    */
1711    }
1712 };
1713 #endif
1714
1715 bool IsDriverTextMode(char * driverName)
1716 {
1717    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
1718    return driver ? driver.textMode : false;
1719 }
1720
1721 bool IsDriverPrinter(char * driverName)
1722 {
1723    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
1724    return driver ? driver.printer : false;
1725 }
1726
1727 static ColorAlpha defaultPalette[256] =
1728 {
1729    // 16 Colors
1730 /*
1731    0xFF000000,0xFF000080,0xFF008000,0xFF008080,0xFF800000,0xFF800080,0xFF808000,0xFFC0C0C0,
1732    0xFF808080,0xFF0000FF,0xFF00FF00,0xFF00FFFF,0xFFFF0000,0xFFFF00FF,0xFFFFFF00,0xFFFFFFFF,
1733 */
1734    0xFF000000,0xFF0000AA,0xFF00AA00,0xFF00AAAA,0xFFAA0000,0xFFAA00AA,0xFFAAAA00,0xFFABABAB,
1735    0xFF555555,0xFF5555FF,0xFF55FF55,0xFF55FFFF,0xFFFF5555,0xFFFF55FF,0xFFFFFF55,0xFFFFFFFF,
1736
1737    // 6 x 6 x 6 Color Cube
1738    0xFF000000, 0xFF000033, 0xFF000066, 0xFF000099, 0xFF0000CC, 0xFF0000FF,
1739    0xFF003300, 0xFF003333, 0xFF003366, 0xFF003399, 0xFF0033CC, 0xFF0033FF,
1740    0xFF006600, 0xFF006633, 0xFF006666, 0xFF006699, 0xFF0066CC, 0xFF0066FF,
1741    0xFF009900, 0xFF009933, 0xFF009966, 0xFF009999, 0xFF0099CC, 0xFF0099FF,
1742    0xFF00CC00, 0xFF00CC33, 0xFF00CC66, 0xFF00CC99, 0xFF00CCCC, 0xFF00CCFF,
1743    0xFF00FF00, 0xFF00FF33, 0xFF00FF66, 0xFF00FF99, 0xFF00FFCC, 0xFF00FFFF,
1744
1745    0xFF330000, 0xFF330033, 0xFF330066, 0xFF330099, 0xFF3300CC, 0xFF3300FF,
1746    0xFF333300, 0xFF333333, 0xFF333366, 0xFF333399, 0xFF3333CC, 0xFF3333FF,
1747    0xFF336600, 0xFF336633, 0xFF336666, 0xFF336699, 0xFF3366CC, 0xFF3366FF,
1748    0xFF339900, 0xFF339933, 0xFF339966, 0xFF339999, 0xFF3399CC, 0xFF3399FF,
1749    0xFF33CC00, 0xFF33CC33, 0xFF33CC66, 0xFF33CC99, 0xFF33CCCC, 0xFF33CCFF,
1750    0xFF33FF00, 0xFF33FF33, 0xFF33FF66, 0xFF33FF99, 0xFF33FFCC, 0xFF33FFFF,
1751
1752    0xFF660000, 0xFF660033, 0xFF660066, 0xFF660099, 0xFF6600CC, 0xFF6600FF,
1753    0xFF663300, 0xFF663333, 0xFF663366, 0xFF663399, 0xFF6633CC, 0xFF6633FF,
1754    0xFF666600, 0xFF666633, 0xFF666666, 0xFF666699, 0xFF6666CC, 0xFF6666FF,
1755    0xFF669900, 0xFF669933, 0xFF669966, 0xFF669999, 0xFF6699CC, 0xFF6699FF,
1756    0xFF66CC00, 0xFF66CC33, 0xFF66CC66, 0xFF66CC99, 0xFF66CCCC, 0xFF66CCFF,
1757    0xFF66FF00, 0xFF66FF33, 0xFF66FF66, 0xFF66FF99, 0xFF66FFCC, 0xFF66FFFF,
1758
1759    0xFF990000, 0xFF990033, 0xFF990066, 0xFF990099, 0xFF9900CC, 0xFF9900FF,
1760    0xFF993300, 0xFF993333, 0xFF993366, 0xFF993399, 0xFF9933CC, 0xFF9933FF,
1761    0xFF996600, 0xFF996633, 0xFF996666, 0xFF996699, 0xFF9966CC, 0xFF9966FF,
1762    0xFF999900, 0xFF999933, 0xFF999966, 0xFF999999, 0xFF9999CC, 0xFF9999FF,
1763    0xFF99CC00, 0xFF99CC33, 0xFF99CC66, 0xFF99CC99, 0xFF99CCCC, 0xFF99CCFF,
1764    0xFF99FF00, 0xFF99FF33, 0xFF99FF66, 0xFF99FF99, 0xFF99FFCC, 0xFF99FFFF,
1765
1766    0xFFCC0000, 0xFFCC0033, 0xFFCC0066, 0xFFCC0099, 0xFFCC00CC, 0xFFCC00FF,
1767    0xFFCC3300, 0xFFCC3333, 0xFFCC3366, 0xFFCC3399, 0xFFCC33CC, 0xFFCC33FF,
1768    0xFFCC6600, 0xFFCC6633, 0xFFCC6666, 0xFFCC6699, 0xFFCC66CC, 0xFFCC66FF,
1769    0xFFCC9900, 0xFFCC9933, 0xFFCC9966, 0xFFCC9999, 0xFFCC99CC, 0xFFCC99FF,
1770    0xFFCCCC00, 0xFFCCCC33, 0xFFCCCC66, 0xFFCCCC99, 0xFFCCCCCC, 0xFFCCCCFF,
1771    0xFFCCFF00, 0xFFCCFF33, 0xFFCCFF66, 0xFFCCFF99, 0xFFCCFFCC, 0xFFCCFFFF,
1772
1773    0xFFFF0000, 0xFFFF0033, 0xFFFF0066, 0xFFFF0099, 0xFFFF00CC, 0xFFFF00FF,
1774    0xFFFF3300, 0xFFFF3333, 0xFFFF3366, 0xFFFF3399, 0xFFFF33CC, 0xFFFF33FF,
1775    0xFFFF6600, 0xFFFF6633, 0xFFFF6666, 0xFFFF6699, 0xFFFF66CC, 0xFFFF66FF,
1776    0xFFFF9900, 0xFFFF9933, 0xFFFF9966, 0xFFFF9999, 0xFFFF99CC, 0xFFFF99FF,
1777    0xFFFFCC00, 0xFFFFCC33, 0xFFFFCC66, 0xFFFFCC99, 0xFFFFCCCC, 0xFFFFCCFF,
1778    0xFFFFFF00, 0xFFFFFF33, 0xFFFFFF66, 0xFFFFFF99, 0xFFFFFFCC, 0xFFFFFFFF,
1779
1780    // 16 Shades of gray
1781    0xFF000000,0xFF101010,0xFF202020,0xFF303030,0xFF404040,0xFF505050,0xFF606060,0xFF707070,
1782    0xFF808080,0xFF909090,0xFFA0A0A0,0xFFB0B0B0,0xFFC0C0C0,0xFFD0D0D0,0xFFE0E0E0,0xFFF0F0F0,
1783
1784    // Some more grays
1785    0xFF080808,0xFF101010,0xFF585858,0xFF606060,0xFFA8A8A8,0xFFB0B0B0,0xFFF8F8F8,0xFFFFFFFF
1786
1787 /*
1788    // Red
1789    0xFF080000,0xFF100000,0xFF180000,0xFF200000,0xFF280000,0xFF300000,0xFF380000,0xFF400000,
1790    0xFF480000,0xFF500000,0xFF580000,0xFF600000,0xFF680000,0xFF700000,0xFF780000,0xFF800000,
1791    0xFF880000,0xFF900000,0xFF980000,0xFFA00000,0xFFA80000,0xFFB00000,0xFFB80000,0xFFC00000,
1792    0xFFC80000,0xFFD00000,0xFFD80000,0xFFE00000,0xFFE80000,0xFFF00000,0xFFF80000,0xFFFF0000,
1793    // Green
1794    0xFF000800,0xFF001000,0xFF001800,0xFF002000,0xFF002800,0xFF003000,0xFF003800,0xFF004000,
1795    0xFF004800,0xFF005000,0xFF005800,0xFF006000,0xFF006800,0xFF007000,0xFF007800,0xFF008000,
1796    0xFF008800,0xFF009000,0xFF009800,0xFF00A000,0xFF00A800,0xFF00B000,0xFF00B800,0xFF00C000,
1797    0xFF00C800,0xFF00D000,0xFF00D800,0xFF00E000,0xFF00E800,0xFF00F000,0xFF00F800,0xFF00FF00,
1798    // Cyan
1799    0xFF000808,0xFF001010,0xFF001818,0xFF002020,0xFF002828,0xFF003030,0xFF003838,0xFF004040,
1800    0xFF004848,0xFF005050,0xFF005858,0xFF006060,0xFF006868,0xFF007070,0xFF007878,0xFF008080,
1801    0xFF008888,0xFF009090,0xFF009898,0xFF00A0A0,0xFF00A8A8,0xFF00B0B0,0xFF00B8B8,0xFF00C0C0,
1802    0xFF00C8C8,0xFF00D0D0,0xFF00D8D8,0xFF00E0E0,0xFF00E8E8,0xFF00F0F0,0xFF00F8F8,0xFF00FFFF,
1803    // Blue
1804    0xFF000008,0xFF000010,0xFF000018,0xFF000020,0xFF000028,0xFF000030,0xFF000038,0xFF000040,
1805    0xFF000048,0xFF000050,0xFF000058,0xFF000060,0xFF000068,0xFF000070,0xFF000078,0xFF000080,
1806    0xFF000088,0xFF000090,0xFF000098,0xFF0000A0,0xFF0000A8,0xFF0000B0,0xFF0000B8,0xFF0000C0,
1807    0xFF0000C8,0xFF0000D0,0xFF0000D8,0xFF0000E0,0xFF0000E8,0xFF0000F0,0xFF0000F8,0xFF0000FF,
1808    // Magenta
1809    0xFF080008,0xFF100010,0xFF180018,0xFF200020,0xFF280028,0xFF300030,0xFF380038,0xFF400040,
1810    0xFF480048,0xFF500050,0xFF580058,0xFF600060,0xFF680068,0xFF700070,0xFF780078,0xFF800080,
1811    0xFF880088,0xFF900090,0xFF980098,0xFFA000A0,0xFFA800A8,0xFFB000B0,0xFFB800B8,0xFFC000C0,
1812    0xFFC800C8,0xFFD000D0,0xFFD800D8,0xFFE000E0,0xFFE800E8,0xFFF000F0,0xFFF800F8,0xFFFF00FF,
1813    // Yellow
1814    0xFF080800,0xFF101000,0xFF181800,0xFF202000,0xFF282800,0xFF303000,0xFF383800,0xFF404000,
1815    0xFF484800,0xFF505000,0xFF585800,0xFF606000,0xFF686800,0xFF707000,0xFF787800,0xFF808000,
1816    0xFF888800,0xFF909000,0xFF989800,0xFFA0A000,0xFFA8A800,0xFFB0B000,0xFFB8B800,0xFFC0C000,
1817    0xFFC8C800,0xFFD0D000,0xFFD8D800,0xFFE0E000,0xFFE8E800,0xFFF0F000,0xFFF8F800,0xFFFFFF00,
1818    // White
1819    0xFF080808,0xFF101010,0xFF181818,0xFF202020,0xFF282828,0xFF303030,0xFF383838,0xFF404040,
1820    0xFF484848,0xFF505050,0xFF585858,0xFF606060,0xFF686868,0xFF707070,0xFF787878,0xFF808080,
1821    0xFF888888,0xFF909090,0xFF989898,0xFFA0A0A0,0xFFA8A8A8,0xFFB0B0B0,0xFFB8B8B8,0xFFC0C0C0,
1822    0xFFC8C8C8,0xFFD0D0D0,0xFFD8D8D8,0xFFE0E0E0,0xFFE8E8E8,0xFFF0F0F0,0xFFF8F8F8,0xFFFFFFFF
1823 */
1824 };
1825
1826 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1827 static Material defaultMaterial
1828 {
1829    opacity = 1.0f,
1830    diffuse = { 1.0f, 1.0f, 1.0f },
1831    ambient = { 1.0f, 1.0f, 1.0f },
1832    flags = { doubleSided = true, noFog = true };
1833 };
1834 #endif
1835
1836 static byte colorDepthShifts[PixelFormat] = { 0,0,1,1,1,2,0,1,2 };
1837 static Size resolutions[Resolution] =
1838 {
1839    {80,25},
1840    {320,200},{320,240},{320,400},{360,480},
1841    {400,256},{400,300},{512,256},{512,384},
1842    {640,200},{640,350},{640,400},{640,480},
1843    {720,348},{800,600},{856,480},{960,720},
1844    {1024,768},{1152,864},{1280,1024},{1600,1200},
1845    {768,480}
1846 };
1847 static int colorDepths[PixelFormat] = {4,8,12,15,16,32,8,16,32};
1848
1849 // --- Query utilities ---
1850
1851 public int GetResolutionWidth(Resolution resolution)
1852 {
1853    return resolutions[resolution].w;
1854 }
1855
1856 public int GetResolutionHeight(Resolution resolution)
1857 {
1858    return resolutions[resolution].h;
1859 }
1860
1861 public int GetDepthBits(PixelFormat colorDepth)
1862 {
1863    return colorDepths[colorDepth];
1864 }
1865
1866 public byte GetColorDepthShifts(PixelFormat format)
1867 {
1868    return colorDepthShifts[format];
1869 }
1870
1871 public ColorAlpha * GetDefaultPalette()
1872 {
1873    return (ColorAlpha *)defaultPalette;
1874 }
1875 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
1876 public Material GetDefaultMaterial()
1877 {
1878    return defaultMaterial;
1879 }
1880 #endif
1881 public int BestColorMatch(ColorAlpha * palette, int start, int end, Color rgb)
1882 {
1883    int best = 0;
1884    if(palette)
1885    {
1886       int c;
1887       int bestscore = MAXINT,score;
1888       byte r = rgb.r, g = rgb.g, b = rgb.b;
1889       Color current;
1890
1891       for(c = start; c <= end; c++)
1892       {
1893          int dr,dg,db;
1894          current = palette[c];
1895          if(rgb && !c) continue;
1896          dr = r - current.r;
1897          dg = g - current.g;
1898          db = b - current.b;
1899          score = Abs(dr) + Abs(dg) + Abs(db);
1900          if(score <= bestscore)
1901          {
1902             bestscore = score;
1903             best = c;
1904          }
1905       }
1906    }
1907    return best;
1908 }