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;
Bookmarks