ecere/gfx/3D/Object: computeLightVector flag to avoid always recomputing uselessly
[sdk] / ecere / src / gfx / Display.ec
index 62c6e94..217a181 100644 (file)
@@ -31,6 +31,42 @@ import "Quaternion"
 import "Vector3D"
 #endif
 
+#if (!defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER))
+import "OpenGLDisplayDriver"
+
+#define near _near
+#define far _far
+#include "gl123es.h"
+#undef near
+#undef far
+#endif
+
+public class GLCapabilities : uint
+{
+public:
+   // Expect reloading graphics
+   bool compatible      :1;
+   bool vertexBuffer    :1;
+   bool quads           :1;
+   bool intAndDouble    :1;
+   bool legacyFormats   :1;
+   bool nonPow2Textures :1;
+   bool vertexPointer   :1;
+
+   // Should be able to toggle without reloading
+   bool legacy          :1;
+   bool shaders         :1;
+   bool fixedFunction   :1;
+   bool immediate       :1;
+   bool frameBuffer     :1;
+   bool pointSize       :1;
+   bool vao             :1;
+   bool select          :1;
+   // bool mapBuffer       :1;
+
+   bool debug           :1;
+};
+
 public enum RenderState { fillMode = 1, depthTest, depthWrite, fogDensity, fogColor, blend, ambient, alphaWrite, antiAlias, vSync };
 
 public union RenderStateFloat { float f; uint ui; };
@@ -48,7 +84,9 @@ public class FontFlags
 
 __attribute__((unused)) static void DummyFunction()
 {
+#if !defined(__EMSCRIPTEN__)
    Mutex { };
+#endif
 }
 
 public class DisplayDriver
@@ -124,10 +162,10 @@ public:
    virtual bool ::ConvertBitmap(DisplaySystem, Bitmap, PixelFormat, ColorAlpha *);
 
    // Converts an LFB bitmap into an offscreen bitmap for this device
-   virtual bool ::MakeDDBitmap(DisplaySystem, Bitmap, bool);
+   virtual bool ::MakeDDBitmap(DisplaySystem, Bitmap, bool mipMaps, int cubeMapFace);
 
    // Font loading
-   virtual Font ::LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags);
+   virtual Font ::LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade);
    virtual void ::UnloadFont(DisplaySystem, Font);
 
    // 2D Drawing
@@ -148,9 +186,9 @@ public:
    virtual void ::FilterDI(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
    virtual void ::TextFont(Display, Surface, Font);
    virtual void ::TextOpacity(Display, Surface, bool);
-   virtual void ::WriteText(Display, Surface, int x, int y, const String text, int len);
-   virtual void ::TextExtent(Display, Surface, const String text, int len, int * tw, int * th);
-   virtual void ::FontExtent(DisplaySystem, Font, const String text, int len, int * tw, int * th);
+   virtual void ::WriteText(Display, Surface, int x, int y, const String text, int len, int prevGlyph, int * rPrevGlyph);
+   virtual void ::TextExtent(Display, Surface, const String text, int len, int * tw, int * th, int prevGlyph, int * rPrevGlyph, int * overHang);
+   virtual void ::FontExtent(DisplaySystem, Font, const String text, int len, int * tw, int * th, int prevGlyph, int * rPrevGlyph, int * overHang);
    virtual void ::DrawingChar(Display, Surface, char ch);
    virtual void ::NextPage(Display);
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
@@ -179,27 +217,6 @@ public:
 public enum Alignment { left, right, center };
 public enum ClearType { colorBuffer, depthBuffer, colorAndDepth };
 
-subclass(DisplayDriver) GetDisplayDriver(const char * driverName)
-{
-   if(driverName)
-   {
-      OldLink link;
-      for(link = class(DisplayDriver).derivatives.first; link; link = link.next)
-      {
-         subclass(DisplayDriver) displayDriver = link.data;
-         if(displayDriver && displayDriver.name && !strcmp(displayDriver.name, driverName))
-            return displayDriver;
-      }
-   }
-   return null;
-}
-
-DisplaySystem GetDisplaySystem(const char * driverName)
-{
-   subclass(DisplayDriver) displayDriver = GetDisplayDriver(driverName);
-   return displayDriver ? displayDriver.displaySystem : null;
-}
-
 define textCellW = 8;
 define textCellH = 16;
 
@@ -240,7 +257,7 @@ public struct Light
    float multiplier;
 };
 
-define NumberOfLights = 8;
+public define NumberOfLights = 8;
 
 // Painter's algorithm
 
