Page 1 of 2 12 LastLast
Results 1 to 10 of 39

Thread: OpenGL GLSL - Text rendering query

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    PGD Community Manager AthenaOfDelphi's Avatar
    Join Date
    Dec 2004
    Location
    South Wales, UK
    Posts
    1,246
    Blog Entries
    2

    OpenGL GLSL - Text rendering query

    Hi all,

    So I'm finally getting back to writing some code and I'm working on what will ultimately be a reusable piece of code. The full details aren't important but what I want to do is as follows:-

    Use a PNG image containing a character set (fixed width, fixed height, ASCII character codes Row 1 0-15, Row 2 16-31 etc.) - Alpha provides transparency information
    Use another PNG image containing 16x16 blocks of colour (organised in the same index sequence as character set) - This is loaded and the colours extracted from the cells

    From these, I want to render fixed size text in a 2D orthographic (I think that's the term) way. With the background being one colour and the text being another (if I choose to colorise it - I may not want to change the color of the font texture, but I can simply bind that to vertices so I'm not too worried about that).

    I thought by having the font in white I could colorise it using glColor prior to my glBegin(GL_QUAD) glVertex..... etc. that binds the glyph in the font texture to the four vertices. Unfortunately this changes the color of the fill behind it, so as I understand it, I need to use shaders to achieve what I want but I'm struggling.

    So this image shows my source images (font and palette):-

    Sample.png

    And this, whilst basic shows what I'd like to do:-

    OutputSample.png

    My current code is this (this renders on a 24x24 grid using 16x16 blocks so there is an 8 pixel gap between them):-

    Code:
      glClearColor(0,0,0,0); // background color of the context
      glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // clear background and depth buffer
      glDisable(GL_BLEND);
    
      glViewport(0,0,fRenderWidth,fRenderHeight);
      glLoadIdentity;             // set it to initial state
      gluOrtho2D(0,fRenderWidth,fRenderHeight,0);
    
      glEnable(GL_TEXTURE_2D);
    
      glColor4f(1,1,1,1);
    
      for y:=0 to 15 do //5 do
      begin
        ty:=y*24;
        for x:=0 to 15 do //5 do
        begin
          tx:=x*24;
    
          glDisable(GL_BLEND);
          glBindTexture(GL_TEXTURE_2D,fPalettes[0]);
    
          glBegin(GL_QUADS);
          glTexCoord2f(x*ONE_16TH,y*ONE_16TH);
          glVertex2i(tx,ty);
          glTexCoord2f(x*ONE_16TH+ONE_16TH,y*ONE_16TH);
          glVertex2i(tx+16,ty);
          glTexCoord2f(x*ONE_16TH+ONE_16TH,y*ONE_16TH+ONE_16TH);
          glVertex2i(tx+16,ty+16);
          glTexCoord2f(x*ONE_16TH,y*ONE_16TH+ONE_16TH);
          glVertex2i(tx,ty+16);
          glEnd;
    
          glBindTexture(GL_TEXTURE_2D,fCharacterSets[0].texture);
          glUseProgram(fTextRender);
    
          glBegin(GL_QUADS);
          glTexCoord2f(x*ONE_16TH,y*ONE_16TH);
          glVertex2i(tx,ty);
          glTexCoord2f(x*ONE_16TH+ONE_16TH,y*ONE_16TH);
          glVertex2i(tx+16,ty);
          glTexCoord2f(x*ONE_16TH+ONE_16TH,y*ONE_16TH+ONE_16TH);
          glVertex2i(tx+16,ty+16);
          glTexCoord2f(x*ONE_16TH,y*ONE_16TH+ONE_16TH);
          glVertex2i(tx,ty+16);
          glEnd;
    
          glUseProgram(0);
    
        end;
      end;
    :: AthenaOfDelphi :: My Blog :: My Software ::

  2. #2
    Nice to see you back in action

    As for your problem. While I can't provide you any code since I don't have enough experience with OpenGL I can tell you that there are generally two approaches to achieve what you desire.
    1. In first you simply draw your colored square on quad using your text texture as alpha mask so only text portions is actually rendered. As far as I know this doesn't require use of shaders. This should work just fine with your example images since your font image is just black and white.
    2. In second approach you would use simple blender shader which uses alpha channel of your font texture to actually determine the render area. This shader would then render your font texture and blend it with desired color. And as far as I know when using shaders you don't need to provide your desired color from another texture but you could even pass it to shader as an input parameter.

  3. #3
    PGD Community Manager AthenaOfDelphi's Avatar
    Join Date
    Dec 2004
    Location
    South Wales, UK
    Posts
    1,246
    Blog Entries
    2
    Unfortunately if you use blending and issue say glColor4f(1.0,0.0,0.0,1.0); before creating the quad with the font texture on it, it alters the colour of the quad behind it. So I've been investigating using shaders. I think part of the problem is I'm not experienced enough with the OpenGL render pipeline and the associated background knowledge hence my looking for some assistance. I'm working on the 3D stuff and learning the OpenGL pipeline.

    I should add, that the issue with blending may just be down to me not fully understanding the blending functions.
    :: AthenaOfDelphi :: My Blog :: My Software ::

  4. #4
    Good luck with this, I am interested in the progress since eventually I want my engine project to move away from VCL and have some sort of OpenGL based GUI.

  5. #5
    what will ultimately be a reusable piece of code.
    DON'T.
    I beg you, stop and rethink your strategy.
    Any code that has glBegin/glVertex/etc. in it will be doomed to the compatibility ghetto, limited to 20k vertices while any semi-modern hardware is capable of millions.
    I will write article about Pascal beginners being cursed with this glBegin trap and why is it so horrible... Some day.
    The first consequence is saying bye-bye to GLES and, correspondingly, to Raspberry Pi 2/3 support.

    Here is my horrible code I replaced the glBegin travesty in my project with. It now runs on Raspberry Pi 2 and 3 (I use ANGLE over Direct3d on Windows to develop it because compiling on RPi takes forever):

    Code:
      TDumbUniMesh = class (TAbstractMesh)
        //for absolutely dynamic things that get uploaded to the videocard
        //  every frame.
      public
        components: TRenderComponentSet;
        colors: array of TVector4f;
        normals: array of TVector3f;
        texcoords: array of TVector2f;
        vertices: array of TVector3f;
        indices: array of GLushort; //word aka uint16
        matrix: TMatrix4f;
    
        constructor Create(_components: TRenderComponentSet =
                                         [renc_Vertex, renc_Texcoord, renc_Color]);
    
        //assumes origin in *upper* left corner (y axis downwards, z axis towards the viewer)
        procedure AddQuad2d(left, top, right, bottom: GLfloat;
          txleft: GLfloat = 0; txtop: GLfloat = 0; txright: GLfloat = 1; txbottom: GLfloat = 1);
        procedure Render; override;
        destructor Destroy;
        procedure Color3f(_r, _g, _b: float);
        procedure Color4f(_r, _g, _b, _a: float);
        procedure Color(c: TVector3f); overload;
        procedure Color(c: TVector4f); overload;
        procedure LineTexCoords(txleft, txtop, txright, txbottom: float);
        procedure AddLine(c: array of const;  width: float); overload;
        procedure AddLineLoop(c: array of const;  width: float); overload;
        procedure AddLine(points: array of TVector2f;  width: float); overload;
        procedure AddLineLoop(points: array of TVector2f;  width: float); overload;
    
        //specifies opposite corners of a rectangle
        procedure SetLineTexCoords(a, b: TVector2f; _repeat: boolean); overload;
    
        procedure SetLineGradientTo(c: TVector4f);  // from current color to this
        procedure SetLineTexCoords; // default glyph from | character in the font
      protected
        currentColor: TVector4f;
        lineTC: array[0..3] of TVector2f;
        lineTCrepeat: boolean;
        lineGrad: TVector4f;
        currentNormal: TVector3f;
        maxVertex, maxIndex: integer;
        function ParseAOCToVector2f(var c: array of const): TVector2fArray;
        procedure GenerateLineMesh(points: array of TVector2f; width: float; loop: boolean);
        procedure EnlargeBuffersIfNecessary; // according to maxVertex, maxIndex
      public
        property GetCurrentColor: TVector4f read currentColor;
      end; 
    
    
    procedure TDumbUniMesh.Render;
    var
      i: integer;
      myib: GLuint;
      mymat: TMatrix4f;
      using_shaders: boolean;
      x: TRenderComponentEnum;
    begin
      if Length(indices) <= 0 then Exit;
      if maxIndex < High(indices) then maxIndex:= High(indices);
      using_shaders:= Mother^.GAPI.currentProgram.prog > 0;
    
      case  Mother^.GAPI.Mode of
       {$ifndef glesonly}
        gapi_GL21: begin
        end;
       {$endif glesonly}
        gapi_GLES2: begin
          if not using_shaders then Die(RuEn(
            'Класс %0 не может рисовать, используя GLES2, когда в API матки не указана спецификация текущей программы.',
            'Class %0 cannot render using GLES2 if no current program is specified in mother API.'),
            [AnsiString(Self.ClassName)]);
        end;
      else
        DieUnsupportedGLMode;
      end;
    
      if using_shaders then begin
        with Mother^.GAPI.currentProgram do begin
          for x in [renc_Matrix, renc_Vertex]
            do if location[x] < 0
              then Die(RuEn(
                'Класс %0 не может рисовать, так как в спецификация текущей программы в API матки не хватает компоненты %1.',
                'Class %0 cannot render because the program specified in the mother API doesn''t have component %1.'),
                [AnsiString(Self.ClassName), GetEnumName(typeinfo(TRenderComponentEnum), ord(x))]);
    
          for x in TRenderComponentSet do begin
            if x =  renc_Matrix then continue; // it then takes the matrix from the TGAPI singleton
            if (location[x] >= 0) and not (x in components)
              then Die(RuEn(
                'Данный экземпляр %0 не может рисовать, так как текущая программа требует компоненты %1 которой у него нету.',
                'This %0 instance cannot render because the current program requires the %1 component which it does not have.'),
                [AnsiString(Self.ClassName), GetEnumName(typeinfo(TRenderComponentEnum), ord(x))]);
          end;
    
          CheckGLError;
    
          glBindBuffer ( GL_ARRAY_BUFFER, 0 );
          glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 );
          CheckGLError;
    
          glEnableVertexAttribArray( location[renc_Vertex] );
          CheckGLError;
    
          glVertexAttribPointer( location[renc_Vertex], 3, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), @vertices[0]);
          CheckGLError;
    
          if (renc_Texcoord in components) and (location[renc_Texcoord] >= 0) then begin
            glEnableVertexAttribArray( location[renc_Texcoord] );
            CheckGLError;
    
            glVertexAttribPointer( location[renc_Texcoord], 2, GL_FLOAT, GL_FALSE, sizeof(texcoords[0]), @texcoords[0]);
            CheckGLError;
          end;
    
          if (renc_Color in components) and (location[renc_Color] >= 0) then begin
            glEnableVertexAttribArray( location[renc_Color] );
            CheckGLError;
    
            glVertexAttribPointer( location[renc_Color], 4, GL_FLOAT, GL_FALSE, sizeof(colors[0]), @colors[0]);
            CheckGLError;
          end;
    
          if (renc_Normal in components) and (location[renc_Normal] >= 0) then begin
            glEnableVertexAttribArray( location[renc_Normal] );
            CheckGLError;
    
            glVertexAttribPointer( location[renc_Normal], 3, GL_FLOAT, GL_FALSE, sizeof(normals[0]), @normals[0]);
            CheckGLError;
          end;
    
          if (renc_Matrix in components) then begin
            mymat:= matrix * TGAPI.matrix;
            glUniformMatrix4fv( location[renc_Matrix], 1, GL_FALSE, @mymat);
          end
          else
            glUniformMatrix4fv( location[renc_Matrix], 1, GL_FALSE, @TGAPI.matrix);
    
          CheckGLError;
    
          // http://openglbook.com/chapter-3-index-buffer-objects-and-primitive-types.html
    
    
          glDrawElements(GL_TRIANGLES, maxIndex + 1, GL_UNSIGNED_SHORT, @indices[0]);
          CheckGLError;
    
          glDisableVertexAttribArray(location[renc_Vertex]);
    
          if (renc_Texcoord in components) and (location[renc_Texcoord] >= 0)
            then  glDisableVertexAttribArray(location[renc_Texcoord]);
    
          if (renc_Color in components) and (location[renc_Color] >= 0)
            then glDisableVertexAttribArray(location[renc_Color]);
    
          if (renc_Normal in components) and (location[renc_Normal] >= 0)
            then glDisableVertexAttribArray(location[renc_Normal]);
        end
      end
      {$ifndef glesonly}
      else begin
          // FFP compatible
        {
        glBegin(GL_TRIANGLES);
        for i:=0 to maxIndex do begin
          glColor4fv(@colors[indices[i]]);
          glNormal3fv(@normals[indices[i]]);
          glTexCoord3fv(@texcoords[indices[i]]);
          glVertex4fv(@vertices[indices[i]]);
        end;
        glEnd;
        }
       // https://www.opengl.org/wiki/Client-Side_Vertex_Arrays
    
      //glGetError(); //***TODO this is a hack for an error state provoked somewhere else. Note to self: find and eliminate
        CheckGLError;
    
        glEnableClientState(GL_VERTEX_ARRAY);
        CheckGLError;
        glVertexPointer(3, GL_FLOAT, 0, @vertices[0]);
        CheckGLError;
    
        if (renc_Color in components) then begin
          glEnableClientState(GL_COLOR_ARRAY);
          CheckGLError;
          glColorPointer(4, GL_FLOAT, 0, @colors[0]);
        end
        else
          glDisableClientState(GL_COLOR_ARRAY);
        CheckGLError;
    
        if (renc_Texcoord in components) then begin
          glClientActiveTexture(GL_TEXTURE0);
          CheckGLError;
          glEnableClientState(GL_TEXTURE_COORD_ARRAY);
          CheckGLError;
          glTexCoordPointer(2, GL_FLOAT, 0, @texcoords[0]);
        end
        else
          glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        CheckGLError;
    
        if (renc_Normal in components) then begin
          glEnableClientState(GL_NORMAL_ARRAY);
          CheckGLError;
          glNormalPointer(GL_FLOAT, 0, @normals[0]);
        end
        else
          glDisableClientState(GL_NORMAL_ARRAY);
        CheckGLError;
    
    
        if (renc_Matrix in components) then begin
          glMatrixMode(GL_MODELVIEW);
          glLoadMatrixf(@matrix);
          CheckGLError;
        end;
    
    
        glDrawElements(GL_TRIANGLES, maxIndex + 1, GL_UNSIGNED_SHORT, @indices[0]);
        CheckGLError;
      end;
      {$endif}
    end; 
    
    destructor TDumbUniMesh.Destroy;
    begin
      SetLength(colors, 0);
    //  SetLength(normals, 0);
      SetLength(texcoords, 0);
      SetLength(vertices, 0);
      SetLength(indices, 0);
      inherited;
    end; 
    
    
    
    constructor TDumbUniMesh.Create(_components: TRenderComponentSet);
    begin
      components:= _components;
      if not (renc_Vertex in components) then Die(RuEn(
        'Класс %0 не приспособлен к отсутствию компоненты renc_Vertex',
        'Class %0 cannot work without the renc_Vertex component'),
        [AnsiString(Self.ClassName)]);
    
      if renc_Color in components then begin
        currentColor[0]:= 1;
        currentColor[1]:= 1;
        currentColor[2]:= 1;
        currentColor[3]:= 1;
      end;
      if renc_Normal in components then begin
        currentNormal[0]:= 0;
        currentNormal[1]:= 0;
        currentNormal[2]:= 1;
      end;
      maxIndex:= -1;
      maxVertex:= -1;
      if renc_Matrix in components then begin
        FillChar(matrix, sizeof(matrix), 0);
        matrix[0,0]:= 1;
        matrix[1,1]:= 1;
        matrix[2,2]:= 1;
        matrix[3,3]:= 1;
      end;
    end;
    
    procedure TDumbUniMesh.Color3f(_r, _g, _b: float);
    begin
      if not (renc_Color in components) then Exit;
      currentColor[0]:= _r;
      currentColor[1]:= _g;
      currentColor[2]:= _b;
      currentColor[3]:= 1.0;
    end;
    
    procedure TDumbUniMesh.Color4f(_r, _g, _b, _a: float);
    begin
      if not (renc_Color in components) then Exit;
      currentColor[0]:= _r;
      currentColor[1]:= _g;
      currentColor[2]:= _b;
      currentColor[3]:= _a;
    end;
    
    procedure TDumbUniMesh.Color(c: TVector3f);
    begin
      PVector3f(@currentColor)^:= c;
      currentColor[3]:= 1.0;
    end;
    
    procedure TDumbUniMesh.Color(c: TVector4f);
    begin
      currentColor:= c;
    end;
    
    procedure TDumbUniMesh.LineTexCoords(txleft, txtop, txright, txbottom: float);
    begin
      if not (renc_Texcoord in components) then Exit;
      lineTC[0][0]:= txleft;
      lineTC[0][1]:= txtop;
      lineTC[1][0]:= txleft;
      lineTC[1][1]:= txbottom;
      lineTC[2][0]:= txright;
      lineTC[2][1]:= txtop;
      lineTC[3][0]:= txright;
      lineTC[3][1]:= txbottom;
    end;
    
    
    procedure TDumbUniMesh.EnlargeBuffersIfNecessary;
    var nv: integer;
    begin
      if Length(indices) <= maxIndex + 1
        then
        Setlength(indices, max(16, max(maxIndex, Length(indices) * 2)));
    
      while Length(vertices) < maxVertex + 1 do begin
        nv:= max(16, max ( maxVertex, Length(vertices) * 2 ));
        if renc_Texcoord in components then SetLength(texcoords, nv);
        if renc_Color in components then SetLength(colors, nv);
        SetLength(vertices, nv);
        if renc_Normal in components then Setlength(normals, nv);
      end;
    end;
    
    //reminder: here "top" denotes smaller Y values as my GUI uses y axis downwards
    procedure TDumbUniMesh.AddQuad2d(left, top, right, bottom: GLfloat;
                                        txleft, txtop, txright, txbottom: GLfloat);
    var
      firstindex, firstvertex, newlength, i: GLint;
    begin
      firstindex:= maxIndex + 1; //because are initialized to 0, not -1
      firstvertex:= maxVertex + 1;
    
      inc (maxIndex, 6);
      inc (maxVertex, 4);
    
      EnlargeBuffersIfNecessary;
    
      indices[firstindex + 0]:= firstvertex + 0;
      indices[firstindex + 1]:= firstvertex + 1;
      indices[firstindex + 2]:= firstvertex + 2;
      indices[firstindex + 3]:= firstvertex + 2;
      indices[firstindex + 4]:= firstvertex + 3;
      indices[firstindex + 5]:= firstvertex + 0;
    
      while Length(vertices) < maxVertex + 6 do begin
        newlength:= max(16, Length(vertices) * 2 );
        if renc_Texcoord in components then SetLength(texcoords, newlength);
        if renc_Color in components then SetLength(colors, newlength);
        SetLength(vertices, newlength);
        if renc_Normal in components then Setlength(normals, newlength);
      end;
    
      if renc_Normal in components then
        for i:= firstvertex to firstvertex + 3
          do normals[i]:= currentNormal;
    
      if renc_Color in components then
        for i:= firstvertex to firstvertex + 3
          do colors[i]:= currentColor;
    
      if renc_Texcoord in components then begin
        texcoords[firstvertex + 0][0]:= txleft;
        texcoords[firstvertex + 0][1]:= txtop;
    //    texcoords[firstvertex + 0][2]:= 0;
    
        texcoords[firstvertex + 1][0]:= txright;
        texcoords[firstvertex + 1][1]:= txtop;
    //    texcoords[firstvertex + 1][2]:= 0;
    
        texcoords[firstvertex + 2][0]:= txright;
        texcoords[firstvertex + 2][1]:= txbottom;
    //    texcoords[firstvertex + 2][2]:= 0;
    
        texcoords[firstvertex + 3][0]:= txleft;
        texcoords[firstvertex + 3][1]:= txbottom;
    //    texcoords[firstvertex + 3][2]:= 0;
      end;
    
    
      //dat was awful idea, if funny for i:= firstvertex to firstvertex + 3 do vertices[i][3]:= 1.0;
    
      vertices[firstvertex + 0][0]:= left;
      vertices[firstvertex + 0][1]:= top;
      vertices[firstvertex + 0][2]:= 0;
    
      vertices[firstvertex + 1][0]:= right;
      vertices[firstvertex + 1][1]:= top;
      vertices[firstvertex + 1][2]:= 0;
    
      vertices[firstvertex + 2][0]:= right;
      vertices[firstvertex + 2][1]:= bottom;
      vertices[firstvertex + 2][2]:= 0;
    
      vertices[firstvertex + 3][0]:= left;
      vertices[firstvertex + 3][1]:= bottom;
      vertices[firstvertex + 3][2]:= 0;
    end;

  6. #6
    Quote Originally Posted by Chebmaster View Post
    DON'T.
    I beg you, stop and rethink your strategy.
    I think it's way better to encourage people in what they are doing than to offer alternatives because there is some other/better/advanced/optimal/fancy/cross-platfrom/new ideology/etc... way, unless they ask.

    At least I myself end up feeling demotivated if there are too many alternate approaches, to know which one is best would require learning about all of them, and that is too much new stuff in one go.

  7. #7
    (too long; didn't fit)
    Code:
    //replacement for GL_LINE_STRIP used in GUI everywhere
    procedure TDumbUniMesh.AddLine(c: array of const;  width: float);
    begin
      GenerateLineMesh(ParseAOCToVector2f(c), width, false);
    end;
    
    procedure TDumbUniMesh.AddLineLoop(c: array of const;  width: float);
    begin
      GenerateLineMesh(ParseAOCToVector2f(c), width, true);
    end;
    
    procedure TDumbUniMesh.AddLine(points: array of TVector2f;  width: float);
    begin
      GenerateLineMesh(points, width, false);
    end;
    
    procedure TDumbUniMesh.AddLineLoop(points: array of TVector2f;  width: float);
    begin
      GenerateLineMesh(points, width, true);
    end;
    
    function TDumbUniMesh.ParseAOCToVector2f(var c: array of const): TVector2fArray;
    var
      i, k, n: integer;
      v: float;
    begin
      if (Length(c) mod 2) <> 0 then Die(MI_ERROR_PROGRAMMER_NO_BAKA, [
                       'Odd number of parameters passed to TDumbLineMesh.AddLine']);
      SetLength(Result, Length(c) div 2);
      n:= 0;
      k:= 0;
      for i:= 0 to High(c) do begin
        case c[i].Vtype of
          vtInteger:    v:= c[i].VInteger;
          vtExtended:   v:= c[i].VExtended^;
        else
          Die(MI_ERROR_PROGRAMMER_NO_BAKA,
                      ['Wrong parameter type passed to TDumbUniMesh.AddLine, Vtype='
                                                           + IntToStr(c[i].Vtype)]);
        end;
        Result[n][k]:= v;
        k:= 1 - k;
        if k = 0 then Inc(n);
      end;
    end;
    
    
    procedure TDumbUniMesh.SetLineTexCoords(a, b: TVector2f; _repeat: boolean); overload;
    begin
      lineTC[0]:= a;
      lineTC[1]:= b - a;
      lineTCrepeat:= _repeat;
    end;
    
    procedure TDumbUniMesh.SetLineTexCoords; // default glyph from | character in the font
    var pv4: PVector4f;
    begin
      pv4:= @Mother^.Text.BuiltinFont[Mother^.Text.FixedFontQuality].GlyphTexCoords^[bifgglyph_Pipe];
      lineTC[0][0]:= pv4^[0];
      lineTC[0][1]:= pv4^[1];
      lineTC[1][0]:= pv4^[2] - pv4^[0];
      lineTC[1][1]:= pv4^[3] - pv4^[1];
      lineTCrepeat:= true;
    end;
    
    
    
    procedure TDumbUniMesh.GenerateLineMesh(points: array of TVector2f;
                                                      width: float;  loop: boolean);
    var
      vectors, //vector goes from i to point i + 1
      rightnormals, conormals, // normal goes clockwise in my GUI
        // coordinate system (X axis goes right, Y axis goes down from the top left
        // screen corner, which is NOT how GL usually is set up)
      v, //vertex buffer. Calculate in 2df before copying to real vertices field
      tc //texcoords.
        : array of TVector2f;
      idx: array of integer; //index buffer. To be copied to the real indices.
      connect, degenerate: array of boolean;//the segment i connects smoothly with segment i - 1
      i, j, a, b, c, numConnections, startInd, startVert, curV, curI: integer;
      anglecosine, veclen, confrac, ffrac: float;
    begin
    
    
      if Length(points) < 2 then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
                      ['Not enough points passed to TDumbUniMesh.GenerateLineMesh']);
      if loop then j:= length(points) else j:= length(points) - 1;
      SetLength(vectors, j);
      SetLength(rightnormals, j);
      SetLength(conormals, j);
      SetLength(connect, j);
      SetLength(degenerate, j);
      for i:= 0 to length(points) - 2 do
        vectors[i]:= points[i + 1] - points[i];
      if loop then
        vectors[j - 1]:= points[0] - points[High(points)];
    
      for i:= 0 to High(vectors) do begin
        rightnormals[i][0]:= - vectors[i][1]; // points clockwise 90 deg from the vector
        rightnormals[i][1]:= vectors[i][0];
        QuickNormalize(rightnormals[i]);
        ffrac:= FastInverseSquareRoot(sqr(vectors[i][0]) + sqr(vectors[i][1]));
        conormals[i]:= vectors[i] * ffrac;
        degenerate[i]:= ffrac > (1 / width);
      end;
    
      numConnections:= 0;
      if loop then a:= 0
      else begin
        a:= 1;
        connect[0]:= false;
      end;
      for i:= a to j - 1 do begin
        if i = 0 then b:= j - 1 else b:= i - 1;
        //connect *only* if angle is less that 90 degrees !
        //otherwise render as separate (overlapping) segments
        anglecosine:= DotProduct(rightnormals[b], rightnormals[i]);
        connect[i]:= (anglecosine >= 0) and not degenerate[i] and not degenerate[b];
        if connect[i] then inc(numConnections);
      end;
    
      width*= 0.5;
    
      //finally generate the mesh
      startInd:= MaxIndex + 1;
      curI:= 0;
      SetLength(idx, (2 * 3 * j) + (4 * 3 * numConnections));
    //addlog('i/i %0/%1 %2 %3 %4 ',[curI, length(idx), j, numConnections, connect[0]]);
    
      startVert:= MaxVertex + 1;
      curV:= 0;
      SetLength(v, (4 * j) + (1 * numConnections));
      if renc_Texcoord in components then begin
        SetLength(tc, Length(v));
      end;
    
      for i:= 0 to j - 1 do begin
        if i = (j - 1)
          then a:= 0 //next point index
          else a:= i + 1;
    
        if connect[i] or connect[a]
          then confrac:= 0.5 * ( 2 * width / QuickLength(vectors[i]));
    
        if connect[i] then begin
          //adding the 4 triangles of the connection
    
          if i > 0
            then c:= curV //vertices added by the previous segment
            else c:= length(v); //vertices that *will* be added by the last segment
              // It's a good thing our algorithm is 100% predictable, isn't it?
    
          idx[curI + 0]:= (* CurV - 1;
          Так вот где таилась погибель моя!
          Мне смертию кость угрожала!
          Из мёртвой главы Access Violation in ig4icd32.dll,
          Шипя, между тем, выползало *) c - 1;
          idx[curI + 1]:= c - 3;
          idx[curI + 2]:= CurV + 0 ;
    
          idx[curI + 3]:= CurV + 1;
          idx[curI + 4]:= c - 2;
          idx[curI + 5]:= c - 1;
    
          idx[curI + 6]:= CurV + 0;
          idx[curI + 7]:= CurV + 1;
          idx[curI + 8]:= c - 1;
    
          idx[curI + 9]:= c - 1;
          idx[curI +10]:= c - 2;
          idx[curI +11]:= c - 3;
    
          inc(curI, 12);
    
          v[curV + 0]:= points[i] + ((conormals[i] - rightnormals[i]) * width);
          v[curV + 1]:= points[i] + ((conormals[i] + rightnormals[i]) * width);
          if renc_Texcoord in components then begin
            tc[curV + 0]:= lineTC[0] + lineTC[1] * ToVector2f(0, confrac);
            tc[curV + 1]:= lineTC[0] + lineTC[1] * ToVector2f(1, confrac);
          end;
        end
        else begin
          v[curV + 0]:= points[i] - (rightnormals[i] * width);
          v[curV + 1]:= points[i] + (rightnormals[i] * width);
          if renc_Texcoord in components then begin
            tc[curV + 0]:= lineTC[0];
            tc[curV + 1]:= lineTC[0]+ lineTC[1] * ToVector2f(1, 0);
          end;
        end;
    
        idx[curI + 0]:= curV + 1; //1st body triangle
        idx[curI + 1]:= curV + 0;
        idx[curI + 2]:= curV + 2;
    
        idx[curI + 3]:= curV + 2; //2nd body triangle
        idx[curI + 4]:= curV + 3;
        idx[curI + 5]:= curV + 1;
    
        inc(curI, 6);
    
        if connect[a] then begin
          v[curV + 2]:= points[a] - ((conormals[i] + rightnormals[i]) * width);
          v[curV + 3]:= points[a] - ((conormals[i] - rightnormals[i]) * width);
          if renc_Texcoord in components then begin
            tc[curV + 2]:= lineTC[0] + lineTC[1] * ToVector2f(0, 1 - confrac);
            tc[curV + 3]:= lineTC[0] + lineTC[1] * ToVector2f(1, 1 - confrac);
          end;
    
          //middle vertex
          v[curV + 4]:= points[a] + (conormals[a] - conormals[i]) * width * 0.5;
          if renc_Texcoord in components then begin
            tc[curV + 4]:= lineTC[0] + lineTC[1] * ToVector2f(0.5, 1);
          end;
    
          inc(curV, 5);
        end
        else begin
          v[curV + 2]:= points[a] - (rightnormals[i] * width);
          v[curV + 3]:= points[a] + (rightnormals[i] * width);
          if renc_Texcoord in components then begin
          tc[curV + 2]:= lineTC[0] + lineTC[1] * ToVector2f(0, 1);
          tc[curV + 3]:= lineTC[0] + lineTC[1] * ToVector2f(1, 1);
          end;
    
          inc(curV, 4);
        end;
      end;
    
      inc (MaxIndex, Length(idx));
      inc (MaxVertex, Length(v));
      EnlargeBuffersIfNecessary;
    
      for i:= 0 to High(idx) do begin
        indices[startInd + i]:= startVert + idx[i];
      end;
    
      ffrac:= 1 / High(v);
      for i:= 0 to High(v) do begin
        vertices[startVert + i]:= v[i];
        if renc_Texcoord in components
          then texcoords[startVert + i]:= tc[i];
        if renc_Normal in components
          then normals[startVert + i]:= currentNormal;
        if renc_Texcoord in components
          then colors[startVert + i]:= currentColor + lineGrad * (i * ffrac);
      end;
    end;
    
    procedure TDumbUniMesh.SetLineGradientTo(c: TVector4f);
    begin
      lineGrad:= c - currentColor;
    end;
    .. and usage (converted my old uber-crappy code to this less-crappy abstraction layer):
    Code:
       Mesh:= TDumbUniMesh.Create;
       Mesh.Color4f(
          f * Mother^.Display.FadeIn, f * Mother^.Display.FadeIn, f * Mother^.Display.FadeIn, 1);
       Mesh.AddQuad2d(
         0, 0, Mother^.Display.ClientRect.Width, Mother^.Display.ClientRect.Height,
         0, 0, 1, 1);
       TGAPI.SetGLStatesForGUI;
       case  Mother^.GAPI.Mode of
        {$ifndef glesonly}
         gapi_GL21 : begin
           glDisable(GL_ALPHA_TEST);
         end;
        {$endif glesonly}
         gapi_GLES2: begin
         end;
       else
         DieUnsupportedGLMode;
       end;
    
       //f_current_bgtex
       //f_current_bgw
       //f_current_bgh
    
       glDisable(GL_BLEND);
       //glEnable(GL_TEXTURE_2D);
       if Mother^.Display.Background.custom = 0
         then glBindTexture(GL_TEXTURE_2D, f_bgtex[Mother^.Display.Background._default])
         else glBindTexture(GL_TEXTURE_2D, Mother^.Display.Background.custom);
       Mesh.Render;
       Mesh.Free;
    {
       glColor4f(f * Mother^.Display.FadeIn, f * Mother^.Display.FadeIn, f * Mother^.Display.FadeIn, 1);
       glBindTexture(GL_TEXTURE_2D, f_bgtex);
       glBegin(GL_QUADS);
         glTexCoord2f(0, 0);
         glVertex2f(0, 0); //bottom left corner
         glTexCoord2f(0, 1);
         glVertex2f(0, Mother^.Display.WindowClientRect.Height);
         glTexCoord2f(1, 1);
         glVertex2f(Mother^.Display.WindowClientRect.Width, Mother^.Display.WindowClientRect.Height);
         glTexCoord2f(1, 0);
         glVertex2f(Mother^.Display.WindowClientRect.Width, 0);
       glEnd;
       glEnable(GL_ALPHA_TEST);
       glEnable(GL_BLEND);
    }

  8. #8
    PGD Community Manager AthenaOfDelphi's Avatar
    Join Date
    Dec 2004
    Location
    South Wales, UK
    Posts
    1,246
    Blog Entries
    2
    Whilst I appreciate the advice that I shouldn't use glBegin... unfortunately posting a class that apparently doesn't use it doesn't really help me as it's way to complicated for me right now. I have no idea what the other alternatives are so I don't have a clue where to start looking in that class.

    glBegin is how I've done all my OpenGL stuff and it's fine for what I'm doing at the moment. I know much of what I'm doing is less than optimal, but that aside, any advice on the question regarding shaders?
    :: AthenaOfDelphi :: My Blog :: My Software ::

  9. #9
    Quote Originally Posted by AthenaOfDelphi View Post
    I thought by having the font in white I could colorise it using glColor prior to my glBegin(GL_QUAD) glVertex..... etc. that binds the glyph in the font texture to the four vertices. Unfortunately this changes the color of the fill behind it, so as I understand it, I need to use shaders to achieve what I want but I'm struggling.
    Try calling the following before your rendering loop:

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    Quote Originally Posted by Chebmaster View Post
    DON'T.
    I beg you, stop and rethink your strategy.
    Any code that has glBegin/glVertex/etc. in it will be doomed to the compatibility ghetto, limited to 20k vertices while any semi-modern hardware is capable of millions.
    This is a really bad advice. See this: Nvidia: Deprecation Myths. Of course everything pre-GL3 is considered legacy now, but it doesn't mean you can't use it for learning. Newer APIs such as Metal/Vulkan/Direct3D 12 are notoriously difficult to get started, so if you want to get something working quickly, glBegin/glEnd is as good as any other option, and in regards in performance, it is likely faster than the source code you suggested (and I challenge you to prove different: do the benchmarks!)

    I would suggest to use whatever technique you find easier to learn - it is better to have something working in the way you want, than not having anything at all.

  10. #10
    This is a really bad advice. See this: Nvidia: Deprecation Myths.
    You are wrong.
    I was not talking about "below 3.3". I was talking about "certain techniques below 2".

    OpenGL 2.1 is *ancient* now - but it's what you should use for learning.
    OpenGL *below* 2, though... It's not just deprecated, it's not just "left to rot" -- you will have to relearn *everything* you learned because those legacy-squared techniques do not scale.

    I performed benchmarking myself and I found that glBegin-style code begins to lag at about 20 000 vertices. That's how badly "left to rot" it is. That's how inefficiently it is emulated.

    So, you learn this, you acquire really bad habits learning it you may even begin making libraries for future use... and then you will have to relearn 50%. You will have to scrap your libraries or salvage what you could of them them using really ugly hacks. You will have to fight your bad habits.

    I've been there. I've done that. I speak from experience.
    It cost me months of my free time.
    I become sad when I see people walking unawares into the same trap.

    P.S. The glBegin technique grew outdated in 20th century, between Quake 2 and Quake 3. If you look at Open Arena sources you will see just half a dozen places where glBegin is used and the vast majority of them are for rendering a single fullscreen quad.
    Long before Crysis. Long before Oblivion. Carmack saw them unfit in 1999.
    Nuff said.

    and I challenge you to prove different: do the benchmarks!
    I definitely will (I'm curious myself how much better my new class is) but it may take weeks: my game engine is currently down for rehauling.
    I can only say that when my subdivided rotating sphere was still working, the difference between glBegin and the new class was difference between SwapBuffers taking most of the allotted frame time and taking a silver of it (intel HD3000)
    Last edited by Chebmaster; 20-01-2018 at 06:58 PM.

Page 1 of 2 12 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •