--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali/public-api/actors/drawable-actor.h>
+
+#include "native-renderer.h"
+
+// Include the GLES header
+#include <GLES3/gl3.h>
+#include <dali/graphics-api/graphics-buffer-create-info.h>
+
+#include <dali/devel-api/common/stage-devel.h>
+
+using TextLabel = Dali::Toolkit::TextLabel;
+using namespace Dali;
+
+// This example shows DrawableActor using native GL code to clear screen to red
+// and renders TextLabel on top of it.
+//
+class DrawableActorExampleController : public ConnectionTracker
+{
+public:
+
+ explicit DrawableActorExampleController(Application& application)
+ : mApplication(application)
+ {
+ // Connect to the Application's Init signal
+ mApplication.InitSignal().Connect(this, &DrawableActorExampleController::Create);
+ }
+
+ ~DrawableActorExampleController() override = default; // Nothing to do in destructor
+
+ // The Init signal is received once (only) during the Application lifetime
+ void Create(Application& application)
+ {
+ // Get a handle to the window
+ Window window = application.GetWindow();
+ window.SetBackgroundColor(Color::WHITE);
+
+ // Create native renderer
+ mRenderer = std::make_unique<NativeRenderer>(window.GetSize().GetWidth(), window.GetSize().GetHeight());
+
+ // Create render callback
+ mRenderCallback = RenderCallback::New<NativeRenderer>( mRenderer.get(), &NativeRenderer::OnRender );
+
+ // Create drawable actor
+ mGLActor = DrawableActor::New( *mRenderCallback );
+
+ mGLActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+ mGLActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
+
+ // Set size on the actor (half the window size to show that glClear() and scissor test work together)
+ mGLActor.SetProperty( Actor::Property::SIZE, Size( window.GetSize() ) * 0.5f);
+
+ // Add actor to the scene
+ window.Add(mGLActor);
+
+ // Create TextLabel
+ mTextLabel = TextLabel::New("This text overlays DrawableActor");
+ mTextLabel.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+ mTextLabel.SetProperty(Dali::Actor::Property::NAME, "SomeTextLabel");
+ window.Add(mTextLabel);
+
+ // Respond to a touch anywhere on the window
+ window.GetRootLayer().TouchedSignal().Connect(this, &DrawableActorExampleController::OnTouch);
+
+ // Respond to key events
+ window.KeyEventSignal().Connect(this, &DrawableActorExampleController::OnKeyEvent);
+ }
+
+ bool OnTouch(Actor actor, const TouchEvent& touch)
+ {
+ // quit the application
+ mApplication.Quit();
+ return true;
+ }
+
+ void OnKeyEvent(const KeyEvent& event)
+ {
+ if(event.GetState() == KeyEvent::DOWN)
+ {
+ if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
+ {
+ mApplication.Quit();
+ }
+ }
+ }
+
+ TextLabel mTextLabel;
+ DrawableActor mGLActor;
+
+ std::unique_ptr<RenderCallback> mRenderCallback;
+ std::unique_ptr<NativeRenderer> mRenderer{nullptr};
+
+private:
+ Application& mApplication;
+};
+
+int DALI_EXPORT_API main(int argc, char** argv)
+{
+ Application application = Application::New(&argc, &argv);
+ DrawableActorExampleController test(application);
+ application.MainLoop();
+ return 0;
+}
--- /dev/null
+//
+// Created by adam.b on 15/03/2022.
+//
+#include "native-renderer.h"
+
+/**
+ * Set of math helper functions
+ */
+namespace
+{
+
+constexpr GLfloat CUBE_VERTICES[] = {-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};
+
+constexpr GLfloat CUBE_COLOURS[] = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
+
+constexpr GLushort CUBE_INDICES[] = {0, 2, 3, 0, 1, 3, 4, 6, 7, 4, 5, 7, 8, 9, 10, 11, 8, 10, 12, 13, 14, 15, 12, 14, 16, 17, 18, 16, 19, 18, 20, 21, 22, 20, 23, 22};
+
+float matrixDegreesToRadians(float degrees)
+{
+ return M_PI * degrees / 180.0f;
+}
+
+[[maybe_unused]] void matrixIdentityFunction(float* matrix)
+{
+ if(matrix == NULL)
+ {
+ return;
+ }
+ matrix[0] = 1.0f;
+ matrix[1] = 0.0f;
+ matrix[2] = 0.0f;
+ matrix[3] = 0.0f;
+ matrix[4] = 0.0f;
+ matrix[5] = 1.0f;
+ matrix[6] = 0.0f;
+ matrix[7] = 0.0f;
+ matrix[8] = 0.0f;
+ matrix[9] = 0.0f;
+ matrix[10] = 1.0f;
+ matrix[11] = 0.0f;
+ matrix[12] = 0.0f;
+ matrix[13] = 0.0f;
+ matrix[14] = 0.0f;
+ matrix[15] = 1.0f;
+}
+
+[[maybe_unused]] void matrixMultiply(float* destination, float* operand1, float* operand2)
+{
+ float theResult[16];
+ int i, j = 0;
+ for(i = 0; i < 4; i++)
+ {
+ for(j = 0; j < 4; j++)
+ {
+ theResult[4 * i + j] = operand1[j] * operand2[4 * i] + operand1[4 + j] * operand2[4 * i + 1] +
+ operand1[8 + j] * operand2[4 * i + 2] + operand1[12 + j] * operand2[4 * i + 3];
+ }
+ }
+ for(int i = 0; i < 16; i++)
+ {
+ destination[i] = theResult[i];
+ }
+}
+
+[[maybe_unused]] void matrixTranslate(float* matrix, float x, float y, float z)
+{
+ float temporaryMatrix[16];
+ matrixIdentityFunction(temporaryMatrix);
+ temporaryMatrix[12] = x;
+ temporaryMatrix[13] = y;
+ temporaryMatrix[14] = z;
+ matrixMultiply(matrix, temporaryMatrix, matrix);
+}
+
+[[maybe_unused]] void matrixScale(float* matrix, float x, float y, float z)
+{
+ float tempMatrix[16];
+ matrixIdentityFunction(tempMatrix);
+ tempMatrix[0] = x;
+ tempMatrix[5] = y;
+ tempMatrix[10] = z;
+ matrixMultiply(matrix, tempMatrix, matrix);
+}
+
+[[maybe_unused]] void matrixRotateX(float* matrix, float angle)
+{
+ float tempMatrix[16];
+ matrixIdentityFunction(tempMatrix);
+ tempMatrix[5] = cos(matrixDegreesToRadians(angle));
+ tempMatrix[9] = -sin(matrixDegreesToRadians(angle));
+ tempMatrix[6] = sin(matrixDegreesToRadians(angle));
+ tempMatrix[10] = cos(matrixDegreesToRadians(angle));
+ matrixMultiply(matrix, tempMatrix, matrix);
+}
+[[maybe_unused]] void matrixRotateY(float* matrix, float angle)
+{
+ float tempMatrix[16];
+ matrixIdentityFunction(tempMatrix);
+ tempMatrix[0] = cos(matrixDegreesToRadians(angle));
+ tempMatrix[8] = sin(matrixDegreesToRadians(angle));
+ tempMatrix[2] = -sin(matrixDegreesToRadians(angle));
+ tempMatrix[10] = cos(matrixDegreesToRadians(angle));
+ matrixMultiply(matrix, tempMatrix, matrix);
+}
+[[maybe_unused]] void matrixRotateZ(float* matrix, float angle)
+{
+ float tempMatrix[16];
+ matrixIdentityFunction(tempMatrix);
+ tempMatrix[0] = cos(matrixDegreesToRadians(angle));
+ tempMatrix[4] = -sin(matrixDegreesToRadians(angle));
+ tempMatrix[1] = sin(matrixDegreesToRadians(angle));
+ tempMatrix[5] = cos(matrixDegreesToRadians(angle));
+ matrixMultiply(matrix, tempMatrix, matrix);
+}
+
+void matrixFrustum(float* matrix, float left, float right, float bottom, float top, float zNear, float zFar)
+{
+ float temp, xDistance, yDistance, zDistance;
+ temp = 2.0 * zNear;
+ xDistance = right - left;
+ yDistance = top - bottom;
+ zDistance = zFar - zNear;
+ matrixIdentityFunction(matrix);
+ matrix[0] = temp / xDistance;
+ matrix[5] = temp / yDistance;
+ matrix[8] = (right + left) / xDistance;
+ matrix[9] = (top + bottom) / yDistance;
+ matrix[10] = (-zFar - zNear) / zDistance;
+ matrix[11] = -1.0f;
+ matrix[14] = (-temp * zFar) / zDistance;
+ matrix[15] = 0.0f;
+}
+
+[[maybe_unused]] void matrixPerspective(float* matrix, float fieldOfView, float aspectRatio, float zNear, float zFar)
+{
+ float ymax, xmax;
+ ymax = zNear * tanf(fieldOfView * M_PI / 360.0);
+ xmax = ymax * aspectRatio;
+ matrixFrustum(matrix, -xmax, xmax, -ymax, ymax, zNear, zFar);
+}
+
+} // namespace
+
+NativeRenderer::NativeRenderer(uint32_t width, uint32_t height)
+: mWidth(width),
+ mHeight(height)
+{
+}
+
+bool NativeRenderer::OnRender(const Dali::RenderCallbackInput& input)
+{
+ if(mState == State::INIT)
+ {
+ Setup(mWidth, mHeight);
+ mState = State::RENDER;
+ }
+
+ RenderCube(input);
+
+ return false;
+}
+
+void NativeRenderer::PrepareShader()
+{
+ static const char glVertexShader[] =
+ "attribute vec4 vertexPosition;\n"
+ "attribute vec3 vertexColour;\n"
+ "varying vec3 fragColour;\n"
+ "uniform mat4 projection;\n"
+ "uniform mat4 modelView;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = projection * modelView * vertexPosition;\n"
+ " fragColour = vertexColour;\n"
+ "}\n";
+
+ static const char glFragmentShader[] =
+ "precision mediump float;\n"
+ "varying vec3 fragColour;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(fragColour, 1.0);\n"
+ "}\n";
+
+ mProgramId = CreateProgram(glVertexShader, glFragmentShader);
+}
+
+void NativeRenderer::Setup(int width, int height)
+{
+ PrepareShader();
+
+ mVertexLocation = glGetAttribLocation(mProgramId, "vertexPosition");
+ mVertexColourLocation = glGetAttribLocation(mProgramId, "vertexColour");
+ mProjectionLocation = glGetUniformLocation(mProgramId, "projection");
+ mModelViewLocation = glGetUniformLocation(mProgramId, "modelView");
+
+ glEnable(GL_DEPTH_TEST);
+ glViewport(0, 0, width, height);
+}
+
+GLuint NativeRenderer::CreateProgram(const char* vertexSource, const char* fragmentSource)
+{
+ GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource);
+ if(!vertexShader)
+ {
+ return 0;
+ }
+ GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource);
+ if(!fragmentShader)
+ {
+ return 0;
+ }
+ GLuint program = glCreateProgram();
+ if(program)
+ {
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if(linkStatus != GL_TRUE)
+ {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if(bufLength)
+ {
+ char* buf = (char*)malloc(bufLength);
+ if(buf)
+ {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ free(buf);
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+}
+
+GLuint NativeRenderer::LoadShader(GLenum shaderType, const char* shaderSource)
+{
+ GLuint shader = glCreateShader(shaderType);
+ if(shader != 0)
+ {
+ glShaderSource(shader, 1, &shaderSource, NULL);
+ glCompileShader(shader);
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if(compiled != GL_TRUE)
+ {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+
+ if(infoLen > 0)
+ {
+ char* logBuffer = (char*)malloc(infoLen);
+
+ if(logBuffer != NULL)
+ {
+ glGetShaderInfoLog(shader, infoLen, NULL, logBuffer);
+ free(logBuffer);
+ logBuffer = NULL;
+ }
+
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ }
+
+ return shader;
+}
+
+void NativeRenderer::RenderCube(const Dali::RenderCallbackInput& input)
+{
+ static float angle = 0.0f;
+
+ auto x = float(mWidth - input.size.width) * 0.5f;
+ auto y = float(mHeight - input.size.height) * 0.5f;
+ auto w = input.size.width;
+ auto h = input.size.height;
+
+ matrixPerspective(mProjectionMatrix, 45, (float)w / (float)h, 0.1f, 100);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(x, y, w, h);
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ matrixIdentityFunction(mModelViewMatrix);
+ matrixScale(mModelViewMatrix, 0.5f, 0.5f, 0.5f);
+ matrixRotateX(mModelViewMatrix, angle);
+ matrixRotateY(mModelViewMatrix, angle);
+ matrixTranslate(mModelViewMatrix, 0.0f, 0.0f, -10.0f);
+ glUseProgram(mProgramId);
+ glVertexAttribPointer(mVertexLocation, 3, GL_FLOAT, GL_FALSE, 0, CUBE_VERTICES);
+ glEnableVertexAttribArray(mVertexLocation);
+ glVertexAttribPointer(mVertexColourLocation, 3, GL_FLOAT, GL_FALSE, 0, CUBE_COLOURS);
+ glEnableVertexAttribArray(mVertexColourLocation);
+ glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix);
+ glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix);
+ glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, CUBE_INDICES);
+ angle += 1;
+ if(angle > 360)
+ {
+ angle -= 360;
+ }
+}