@@ -438,7 +455,7 @@ public:
    Surface GetSurface(int x, int y, Box clip)
    {
       Surface result = null;
-      Surface surface { };
+      Surface surface { _refCount = 1 };
       if(surface)
       {
          Box box { -x, -y, -x + width - 1, -y + height - 1 };
@@ -506,6 +523,13 @@ public:
 
    void FontExtent(Font font, const char * text, int len, int * width, int * height)
    {
+      int overHang = 0;
+      FontExtent2(font, text, len, width, height, 0, null, &overHang);
+      if(width) *width += overHang;
+   }
+
+   void FontExtent2(Font font, const char * text, int len, int * width, int * height, int prevGlyph, int * rPrevGlyph, int * overHang)
+   {
       // Fix for OnLoadGraphics time alpha blended window text extent on GDI
 #if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE)
       if(this && alphaBlend && pixelFormat == pixelFormat888 &&
@@ -515,14 +539,14 @@ public:
          if(s)
          {
             s.font = font;
-            s.TextExtent(text, len, width, height);
+            s.TextExtent2(text, len, width, height, prevGlyph, rPrevGlyph, overHang);
             delete s;
          }
       }
       else
 #endif
          // TODO: Should really pass display here...
-         DisplaySystem::FontExtent(this ? displaySystem : null, font, text, len, width, height);
+         DisplaySystem::FontExtent2(this ? displaySystem : null, font, text, len, width, height, prevGlyph, rPrevGlyph, overHang);
    }
 
    void SetPalette(ColorAlpha * palette, bool colorMatch)
@@ -549,7 +573,9 @@ public:
       result = displaySystem && displaySystem.Lock();
       if(result && render)
       {
+#if !defined(__EMSCRIPTEN__)
          mutex.Wait();
+#endif
 
          if(!current)
             result = displaySystem.driver.Lock(this);
@@ -575,7 +601,9 @@ public:
          */
          if(!current && displaySystem)
             displaySystem.driver.Unlock(this);
+#if !defined(__EMSCRIPTEN__)
          mutex.Release();
+#endif
       }
       if(displaySystem)
          displaySystem.Unlock();
@@ -689,6 +717,10 @@ public:
    // --- Lights ---
    void SetLight(int id, Light light)
    {
+      if(!display3D)
+      {
+         display3D = Display3D { };
+      }
       displaySystem.driver.SetLight(this, id, light);
    }
 
@@ -914,6 +946,136 @@ public:
                if(!display3D.selection && displaySystem.driver.PushMatrix)
                   displaySystem.driver.PushMatrix(this);
 
+#if ENABLE_GL_FFP
+               if(object.mesh.tangents && object.mesh.normals && object.flags.computeLightVectors)
+               {
+                  Mesh mesh = object.mesh;
+                  if(!glCaps_shaders)
+                  {
+                     int count = mesh.nVertices;
+                     Vector3Df * normals = mesh.normals;
+                     Vector3Df * vertices = mesh.vertices;
+                     ColorRGB * lightVectors;
+                     Vector3Df * tangents = mesh.tangents;
+                     int i;
+                     float * l = display3D.light0Pos;
+                     Vector3Df light { l[0], l[1], l[2] };
+                     Matrix o = object.matrix;
+                     Matrix t, inv = camera.viewMatrix;
+                     Vector3D ot { };
+                     Vector3D cPos = camera.cPosition;
+                     Vector3D pos;
+                     bool positional = l[3] ? true : false;
+
+                     inv.Scale(1.0/nearPlane, -1.0/nearPlane,-1.0/nearPlane);
+
+                     pos.MultMatrix(cPos, camera.viewMatrix);
+
+                     ot.x = o.m[3][0] + pos.x;
+                     ot.y = o.m[3][1] + pos.y;
+                     ot.z = o.m[3][2] + pos.z;
+                     o.m[3][0] = 0;
+                     o.m[3][1] = 0;
+                     o.m[3][2] = 0;
+                     t.Multiply(o, inv);
+                     inv = t;
+                     t.Transpose(inv);
+                     inv.Inverse(t);
+
+                     mesh.Allocate({ lightVectors = true }, mesh.nVertices, displaySystem);
+                     mesh.Lock({ lightVectors = true });
+                     lightVectors = mesh.lightVectors;
+                     for(i = 0; i < count; i++)
+                     {
+                        Vector3Df tangent1 = tangents[i*2 + 0];
+                        Vector3Df tangent2 = tangents[i*2 + 1];
+                        Vector3Df normal = normals[i];
+                        Vector3Df tTangent1, tTangent2, tNormal;
+
+                        tTangent1.MultMatrix(tangent1, inv);
+                        tTangent2.MultMatrix(tangent2, inv);
+                        tNormal  .MultMatrix(normal,   inv);
+
+                        tTangent1.Normalize(tTangent1);
+                        tTangent2.Normalize(tTangent2);
+                        tNormal  .Normalize(tNormal);
+
+                        {
+                           Matrix tbn
+                           { {
+                               tTangent1.x, tTangent2.x, tNormal.x, 0,
+                               tTangent1.y, tTangent2.y, tNormal.y, 0,
+                               tTangent1.z, tTangent2.z, tNormal.z, 1
+                           } };
+                           Vector3Df n;
+                           if(positional)
+                           {
+                              Vector3Df tPos = vertices[i];
+                              tPos.x += ot.x, tPos.y += ot.y, tPos.z += ot.z;
+
+                              // Subtract vertex from light for positional lights
+                              light.x = l[0] - tPos.x;
+                              light.y = l[1] + tPos.y;
+                              light.z = l[2] - tPos.z;
+                           }
+                           n.MultMatrix(light, tbn);
+                           if(positional)
+                              n.Normalize(n);
+                           lightVectors[i] = { n.x / 2 + 0.5f, n.y / 2 + 0.5f, n.z / 2 + 0.5f };
+                        }
+                     }
+                     mesh.Unlock({ lightVectors = true });
+
+                     // Create normalization cube map
+                     /*
+                     if(!mesh.normMap)
+                        mesh.normMap = { };
+                     {
+                        int w = 256, h = 256, d = 256;
+                        Vector3Df min = mesh.min, max = mesh.max;
+                        Vector3Df delta
+                        {
+                           (max.x - min.x) / w,
+                           (max.y - min.y) / h,
+                           (max.z - min.z) / d
+                        };
+                        int i;
+                        for(i = 0; i < 6; i++)
+                        {
+                           Bitmap face = i > 0 ? { } : mesh.normMap;
+                           int x, y;
+                           ColorAlpha * p;
+                           face.Free();
+                           face.Allocate(null, w, h, 0, pixelFormat888, false);
+                           face.driverData = mesh.normMap.driverData;
+                           p = (ColorAlpha *)face.picture;
+                           for(y = 0; y < h; y++)
+                           {
+                              for(x = 0; x < w; x++, p++)
+                              {
+                                 Vector3Df v { min.x + x * delta.x, min.y + y * delta.y, min.z };
+                                 v.Normalize(v);
+                                 *p = ColorAlpha { 255, {
+                                       (byte)((v.x / 2.0 + 0.5) * 255),
+                                       (byte)((v.y / 2.0 + 0.5) * 255),
+                                       (byte)((v.z / 2.0 + 0.5) * 255) } };
+                             }
+                           }
+                           displaySystem.driver.MakeDDBitmap(displaySystem, face, true, (i + 1));
+                           if(i > 0)
+                           {
+                              face.driverData = 0;
+                              delete face;
+                           }
+                        }
+                     }
+                     */
+                  }
+                  else
+                     mesh.Free({ lightVectors = true });
+               }
+#endif
+
                SetTransform(object.matrix, object.flags.viewSpace);
                if(display3D.selection)
                {
@@ -1119,6 +1281,36 @@ public:
    property bool useSharedMemory { set { useSharedMemory = value; } get { return useSharedMemory; } };
    property void * systemWindow { get { return window; } };
    property DisplaySystem displaySystem { get { return displaySystem; } };
+#ifndef ECERE_VANILLA
+   property GLCapabilities glCapabilities
+   {
+      get { return ((OGLDisplay)driverData).capabilities; }
+      set
+      {
+         glCapabilities = value;
+         if(driverData && displaySystem.driver == class(OpenGLDisplayDriver))
+         {
+            OGLDisplay oglDisplay = driverData;
+            if(!oglDisplay.originalCapabilities.fixedFunction)
+               value.shaders = true;
+            if(!oglDisplay.originalCapabilities.shaders)
+               value.fixedFunction = true;
+            // Disable things that don't work with shaders
+            if(value.shaders)
+            {
+               value.fixedFunction = false;
+               value.legacy = false;
+               value.immediate = false;
+            }
+            oglDisplay.capabilities = oglDisplay.originalCapabilities & value;
+
+            Lock(true);
+            OpenGLDisplayDriver::initialDisplaySetup(this, true, false);
+            Unlock();
+         }
+      }
+   };
+#endif
 
    int width, height;
    void * driverData;
@@ -1128,7 +1320,9 @@ private:
    DisplaySystem displaySystem;
    void * window;
 
+#if !defined(__EMSCRIPTEN__)
    Mutex mutex { };
+#endif
    int current;
 
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
@@ -1137,10 +1331,12 @@ private:
    bool alphaBlend;
    void * windowDriverData;
    bool useSharedMemory;
+   GLCapabilities glCapabilities;
+   glCapabilities = { true, true, true, true, true, true, true, true };
 };
 
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
-private class Display3D
+private class Display3D : struct
 {
    // 3D Display
    int nTriangles;
@@ -1164,6 +1360,7 @@ private class Display3D
 
    Line rayView, rayWorld, rayLocal;
    Vector3D rayIntersect;
+   float light0Pos[4];
 
    ~Display3D()
    {
@@ -1909,3 +2106,25 @@ public int BestColorMatch(ColorAlpha * palette, int start, int end, Color rgb)
    }
    return best;
 }
+
+// had to move this here due to compiler ordering issue for "get property" symbol
+subclass(DisplayDriver) GetDisplayDriver(const char * driverName)
+{
+   if(driverName)
+   {
+      OldLink link;
+      for(link = class(DisplayDriver).derivatives.first; link; link = link.next)
+      {
+         subclass(DisplayDriver) displayDriver = link.data;
+         if(displayDriver && displayDriver.name && !strcmp(displayDriver.name, driverName))
+            return displayDriver;
+      }
+   }
+   return null;
+}
+
+DisplaySystem GetDisplaySystem(const char * driverName)
+{
+   subclass(DisplayDriver) displayDriver = GetDisplayDriver(driverName);
+   return displayDriver ? displayDriver.displaySystem : null;
+}