ecere/gfx/3D/Object: computeLightVector flag to avoid always recomputing uselessly
[sdk] / ecere / src / gfx / Display.ec
index 481dc41..217a181 100644 (file)
@@ -31,8 +31,46 @@ 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; };
+
 public enum FillModeValue { solid, wireframe };
 
 public class DisplayFlags
@@ -44,15 +82,17 @@ public class FontFlags
    public bool bold:1, italic:1, underline:1;
 };
 
-static void DummyFunction()
+__attribute__((unused)) static void DummyFunction()
 {
-   Mutex mutex { };
+#if !defined(__EMSCRIPTEN__)
+   Mutex { };
+#endif
 }
 
 public class DisplayDriver
 {
 public:
-   class_data char * name;
+   class_data const char * name;
    class_data bool textMode;
    class_data bool printer;
    class_data DisplaySystem displaySystem;
@@ -63,7 +103,7 @@ public:
       get { return class_data(displaySystem); }
    };
 
-   class_property char * name
+   class_property const char * name
    {
       set { class_data(name) = value; }
       get { return class_data(name); }
@@ -122,41 +162,41 @@ 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, 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
    virtual void ::SetForeground(Display, Surface, ColorAlpha);
    virtual void ::SetBackground(Display, Surface, ColorAlpha);
-   virtual void ::LineStipple(Display, Surface, uint);
-   virtual ColorAlpha ::GetPixel(Display, Surface, int, int);
-   virtual void ::PutPixel(Display, Surface, int, int);
-   virtual void ::DrawLine(Display, Surface, int, int, int, int);
-   virtual void ::Rectangle(Display, Surface,int,int,int,int);
-   virtual void ::Area(Display, Surface,int,int,int,int);
+   virtual void ::LineStipple(Display, Surface, uint pattern);
+   virtual ColorAlpha ::GetPixel(Display, Surface, int x, int y);
+   virtual void ::PutPixel(Display, Surface, int x, int y);
+   virtual void ::DrawLine(Display, Surface, int x1, int y1, int x2, int y2);
+   virtual void ::Rectangle(Display, Surface, int x1, int y1, int x2, int y2);
+   virtual void ::Area(Display, Surface, int x1, int y1, int x2, int y2);
    virtual void ::Clear(Display, Surface, ClearType);
-   virtual void ::Blit(Display, Surface, Bitmap, int, int, int, int, int, int);
-   virtual void ::Stretch(Display, Surface, Bitmap, int, int, int, int, int, int,int,int);
-   virtual void ::Filter(Display, Surface, Bitmap, int, int, int, int, int, int,int,int);
-   virtual void ::BlitDI(Display, Surface, Bitmap, int, int, int, int, int, int);
-   virtual void ::StretchDI(Display, Surface, Bitmap, int, int, int, int, int, int,int,int);
-   virtual void ::FilterDI(Display, Surface, Bitmap, int, int, int, int, int, int, int,int);
+   virtual void ::Blit(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h);
+   virtual void ::Stretch(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
+   virtual void ::Filter(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
+   virtual void ::BlitDI(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h);
+   virtual void ::StretchDI(Display, Surface, Bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh);
+   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, int, char *, int);
-   virtual void ::TextExtent(Display, Surface, char *, int, int *, int *);
-   virtual void ::FontExtent(DisplaySystem, Font, char *, int, int *, int *);
-   virtual void ::DrawingChar(Display, Surface, char);
+   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)
    // 3D Graphics
    virtual void ::SetRenderState(Display, RenderState, uint);
    virtual void ::SetLight(Display, int, Light);
    virtual void ::SetCamera(Display, Surface, Camera);
-   virtual bool ::AllocateMesh(DisplaySystem, Mesh);
+   virtual bool ::AllocateMesh(DisplaySystem, Mesh, MeshFeatures, int nVertices);
    virtual void ::FreeMesh(DisplaySystem, Mesh);
    virtual bool ::LockMesh(DisplaySystem, Mesh, MeshFeatures flags);
    virtual void ::UnlockMesh(DisplaySystem, Mesh, MeshFeatures flags);
@@ -177,27 +217,6 @@ public:
 public enum Alignment { left, right, center };
 public enum ClearType { colorBuffer, depthBuffer, colorAndDepth };
 
-subclass(DisplayDriver) GetDisplayDriver(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(char * driverName)
-{
-   subclass(DisplayDriver) displayDriver = GetDisplayDriver(driverName);
-   return displayDriver ? displayDriver.displaySystem : null;
-}
-
 define textCellW = 8;
 define textCellH = 16;
 
@@ -238,7 +257,7 @@ public struct Light
    float multiplier;
 };
 
-define NumberOfLights = 8;
+public define NumberOfLights = 8;
 
 // Painter's algorithm
 
@@ -436,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 };
@@ -502,7 +521,14 @@ public:
       return result;
    }
 
