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