diff --git a/examples/opengl/opengl-msvc.visualdproj b/examples/opengl/opengl-msvc.visualdproj index a37128b1..183d5c8a 100644 --- a/examples/opengl/opengl-msvc.visualdproj +++ b/examples/opengl/opengl-msvc.visualdproj @@ -155,7 +155,7 @@ $(CC) -c 1 $(DMDInstallDir)windows\bin\dmd.exe - $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source + $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/gl3n views views/res views/res/i18n views/res/mdpi views/res/hdpi $(ConfigurationName) $(OutDir) diff --git a/examples/opengl/src/openglexample.d b/examples/opengl/src/openglexample.d index 9c7ffe20..603dbec8 100644 --- a/examples/opengl/src/openglexample.d +++ b/examples/opengl/src/openglexample.d @@ -31,6 +31,8 @@ static if (ENABLE_OPENGL) { import derelict.opengl3.gl3; import derelict.opengl3.gl; + import dlangui.graphics.glsupport; + import dlangui.graphics.gldrawbuf; class MyOpenglWidget : VerticalLayout { this() { @@ -98,16 +100,16 @@ static if (ENABLE_OPENGL) { Log.v("GlGears: OpenGL is disabled"); return; } - _oldApi = !!glLightfv; + _oldApi = glSupport.legacyMode; // !!glLightfv; if (_oldApi) { - drawUsingOldAPI(rc); + drawUsingOldAPI(windowRect, rc); } else { - drawUsingNewAPI(rc); + drawUsingNewAPI(windowRect, rc); } } /// Legacy API example (glBegin/glEnd) - void drawUsingOldAPI(Rect rc) { + void drawUsingOldAPI(Rect windowRect, Rect rc) { static bool _initCalled; if (!_initCalled) { Log.d("GlGears: calling init()"); @@ -126,9 +128,134 @@ static if (ENABLE_OPENGL) { glDisable(GL_DEPTH_TEST); } + MyProgram _program; + + GLTexture _tx; + float[] vertices; + float[] texcoords; + float[4*6*6] colors; + void createMesh() { + if (!_tx) + _tx = new GLTexture("crate"); + // define Cube mesh + vertices = [ + -1.0f,-1.0f,-1.0f, // triangle 1 : begin + -1.0f,-1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, // triangle 1 : end + 1.0f, 1.0f,-1.0f, // triangle 2 : begin + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, // triangle 2 : end + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f + ]; + float tx0 = 0.0f; + float tx1 = 1.0f; + float ty0 = 0.0f; + float ty1 = 1.0f; + texcoords = [ + // + tx0, ty1, + tx0, ty0, + tx1, ty0, + tx1, ty0, + tx1, ty1, + tx0, ty1, + // + tx0, ty1, + tx0, ty0, + tx1, ty0, + tx1, ty0, + tx1, ty1, + tx0, ty1, + // + tx0, ty1, + tx0, ty0, + tx1, ty0, + tx1, ty0, + tx1, ty1, + tx0, ty1, + // + tx0, ty1, + tx0, ty0, + tx1, ty0, + tx1, ty0, + tx1, ty1, + tx0, ty1, + // + tx0, ty1, + tx0, ty0, + tx1, ty0, + tx1, ty0, + tx1, ty1, + tx0, ty1, + // + tx0, ty1, + tx0, ty0, + tx1, ty0, + tx1, ty0, + tx1, ty1, + tx0, ty1, + ]; + // init with white color + foreach(ref cl; colors) + cl = 1.0f; + } + /// New API example (OpenGL3+, shaders) - void drawUsingNewAPI(Rect rc) { + void drawUsingNewAPI(Rect windowRect, Rect rc) { // TODO: put some sample code here + if (!_program) { + _program = new MyProgram(); + createMesh(); + } + if (!_program.check()) + return; + + if (!_tx.isValid) { + Log.e("Invalid texture"); + return; + } + + import gl3n.linalg; + mat4 projectionMatrix = mat4.perspective(rc.width, rc.height, 45.0f, 0.5f, 100.0f); + mat4 viewMatrix = mat4.translation(0.0f, 0.0f, -4.0f); + mat4 modelMatrix = mat4.identity; + mat4 m = projectionMatrix * viewMatrix * modelMatrix; + + float[16] matrix; + for (int y = 0; y < 4; y++) + for (int x = 0; x < 4; x++) + matrix[y * 4 + x] = m[y][x]; + + _program.execute(vertices, colors, texcoords, _tx.texture, true, matrix); } /// returns true is widget is being animated - need to call animate() and redraw @property override bool animating() { return true; } @@ -145,8 +272,100 @@ static if (ENABLE_OPENGL) { } } + // ==================================================================================== + // Shaders based example - // Sample project for old API: GlxGears + class MyProgram : GLProgram { + @property override string vertexSource() { + return q{ + in vec4 vertex; + in vec4 colAttr; + in vec4 texCoord; + out vec4 col; + out vec4 texc; + uniform mat4 matrix; + void main(void) + { + gl_Position = matrix * vertex; + col = colAttr; + texc = texCoord; + } + }; + + } + @property override string fragmentSource() { + return q{ + uniform sampler2D tex; + in vec4 col; + in vec4 texc; + out vec4 outColor; + void main(void) + { + outColor = texture(tex, texc.st) * col; + } + }; + } + + protected GLint matrixLocation; + protected GLint vertexLocation; + protected GLint colAttrLocation; + protected GLint texCoordLocation; + override bool initLocations() { + matrixLocation = getUniformLocation("matrix"); + vertexLocation = getAttribLocation("vertex"); + colAttrLocation = getAttribLocation("colAttr"); + texCoordLocation = getAttribLocation("texCoord"); + return matrixLocation >= 0 && vertexLocation >= 0 && colAttrLocation >= 0 && texCoordLocation >= 0; + } + + bool execute(float[] vertices, float[] colors, float[] texcoords, Tex2D texture, bool linear, float[16] matrix) { + if(!check()) + return false; + + glEnable(GL_BLEND); + checkgl!glDisable(GL_CULL_FACE); + checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + bind(); + checkgl!glUniformMatrix4fv(matrixLocation, 1, false, matrix.ptr); + + texture.setup(); + texture.setSamplerParams(linear); + + VAO vao = new VAO(); + + VBO vbo = new VBO(); + vbo.fill([vertices, colors, texcoords]); + + glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0); + glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * vertices[0].sizeof)); + glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * vertices[0].sizeof + colors.length * colors[0].sizeof)); + + glEnableVertexAttribArray(vertexLocation); + glEnableVertexAttribArray(colAttrLocation); + glEnableVertexAttribArray(texCoordLocation); + + checkgl!glDrawArrays(GL_TRIANGLES, 0, cast(int)vertices.length/3); + + glDisableVertexAttribArray(vertexLocation); + glDisableVertexAttribArray(colAttrLocation); + glDisableVertexAttribArray(texCoordLocation); + + unbind(); + + destroy(vbo); + destroy(vao); + + texture.unbind(); + return true; + } + } + + + + + //===================================================================================== + // Legacy OpenGL API example + // GlxGears import std.math; static __gshared GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; diff --git a/examples/opengl/views/res/mdpi/crate.png b/examples/opengl/views/res/mdpi/crate.png new file mode 100644 index 00000000..18687fff Binary files /dev/null and b/examples/opengl/views/res/mdpi/crate.png differ diff --git a/examples/opengl/views/resources.list b/examples/opengl/views/resources.list index 0fc68b55..bf609e9e 100644 --- a/examples/opengl/views/resources.list +++ b/examples/opengl/views/resources.list @@ -1 +1,2 @@ res/mdpi/tx_fabric.jpg +res/mdpi/crate.png diff --git a/src/dlangui/graphics/gldrawbuf.d b/src/dlangui/graphics/gldrawbuf.d index e4cf031f..3aeb6963 100644 --- a/src/dlangui/graphics/gldrawbuf.d +++ b/src/dlangui/graphics/gldrawbuf.d @@ -318,7 +318,7 @@ private class GLImageCache { destroy(_drawbuf); _drawbuf = null; } - if (_texture.ID != 0) { + if (_texture && _texture.ID != 0) { destroy(_texture); _texture = null; } @@ -928,3 +928,73 @@ public: } } } + + +/// GL Texture object from image +static class GLTexture { + protected int _dx; + protected int _dy; + protected int _tdx; + protected int _tdy; + + @property Point imageSize() { + return Point(_dx, _dy); + } + + protected Tex2D _texture; + /// returns texture object + @property Tex2D texture() { return _texture; } + /// returns texture id + @property uint textureId() { return _texture ? _texture.ID : 0; } + + bool isValid() { + return _texture && _texture.ID; + } + /// image coords to UV + float[2] uv(int x, int y) { + float[2] res; + res[0] = x * cast(float) _dx / _tdx; + res[1] = y * cast(float) _dy / _tdy; + return res; + } + float[2] uv(Point pt) { + float[2] res; + res[0] = pt.x * cast(float) _dx / _tdx; + res[1] = pt.y * cast(float) _dy / _tdy; + return res; + } + /// return UV coords for bottom right corner + float[2] uv() { + return uv(_dx, _dy); + } + + this(string resourceId) { + import dlangui.graphics.resources; + string path = drawableCache.findResource(resourceId); + this(cast(ColorDrawBuf)imageCache.get(path)); + } + + this(ColorDrawBuf buf) { + if (buf) { + _dx = buf.width; + _dy = buf.height; + _tdx = nearestPOT(_dx); + _tdy = nearestPOT(_dy); + _texture = new Tex2D(); + if (!_texture.ID) + return; + uint * pixels = buf.scanLine(0); + if (!glSupport.setTextureImage(_texture, buf.width, buf.height, cast(ubyte*)pixels)) { + destroy(_texture); + _texture = null; + return; + } + } + } + ~this() { + if (_texture && _texture.ID != 0) { + destroy(_texture); + _texture = null; + } + } +} diff --git a/src/dlangui/graphics/glsupport.d b/src/dlangui/graphics/glsupport.d index 9cc9cfd7..171bcb50 100644 --- a/src/dlangui/graphics/glsupport.d +++ b/src/dlangui/graphics/glsupport.d @@ -260,8 +260,8 @@ class SolidFillProgram : GLProgram { col = colAttr; } }; - } + @property override string fragmentSource() { return q{ in vec4 col; @@ -278,7 +278,7 @@ class SolidFillProgram : GLProgram { checkgl!glDisable(GL_CULL_FACE); checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); bind(); - checkgl!glUniformMatrix4fv(matrixLocation, 1, false, glSupport.qtmatrix.ptr); + checkgl!glUniformMatrix4fv(matrixLocation, 1, false, glSupport.projectionMatrix.ptr); } void afterExecute() { @@ -842,7 +842,11 @@ class GLSupport { /// current gl buffer height private int bufferDy; //private float[16] matrix; - private float[16] qtmatrix; + private float[16] _projectionMatrix; + + @property float[16] projectionMatrix() { + return _projectionMatrix; + } void QMatrix4x4_ortho(float left, float right, float bottom, float top, float nearPlane, float farPlane) { @@ -873,7 +877,7 @@ class GLSupport { m[3][3] = 1.0f; for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++) - qtmatrix[y * 4 + x] = m[y][x]; + _projectionMatrix[y * 4 + x] = m[y][x]; } void QMatrix4x4_perspective(float angle, float aspect, float nearPlane, float farPlane) @@ -910,10 +914,11 @@ class GLSupport { for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++) - qtmatrix[y * 4 + x] = m[y][x]; + _projectionMatrix[y * 4 + x] = m[y][x]; } void setOrthoProjection(Rect windowRect, Rect view) { + flushGL(); bufferDx = windowRect.width; bufferDy = windowRect.height; QMatrix4x4_ortho(view.left, view.right, view.top, view.bottom, 0.5f, 50.0f); @@ -923,7 +928,7 @@ class GLSupport { glMatrixMode(GL_PROJECTION); //checkgl!glPushMatrix(); //glLoadIdentity(); - glLoadMatrixf(qtmatrix.ptr); + glLoadMatrixf(_projectionMatrix.ptr); //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); //checkgl!glPushMatrix(); @@ -933,11 +938,21 @@ class GLSupport { } void setPerspectiveProjection(Rect windowRect, Rect view, float fieldOfView, float nearPlane, float farPlane) { - + flushGL(); bufferDx = windowRect.width; bufferDy = windowRect.height; float aspectRatio = cast(float)view.width / cast(float)view.height; QMatrix4x4_perspective(fieldOfView, aspectRatio, nearPlane, farPlane); + if (_legacyMode) { + glMatrixMode(GL_PROJECTION); + //checkgl!glPushMatrix(); + //glLoadIdentity(); + glLoadMatrixf(_projectionMatrix.ptr); + //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + //checkgl!glPushMatrix(); + glLoadIdentity(); + } checkgl!glViewport(view.left, currentFBO ? view.top : windowRect.height - view.bottom, view.width, view.height); } }