From 296f981a4917361875a6fcf76c4f254554a26390 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Sun, 31 May 2026 07:00:55 +0800 Subject: [PATCH 1/4] Configure EGL config chooser for GLSurfaceView in GLActivity Add Stencil: 8 bits in the project --- tutorial/src/main/java/com/minininja/learngles/GLActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt b/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt index 4f68c99a..808af31b 100644 --- a/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/GLActivity.kt @@ -189,6 +189,7 @@ fun OpenGLContainer( factory = { context -> GLSurfaceView(context).apply { setEGLContextClientVersion(3) + setEGLConfigChooser(8, 8, 8, 8, 16, 8) setEGLContextFactory(object : EGLContextFactory { private val EGL_CONTEXT_CLIENT_VERSION = 0x3098 From 01927d727365d09afda24e4e5b61b1a4396b5682 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Sun, 31 May 2026 07:49:44 +0800 Subject: [PATCH 2/4] Add stencil test toggle to UI and implement native event handling - Added a stencil test toggle to `StencilTestingActivity` and updated `ControlPanelContent` to support dynamic titles. - Implemented `stenciltest_event` handling in `StencilTestingScene` to receive state changes from the UI. - Updated native rendering logic to conditionally enable `GL_STENCIL_TEST` and render object outlines based on the toggle state. --- .../main/cpp/basic/StencilTestingScene.cpp | 80 +++++++++++++------ .../src/main/cpp/basic/StencilTestingScene.h | 10 +++ .../learngles/basic/StencilTestingActivity.kt | 19 ++++- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/tutorial/src/main/cpp/basic/StencilTestingScene.cpp b/tutorial/src/main/cpp/basic/StencilTestingScene.cpp index 6441b0c6..32493dcb 100644 --- a/tutorial/src/main/cpp/basic/StencilTestingScene.cpp +++ b/tutorial/src/main/cpp/basic/StencilTestingScene.cpp @@ -18,7 +18,7 @@ void StencilTestingScene::init() { glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); - glEnable(GL_STENCIL_TEST); + if (stencilTestOn) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); glStencilFunc(GL_NOTEQUAL, 1, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); @@ -135,6 +135,8 @@ void StencilTestingScene::draw() { // don't forget to clear the stencil buffer! glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + if (stencilTestOn) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); + // set uniforms glm::mat4 view = m_camera->getViewMatrix(); glm::mat4 projection = m_camera->getProjectionMatrix(); @@ -190,31 +192,33 @@ void StencilTestingScene::draw() { // The parts of the buffer that are 1 are not drawn, thus only drawing // the objects' size differences, making it look like borders. // ------------------------------------------------------------------------------ - glStencilFunc(GL_NOTEQUAL, 1, 0xFF); - glStencilMask(0x00); - glDisable(GL_DEPTH_TEST); - if(m_pShaderSingleColor) - { - m_pShaderSingleColor->use(); - float scale = 1.1f; - // cubes - glBindVertexArray(m_cubeVAO); - glBindTexture(GL_TEXTURE_2D, m_cubeTexture); - model = glm::mat4(1.0f); - model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f)); - model = glm::scale(model, glm::vec3(scale, scale, scale)); - m_pShaderSingleColor->setMat4("model", model); - glDrawArrays(GL_TRIANGLES, 0, 36); - model = glm::mat4(1.0f); - model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f)); - model = glm::scale(model, glm::vec3(scale, scale, scale)); - m_pShaderSingleColor->setMat4("model", model); - glDrawArrays(GL_TRIANGLES, 0, 36); + if (stencilTestOn) { + glStencilFunc(GL_NOTEQUAL, 1, 0xFF); + glStencilMask(0x00); + glDisable(GL_DEPTH_TEST); + if(m_pShaderSingleColor) + { + m_pShaderSingleColor->use(); + float scale = 1.1f; + // cubes + glBindVertexArray(m_cubeVAO); + glBindTexture(GL_TEXTURE_2D, m_cubeTexture); + model = glm::mat4(1.0f); + model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f)); + model = glm::scale(model, glm::vec3(scale, scale, scale)); + m_pShaderSingleColor->setMat4("model", model); + glDrawArrays(GL_TRIANGLES, 0, 36); + model = glm::mat4(1.0f); + model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f)); + model = glm::scale(model, glm::vec3(scale, scale, scale)); + m_pShaderSingleColor->setMat4("model", model); + glDrawArrays(GL_TRIANGLES, 0, 36); + } + glBindVertexArray(0); + glStencilMask(0xFF); + glStencilFunc(GL_ALWAYS, 0, 0xFF); + glEnable(GL_DEPTH_TEST); } - glBindVertexArray(0); - glStencilMask(0xFF); - glStencilFunc(GL_ALWAYS, 0, 0xFF); - glEnable(GL_DEPTH_TEST); } void StencilTestingScene::destroy() { @@ -246,6 +250,9 @@ std::map StencilTestingScene::propertyEvent(std::map targetCamera->reset(); } } + +bool StencilTestingScene::isStencilTestOn() const { + return stencilTestOn; +} + +void StencilTestingScene::setStencilTestOn(bool stencilTestOn) { + StencilTestingScene::stencilTestOn = stencilTestOn; +} + +void StencilTestingScene::parseStencilTestEvent(std::map &event) { + if (auto it = event.find("on"); it != event.end()) { + if (it->second.type() == typeid(bool)) { + const auto& val = std::any_cast(it->second); + if (val) { + stencilTestOn = true; + glEnable(GL_STENCIL_TEST); + } else { + stencilTestOn = false; + glDisable(GL_STENCIL_TEST); + } + } + } +} diff --git a/tutorial/src/main/cpp/basic/StencilTestingScene.h b/tutorial/src/main/cpp/basic/StencilTestingScene.h index 5aceee84..defccb94 100644 --- a/tutorial/src/main/cpp/basic/StencilTestingScene.h +++ b/tutorial/src/main/cpp/basic/StencilTestingScene.h @@ -23,6 +23,10 @@ public : virtual ~StencilTestingScene(); + bool isStencilTestOn() const; + + void setStencilTestOn(bool stencilTestOn); + private: Camera* m_camera = nullptr; Shader* m_pShader = nullptr; @@ -34,7 +38,13 @@ public : unsigned int m_cubeTexture = 0u; unsigned int m_floorTexture = 0u; + //region properties + bool stencilTestOn = true; + //endregion + void parseTargetCameraEvent(std::map &event); + + void parseStencilTestEvent(std::map &map); }; #endif //LEARNGLES_STENCILTESTINGSCENE_H diff --git a/tutorial/src/main/java/com/minininja/learngles/basic/StencilTestingActivity.kt b/tutorial/src/main/java/com/minininja/learngles/basic/StencilTestingActivity.kt index 4a279832..f9ac1fd4 100644 --- a/tutorial/src/main/java/com/minininja/learngles/basic/StencilTestingActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/basic/StencilTestingActivity.kt @@ -23,6 +23,8 @@ import com.minininja.learngles.NativeHelper class StencilTestingActivity : GLActivity() { private var active by mutableStateOf(true) + private var stencilTestOn by mutableStateOf(true) + override fun createTouchCallback(): Layer3DTouchCallback { return object : Layer3DTouchCallback { override fun onSingleTouch( @@ -75,16 +77,31 @@ class StencilTestingActivity : GLActivity() { @Composable override fun ControlPanel() { ControlPanelContent( + title = "Target Camera Events", active = active, onActiveChange = { active = it }, ) + + ControlPanelContent( + title = "Stencil Test", + active = stencilTestOn, + onActiveChange = { + stencilTestOn = it + val event = mapOf("event_id" to "stenciltest_event", "on" to it) + glSurfaceView?.queueEvent { + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } + }, + ) } } @Composable private fun ControlPanelContent( + title: String, active: Boolean, onActiveChange: (Boolean) -> Unit, modifier: Modifier = Modifier, @@ -94,7 +111,7 @@ private fun ControlPanelContent( ) { Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier.padding(8.dp)) { - Text(text = "Target Camera Events", color = Color.White) + Text(text = title, color = Color.White) Checkbox( checked = active, onCheckedChange = onActiveChange From 3bbf0017e323390dd0faffabce1a9589c0ce2acb Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Sun, 31 May 2026 22:22:11 +0800 Subject: [PATCH 3/4] Implement dynamic blending configuration and UI controls Added Compose-based dropdown selectors and toggles to `BlendingActivity` to allow real-time adjustment of OpenGL blending parameters. This includes support for modifying source/destination factors and blend equations. Key changes: - **UI:** Introduced `SelectorControlPanelContent` using Material3 `ExposedDropdownMenuBox` to manage blending factors and equations. - **Native:** Implemented event parsing in `BlendingScene` to handle dynamic updates from the UI and apply `glBlendFunc` and `glBlendEquation` during the draw cycle. - **Scene Cleanup:** Refactored vertex data for better readability, enabled `GL_CULL_FACE`, and updated asset paths for shaders and textures. - **Shaders:** Renamed blending shaders for consistency and performed minor cleanup of float literals in tessellation shaders. --- .../{3.2.blending.frag => blending.frag} | 0 .../{3.2.blending.vert => blending.vert} | 0 .../tesselation_shaders/8.3.gpuheight.frag | 2 +- .../tesselation_shaders/8.3.gpuheight.tesc | 4 +- .../tesselation_shaders/8.3.gpuheight.tese | 2 +- tutorial/src/main/cpp/basic/BlendingScene.cpp | 208 ++++++++++++------ tutorial/src/main/cpp/basic/BlendingScene.h | 25 +++ .../learngles/basic/BlendingActivity.kt | 134 ++++++++++- 8 files changed, 306 insertions(+), 69 deletions(-) rename tutorial/src/main/assets/shaders/blending/{3.2.blending.frag => blending.frag} (100%) rename tutorial/src/main/assets/shaders/blending/{3.2.blending.vert => blending.vert} (100%) diff --git a/tutorial/src/main/assets/shaders/blending/3.2.blending.frag b/tutorial/src/main/assets/shaders/blending/blending.frag similarity index 100% rename from tutorial/src/main/assets/shaders/blending/3.2.blending.frag rename to tutorial/src/main/assets/shaders/blending/blending.frag diff --git a/tutorial/src/main/assets/shaders/blending/3.2.blending.vert b/tutorial/src/main/assets/shaders/blending/blending.vert similarity index 100% rename from tutorial/src/main/assets/shaders/blending/3.2.blending.vert rename to tutorial/src/main/assets/shaders/blending/blending.vert diff --git a/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.frag b/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.frag index 90f21f22..063ed10c 100644 --- a/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.frag +++ b/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.frag @@ -7,6 +7,6 @@ out vec4 FragColor; void main() { - float h = (Height + 16)/64.0f; + float h = (Height + 16.) / 64.0; FragColor = vec4(h, h, h, 1.0); } diff --git a/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tesc b/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tesc index 8faf5610..97672d4f 100644 --- a/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tesc +++ b/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tesc @@ -17,8 +17,8 @@ void main() { const int MIN_TESS_LEVEL = 4; const int MAX_TESS_LEVEL = 64; - const float MIN_DISTANCE = 20; - const float MAX_DISTANCE = 800; + const float MIN_DISTANCE = 20.; + const float MAX_DISTANCE = 800.; vec4 eyeSpacePos00 = view * model * gl_in[0].gl_Position; vec4 eyeSpacePos01 = view * model * gl_in[1].gl_Position; diff --git a/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tese b/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tese index c89312d8..03e23681 100644 --- a/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tese +++ b/tutorial/src/main/assets/shaders/tesselation_shaders/8.3.gpuheight.tese @@ -33,7 +33,7 @@ void main() vec4 uVec = p01 - p00; vec4 vVec = p10 - p00; - vec4 normal = normalize( vec4(cross(vVec.xyz, uVec.xyz), 0) ); + vec4 normal = normalize( vec4(cross(vVec.xyz, uVec.xyz), 0.) ); vec4 p0 = (p01 - p00) * u + p00; vec4 p1 = (p11 - p10) * u + p10; diff --git a/tutorial/src/main/cpp/basic/BlendingScene.cpp b/tutorial/src/main/cpp/basic/BlendingScene.cpp index 6b05fd39..02bcbe7a 100644 --- a/tutorial/src/main/cpp/basic/BlendingScene.cpp +++ b/tutorial/src/main/cpp/basic/BlendingScene.cpp @@ -22,79 +22,80 @@ void BlendingScene::init() { m_camera = new TargetCamera; // configure global opengl state // ----------------------------- + glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // build and compile our shader zprogram // ------------------------------------ - m_pShader = new Shader("shaders/blending/3.2.blending.vert", "shaders/blending/3.2.blending.frag"); + m_pShader = new Shader("shaders/blending/blending.vert", "shaders/blending/blending.frag"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ float cubeVertices[] = { - // positions // texture Coords - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, - - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f + // back face + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // bottom-left + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // bottom-left + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left + // front face + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // top-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left + // left face + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right + -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-left + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right + // right face + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left + // bottom face + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, // top-left + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right + // top face + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f // bottom-left }; float planeVertices[] = { - // positions // texture Coords - 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, - -5.0f, -0.5f, 5.0f, 0.0f, 0.0f, - -5.0f, -0.5f, -0.5f, 0.0f, 2.0f, + // positions // texture Coords + 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, + -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, + -5.0f, -0.5f, 5.0f, 0.0f, 0.0f, - 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, - -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, - 5.0f, -0.5f, -5.0f, 2.0f, 2.0f + 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, + 5.0f, -0.5f, -5.0f, 2.0f, 2.0f, + -5.0f, -0.5f, -5.0f, 0.0f, 2.0f }; float transparentVertices[] = { - // positions // texture Coords (swapped y coordinates because texture is flipped upside down) - 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, - 0.0f, -0.5f, 0.0f, 0.0f, 1.0f, - 1.0f, -0.5f, 0.0f, 1.0f, 1.0f, + // positions // texture Coords (swapped y coordinates because texture is flipped upside down) + -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.0f, 1.0f, 1.0f, - 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, - 1.0f, -0.5f, 0.0f, 1.0f, 1.0f, - 1.0f, 0.5f, 0.0f, 1.0f, 0.0f + -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.0f, 1.0f, 0.0f }; // cube VAO glGenVertexArrays(1, &m_cubeVAO); @@ -130,9 +131,9 @@ void BlendingScene::init() { // load textures // ------------- - m_cubeTexture = loadTexture("marble.jpg"); - m_floorTexture = loadTexture("metal.png"); - m_transparentTexture = loadTexture("window.png"); + m_cubeTexture = loadTexture("textures/marble.jpg"); + m_floorTexture = loadTexture("textures/metal.png"); + m_transparentTexture = loadTexture("textures/window.png"); // shader configuration // -------------------- @@ -147,7 +148,17 @@ void BlendingScene::resize(int width, int height) { void BlendingScene::draw() { m_camera->update(); -// sort the transparent windows before rendering + + if (blendOn) { + LOGI(__FILE_NAME__, "blend properties %x, %x, %x", srcFactor, dstFactor, blendEquationMode); + glEnable(GL_BLEND); + glBlendFunc(srcFactor, dstFactor); + glBlendEquation(blendEquationMode); + } else { + glDisable(GL_BLEND); + } + + // sort the transparent windows before rendering // --------------------------------------------- std::map sorted; for (unsigned int i = 0; i < m_windows.size(); i++) @@ -216,7 +227,6 @@ void BlendingScene::destroy() { } BlendingScene::~BlendingScene() { - } std::map BlendingScene::propertyEvent(std::map &map) { @@ -226,10 +236,78 @@ std::map BlendingScene::propertyEvent(std::map &event) { + if (auto it = event.find("on"); it != event.end()) { + if (it->second.type() == typeid(bool)) { + blendOn = std::any_cast(it->second); + } + } +} + +void BlendingScene::parseSrcFactorEvent(std::map &event) { + if (auto it = event.find("factor"); it != event.end()) { + if (it->second.type() == typeid(int)) { + const auto& val = std::any_cast(it->second); + const GLenum factors[] = { + GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, + GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, + GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA + }; + if (val >= 0 && val < sizeof(factors)/sizeof(factors[0])) { + srcFactor = factors[val]; + } + } + } +} + +void BlendingScene::parseDstFactorEvent(std::map &event) { + if (auto it = event.find("factor"); it != event.end()) { + if (it->second.type() == typeid(int)) { + const auto& val = std::any_cast(it->second); + const GLenum factors[] = { + GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, + GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, + GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA + }; + if (val >= 0 && val < sizeof(factors)/sizeof(factors[0])) { + dstFactor = factors[val]; + } + } + } +} + +void BlendingScene::parseBlendEquationEvent(std::map &event) { + if (auto it = event.find("mode"); it != event.end()) { + if (it->second.type() == typeid(int)) { + const auto& val = std::any_cast(it->second); + const GLenum modes[] = { + GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX + }; + if (val >= 0 && val < sizeof(modes)/sizeof(modes[0])) { + blendEquationMode = modes[val]; + } + } + } +} + void BlendingScene::parseTargetCameraEvent(std::map &event) { auto* targetCamera = dynamic_cast(m_camera); if (!targetCamera) return; @@ -247,8 +325,10 @@ void BlendingScene::parseTargetCameraEvent(std::map &even if (it->second.type() == typeid(std::vector)) { const auto& val = std::any_cast&>(it->second); if (val.size() >= 8) { - targetCamera->onDoubleTouching(glm::vec2(val[0], val[1]), glm::vec2(val[2], val[3]), - glm::vec2(val[4], val[5]), glm::vec2(val[6], val[7])); + targetCamera->onDoubleTouching(glm::vec2(val[0], val[1]), + glm::vec2(val[2], val[3]), + glm::vec2(val[4], val[5]), + glm::vec2(val[6], val[7])); } } } diff --git a/tutorial/src/main/cpp/basic/BlendingScene.h b/tutorial/src/main/cpp/basic/BlendingScene.h index b4646952..56037202 100644 --- a/tutorial/src/main/cpp/basic/BlendingScene.h +++ b/tutorial/src/main/cpp/basic/BlendingScene.h @@ -3,6 +3,8 @@ #include "TutorialScene.h" +#include + #include class Camera; @@ -41,7 +43,30 @@ public : std::vector m_windows; + //region Properties + // Switch if blend is on. + bool blendOn = true; + + // source blending factors. Value is one of GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, + // GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA. + // GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, and GL_ONE_MINUS_CONSTANT_ALPHA. + GLenum srcFactor = GL_SRC_ALPHA; + + // destination blending factors. Value is one of GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, + // GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA. + // GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, and GL_ONE_MINUS_CONSTANT_ALPHA. + GLenum dstFactor = GL_ONE_MINUS_SRC_ALPHA; + + // BlendEquation mode. + // Value is one of GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX + GLenum blendEquationMode = GL_FUNC_ADD; + //endregion + void parseTargetCameraEvent(std::map &event); + void parseBlendOnEvent(std::map &event); + void parseSrcFactorEvent(std::map &event); + void parseDstFactorEvent(std::map &event); + void parseBlendEquationEvent(std::map &event); }; #endif //LEARNGLES_BLENDINGSCENE_H diff --git a/tutorial/src/main/java/com/minininja/learngles/basic/BlendingActivity.kt b/tutorial/src/main/java/com/minininja/learngles/basic/BlendingActivity.kt index 7ed867a2..1222eefd 100644 --- a/tutorial/src/main/java/com/minininja/learngles/basic/BlendingActivity.kt +++ b/tutorial/src/main/java/com/minininja/learngles/basic/BlendingActivity.kt @@ -5,9 +5,16 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.layout.width import androidx.compose.material3.Checkbox +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -23,6 +30,25 @@ import com.minininja.learngles.NativeHelper class BlendingActivity : GLActivity() { private var active by mutableStateOf(true) + private var blendOn by mutableStateOf(true) + + private var srcFactorIndex by mutableStateOf(6) // GL_SRC_ALPHA + + private var dstFactorIndex by mutableStateOf(7) // GL_ONE_MINUS_SRC_ALPHA + + private var blendEquationIndex by mutableStateOf(0) // GL_FUNC_ADD + + private val blendingFactors = listOf( + "GL_ZERO", "GL_ONE", "GL_SRC_COLOR", "GL_ONE_MINUS_SRC_COLOR", + "GL_DST_COLOR", "GL_ONE_MINUS_DST_COLOR", "GL_SRC_ALPHA", "GL_ONE_MINUS_SRC_ALPHA", + "GL_DST_ALPHA", "GL_ONE_MINUS_DST_ALPHA", "GL_CONSTANT_COLOR", "GL_ONE_MINUS_CONSTANT_COLOR", + "GL_CONSTANT_ALPHA", "GL_ONE_MINUS_CONSTANT_ALPHA" + ) + + private val blendingEquations = listOf( + "GL_FUNC_ADD", "GL_FUNC_SUBTRACT", "GL_FUNC_REVERSE_SUBTRACT", "GL_MIN", "GL_MAX" + ) + override fun createTouchCallback(): Layer3DTouchCallback { return object : Layer3DTouchCallback { @@ -64,6 +90,7 @@ class BlendingActivity : GLActivity() { val event = mapOf("event_id" to "target_camera_touching_event", "reset" to 1) NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() Log.d("BlendingActivity", "Custom DoubleClick at: $event") } @@ -81,16 +108,121 @@ class BlendingActivity : GLActivity() { @Composable override fun ControlPanel() { ControlPanelContent( + title = "Target Camera Events", active = active, onActiveChange = { active = it }, ) + + ControlPanelContent( + title = "Blend On", + active = blendOn, + onActiveChange = { + blendOn = it + val event = mapOf("event_id" to "blend_on_event", "on" to it) + glSurfaceView?.queueEvent { + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } + }, + ) + + SelectorControlPanelContent( + title = "Source Factor", + options = blendingFactors, + selectedIndex = srcFactorIndex, + onOptionSelected = { index -> + srcFactorIndex = index + val event = mapOf("event_id" to "src_factor_event", "factor" to index) + glSurfaceView?.queueEvent { + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } + } + ) + + SelectorControlPanelContent( + title = "Destination Factor", + options = blendingFactors, + selectedIndex = dstFactorIndex, + onOptionSelected = { index -> + dstFactorIndex = index + val event = mapOf("event_id" to "dst_factor_event", "factor" to index) + glSurfaceView?.queueEvent { + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } + } + ) + + SelectorControlPanelContent( + title = "Blend Equation", + options = blendingEquations, + selectedIndex = blendEquationIndex, + onOptionSelected = { index -> + blendEquationIndex = index + val event = mapOf("event_id" to "blend_equation_event", "mode" to index) + glSurfaceView?.queueEvent { + NativeHelper.sendCommands(event) + glSurfaceView?.requestRender() + } + } + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun SelectorControlPanelContent( + title: String, + options: List, + selectedIndex: Int, + onOptionSelected: (Int) -> Unit, + modifier: Modifier = Modifier, +) { + var expanded by remember { mutableStateOf(false) } + + Column( + modifier = modifier + .padding(8.dp) + .wrapContentSize() + ) { + Text(text = title, color = Color.White, modifier = Modifier.padding(bottom = 4.dp)) + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded }, + modifier = Modifier.width(200.dp) + ) { + TextField( + value = options[selectedIndex], + onValueChange = {}, + readOnly = true, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + modifier = Modifier.menuAnchor() + ) + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + options.forEachIndexed { index, selectionOption -> + DropdownMenuItem( + text = { Text(text = selectionOption) }, + onClick = { + onOptionSelected(index) + expanded = false + } + ) + } + } + } } } @Composable private fun ControlPanelContent( + title: String, active: Boolean, onActiveChange: (Boolean) -> Unit, modifier: Modifier = Modifier, @@ -100,7 +232,7 @@ private fun ControlPanelContent( ) { Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier.padding(8.dp)) { - Text(text = "Target Camera Events", color = Color.White) + Text(text = title, color = Color.White) Checkbox( checked = active, onCheckedChange = onActiveChange From 005e3302fd6de3f2cb22c0acaed754136ba4ab6f Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Sun, 31 May 2026 22:31:53 +0800 Subject: [PATCH 4/4] Rename shader files to remove numeric prefixes Simplified shader filenames for CubemapScene and GeometryShaderScene by removing numeric prefixes and updated the corresponding C++ source files to use the new paths. --- .../shaders/cubemaps/{6.1.cubemaps.frag => cubemaps.frag} | 0 .../shaders/cubemaps/{6.1.cubemaps.vert => cubemaps.vert} | 0 .../shaders/cubemaps/{6.1.skybox.frag => skybox.frag} | 0 .../shaders/cubemaps/{6.1.skybox.vert => skybox.vert} | 0 .../{9.1.geometry_shader.frag => geometry_shader.frag} | 0 .../{9.1.geometry_shader.geom => geometry_shader.geom} | 0 .../{9.1.geometry_shader.vert => geometry_shader.vert} | 0 tutorial/src/main/cpp/basic/CubemapScene.cpp | 4 ++-- tutorial/src/main/cpp/basic/GeometryShaderScene.cpp | 6 +++--- 9 files changed, 5 insertions(+), 5 deletions(-) rename tutorial/src/main/assets/shaders/cubemaps/{6.1.cubemaps.frag => cubemaps.frag} (100%) rename tutorial/src/main/assets/shaders/cubemaps/{6.1.cubemaps.vert => cubemaps.vert} (100%) rename tutorial/src/main/assets/shaders/cubemaps/{6.1.skybox.frag => skybox.frag} (100%) rename tutorial/src/main/assets/shaders/cubemaps/{6.1.skybox.vert => skybox.vert} (100%) rename tutorial/src/main/assets/shaders/geometry_shader/{9.1.geometry_shader.frag => geometry_shader.frag} (100%) rename tutorial/src/main/assets/shaders/geometry_shader/{9.1.geometry_shader.geom => geometry_shader.geom} (100%) rename tutorial/src/main/assets/shaders/geometry_shader/{9.1.geometry_shader.vert => geometry_shader.vert} (100%) diff --git a/tutorial/src/main/assets/shaders/cubemaps/6.1.cubemaps.frag b/tutorial/src/main/assets/shaders/cubemaps/cubemaps.frag similarity index 100% rename from tutorial/src/main/assets/shaders/cubemaps/6.1.cubemaps.frag rename to tutorial/src/main/assets/shaders/cubemaps/cubemaps.frag diff --git a/tutorial/src/main/assets/shaders/cubemaps/6.1.cubemaps.vert b/tutorial/src/main/assets/shaders/cubemaps/cubemaps.vert similarity index 100% rename from tutorial/src/main/assets/shaders/cubemaps/6.1.cubemaps.vert rename to tutorial/src/main/assets/shaders/cubemaps/cubemaps.vert diff --git a/tutorial/src/main/assets/shaders/cubemaps/6.1.skybox.frag b/tutorial/src/main/assets/shaders/cubemaps/skybox.frag similarity index 100% rename from tutorial/src/main/assets/shaders/cubemaps/6.1.skybox.frag rename to tutorial/src/main/assets/shaders/cubemaps/skybox.frag diff --git a/tutorial/src/main/assets/shaders/cubemaps/6.1.skybox.vert b/tutorial/src/main/assets/shaders/cubemaps/skybox.vert similarity index 100% rename from tutorial/src/main/assets/shaders/cubemaps/6.1.skybox.vert rename to tutorial/src/main/assets/shaders/cubemaps/skybox.vert diff --git a/tutorial/src/main/assets/shaders/geometry_shader/9.1.geometry_shader.frag b/tutorial/src/main/assets/shaders/geometry_shader/geometry_shader.frag similarity index 100% rename from tutorial/src/main/assets/shaders/geometry_shader/9.1.geometry_shader.frag rename to tutorial/src/main/assets/shaders/geometry_shader/geometry_shader.frag diff --git a/tutorial/src/main/assets/shaders/geometry_shader/9.1.geometry_shader.geom b/tutorial/src/main/assets/shaders/geometry_shader/geometry_shader.geom similarity index 100% rename from tutorial/src/main/assets/shaders/geometry_shader/9.1.geometry_shader.geom rename to tutorial/src/main/assets/shaders/geometry_shader/geometry_shader.geom diff --git a/tutorial/src/main/assets/shaders/geometry_shader/9.1.geometry_shader.vert b/tutorial/src/main/assets/shaders/geometry_shader/geometry_shader.vert similarity index 100% rename from tutorial/src/main/assets/shaders/geometry_shader/9.1.geometry_shader.vert rename to tutorial/src/main/assets/shaders/geometry_shader/geometry_shader.vert diff --git a/tutorial/src/main/cpp/basic/CubemapScene.cpp b/tutorial/src/main/cpp/basic/CubemapScene.cpp index 06ce5d11..020e136c 100644 --- a/tutorial/src/main/cpp/basic/CubemapScene.cpp +++ b/tutorial/src/main/cpp/basic/CubemapScene.cpp @@ -20,8 +20,8 @@ void CubemapScene::init() { // build and compile shaders // ------------------------- - m_pShader = new Shader("shaders/cubemaps/6.1.cubemaps.vert", "shaders/cubemaps/6.1.cubemaps.frag"); - m_pSkyboxShader = new Shader("shaders/cubemaps/6.1.skybox.vert", "shaders/cubemaps/6.1.skybox.frag"); + m_pShader = new Shader("shaders/cubemaps/cubemaps.vert", "shaders/cubemaps/cubemaps.frag"); + m_pSkyboxShader = new Shader("shaders/cubemaps/skybox.vert", "shaders/cubemaps/skybox.frag"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ diff --git a/tutorial/src/main/cpp/basic/GeometryShaderScene.cpp b/tutorial/src/main/cpp/basic/GeometryShaderScene.cpp index f9e86c99..11adb19a 100644 --- a/tutorial/src/main/cpp/basic/GeometryShaderScene.cpp +++ b/tutorial/src/main/cpp/basic/GeometryShaderScene.cpp @@ -18,9 +18,9 @@ void GeometryShaderScene::init() { // build and compile shaders // ------------------------- - m_pShader = new Shader("shaders/geometry_shader/9.1.geometry_shader.vert", - "shaders/geometry_shader/9.1.geometry_shader.frag", - "shaders/geometry_shader/9.1.geometry_shader.geom"); + m_pShader = new Shader("shaders/geometry_shader/geometry_shader.vert", + "shaders/geometry_shader/geometry_shader.frag", + "shaders/geometry_shader/geometry_shader.geom"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------