-   void FontExtent(Font font, char * text, int len, int * width, int * height)
+   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)
@@ -513,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)
@@ -547,7 +573,9 @@ public:
       result = displaySystem && displaySystem.Lock();
       if(result && render)
       {
+#if !defined(__EMSCRIPTEN__)
          mutex.Wait();
+#endif
 
          if(!current)
             result = displaySystem.driver.Lock(this);
@@ -573,7 +601,9 @@ public:
          */
          if(!current && displaySystem)
             displaySystem.driver.Unlock(this);
+#if !defined(__EMSCRIPTEN__)
          mutex.Release();
+#endif
       }
       if(displaySystem)
          displaySystem.Unlock();
@@ -611,7 +641,7 @@ public:
             camera.Setup(width, height, null);
 
          // Always calling Update() here had broken interpolation in OrbitWithMouse!
-         if(!camera.cAngle.w)
+         if(!camera.cAngle.w && surface)
             camera.Update();
 
          if(display3D.selection)
@@ -687,6 +717,10 @@ public:
    // --- Lights ---
    void SetLight(int id, Light light)
    {
+      if(!display3D)
+      {
+         display3D = Display3D { };
+      }
       displaySystem.driver.SetLight(this, id, light);
    }
 
@@ -912,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)
                {
@@ -1101,7 +1265,7 @@ public:
    property FillModeValue fillMode { set { displaySystem.driver.SetRenderState(this, fillMode, value); } };
    property bool depthTest    { set { displaySystem.driver.SetRenderState(this, depthTest, value); } };
    property bool depthWrite   { set { displaySystem.driver.SetRenderState(this, depthWrite, value); } };
-   property float fogDensity  { set { displaySystem.driver.SetRenderState(this, fogDensity, *(uint *)(void *)&value); } };
+   property float fogDensity  { set { displaySystem.driver.SetRenderState(this, fogDensity, RenderStateFloat { value }.ui); } };
    property Color fogColor    { set { displaySystem.driver.SetRenderState(this, fogColor, value); } };
    property bool blend        { set { displaySystem.driver.SetRenderState(this, blend, value); } };
    property Color ambient     { set { displaySystem.driver.SetRenderState(this, ambient, value); } };
@@ -1117,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;
@@ -1126,7 +1320,9 @@ private:
    DisplaySystem displaySystem;
    void * window;
 
+#if !defined(__EMSCRIPTEN__)
    Mutex mutex { };
+#endif
    int current;
 
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
@@ -1135,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;
@@ -1162,6 +1360,7 @@ private class Display3D
 
    Line rayView, rayWorld, rayLocal;
    Vector3D rayIntersect;
+   float light0Pos[4];
 
    ~Display3D()
    {
@@ -1203,7 +1402,7 @@ private class Display3D
       int strip = 1;
       Vector3Df tmp;
       bool i32bit = primitive.type.indices32bit;
-      uint32 * indices32 = primitive.indices;
+      uint32 * indices32 = primitive.indices32;
       uint16 * indices16 = primitive.indices;
 
       switch(primitive.type.primitiveType)
@@ -1468,6 +1667,7 @@ private class Display3D
                Vector3Df min { MAXFLOAT, MAXFLOAT, MAXFLOAT };
          Vector3Df max { -MAXFLOAT, -MAXFLOAT, -MAXFLOAT };
          int v;
+         bool ix32 = primitive->type.indices32bit;
          if(object != sort->object)
          {
             object = sort->object;
@@ -1486,7 +1686,7 @@ private class Display3D
 
          for(v = 0; v<primitive->nIndices; v++)
          {
-            Vector3Df * local = &mesh.vertices[primitive->indices[v]];
+            Vector3Df * local = &mesh.vertices[ix32 ? primitive->indices32[v] : primitive->indices[v]];
             Vector3Df vertex;
 
             vertex.MultMatrix(local, &matrix);
@@ -1712,13 +1912,13 @@ private class Display3D
 };
 #endif
 
-bool IsDriverTextMode(char * driverName)
+bool IsDriverTextMode(const char * driverName)
 {
    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
    return driver ? driver.textMode : false;
 }
 
-bool IsDriverPrinter(char * driverName)
+bool IsDriverPrinter(const char * driverName)
 {
    subclass(DisplayDriver) driver = GetDisplayDriver(driverName);
    return driver ? driver.printer : false;
@@ -1906,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;
+}