--- a/plugins/cube/cube.cpp +++ b/plugins/cube/cube.cpp @@ -332,16 +332,14 @@ this->cube = cube; auto grid = cube->output->wset()->get_workspace_grid_size(); auto current = cube->output->wset()->get_current_workspace(); - int count = cube->vertical_transition ? grid.height : grid.width; + // A vertical move translates complete Cube rows, including their + // side and rear faces. Keep textures for every row so fast steps + // and reversal preserve the visible position. + int count = cube->vertical_transition ? grid.height * grid.width : grid.width; for (int i = 0; i < count; i++) { wf::point_t workspace = cube->vertical_transition ? - wf::point_t{current.x, i} : wf::point_t{i, current.y}; - if (cube->vertical_transition && cube->workspace_animation_anchor && - (i == cube->workspace_animation_anchor->y)) - { - workspace.x = cube->workspace_animation_anchor->x; - } + wf::point_t{i % grid.width, i / grid.width} : wf::point_t{i, current.y}; auto node = std::make_shared( cube->output, workspace); @@ -1209,13 +1207,18 @@ return wf::gles::render_target_gl_to_framebuffer(target) * scale; } - glm::mat4 calculate_vp_matrix(const wf::render_target_t& dest) + glm::mat4 calculate_vp_matrix(const wf::render_target_t& dest, float row_offset = 0.0f) { float zoom_factor = animation.cube_animation.zoom; auto scale_matrix = glm::scale(glm::mat4(1.0), glm::vec3(1. / zoom_factor, 1. / zoom_factor, 1. / zoom_factor)); - return output_transform(dest) * animation.projection * animation.view * scale_matrix; + // Translate the complete projected row by its viewport height. Doing + // this before the output transform keeps side/rear content moving at + // the same speed as the front, including rotated/scaled outputs. + auto row_translation = glm::translate(glm::mat4(1.0), + glm::vec3(0, 2 * row_offset, 0)); + return output_transform(dest) * row_translation * animation.projection * animation.view * scale_matrix; } /* Calculate the base model matrix for the i-th side of the cube */ @@ -1242,78 +1245,52 @@ } /* Render the sides of the cube, using the given culling mode - cw or ccw */ - void render_cube(GLuint front_face, std::vector& buffers) + void render_cube(GLuint front_face, std::vector& buffers, + const wf::render_target_t& target) { GL_CALL(glFrontFace(front_face)); static const GLuint indexData[] = {0, 1, 2, 0, 2, 3}; auto cws = output->wset()->get_current_workspace(); - const int count = get_num_faces(); - for (int i = 0; i < count; i++) - { - int index = (cws.x + i) % get_num_faces(); - GL_CALL(glBindTexture(GL_TEXTURE_2D, wf::gles_texture_t::from_aux(buffers[index]).tex_id)); - program.uniformMatrix4f("model", calculate_model_matrix(i)); - - if (tessellation_support) - { + const int faces = get_num_faces(); + const int rows = vertical_transition ? (int)buffers.size() / faces : 1; + for (int row = 0; row < rows; row++) + { + // Move every face of a row together without changing its yaw, + // orientation, or perspective, including through the final frame. + const float offset = vertical_transition ? + row - cws.y + float(row_translation) : 0.0f; + program.uniformMatrix4f("VP", calculate_vp_matrix(target, offset)); + for (int i = 0; i < faces; i++) + { + int index = row * faces + (cws.x + i) % faces; + GL_CALL(glBindTexture(GL_TEXTURE_2D, wf::gles_texture_t::from_aux(buffers[index]).tex_id)); + program.uniformMatrix4f("model", calculate_model_matrix(i)); + + if (tessellation_support) + { #ifdef USE_GLES32 - GL_CALL(glDrawElements(GL_PATCHES, 6, GL_UNSIGNED_INT, &indexData)); + GL_CALL(glDrawElements(GL_PATCHES, 6, GL_UNSIGNED_INT, &indexData)); #endif - } else - { - GL_CALL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, - &indexData)); - } - } - } - - void render_vertical_rows(const wf::scene::render_instruction_t& data, - std::vector& buffers) - { - const auto size = wf::dimensions(output->get_relative_geometry()); - const int current_row = output->wset()->get_current_workspace().y; - // Round the shared offset once so adjacent rows meet without gaps. - const int offset = std::lround(float(row_translation) * size.height); + } else + { + GL_CALL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, + &indexData)); + } + } + } + } + + void render(const wf::scene::render_instruction_t& data, std::vector& buffers) + { data.pass->custom_gles_subpass([&] { - wf::gles::for_each_scissor_rect(data.target, data.damage, [&] - { - for (int i = 0; i < (int)buffers.size(); i++) - { - const int y = (i - current_row) * size.height + offset; - if ((y >= size.height) || (y + size.height <= 0)) - { - continue; - } - - // Translate full-size workspace textures. No pitch, face - // rotation, perspective scaling, or row wrapping. - OpenGL::render_texture(wf::gles_texture_t::from_aux(buffers[i]), - data.target, {0.0, double(y), double(size.width), double(size.height)}); - } - }); - }); - } - - void render(const wf::scene::render_instruction_t& data, std::vector& buffers) - { - if (vertical_transition) - { - render_vertical_rows(data, buffers); - return; - } - - data.pass->custom_gles_subpass([&] - { if (program.get_program_id(wf::TEXTURE_TYPE_RGBA) == 0) { load_program(); } /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); program.use(wf::TEXTURE_TYPE_RGBA); GL_CALL(glEnable(GL_DEPTH_TEST)); @@ -1335,7 +1312,6 @@ program.attrib_pointer("position", 2, 0, vertexData); program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); program.uniform1f("cube_brightness", 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); if (tessellation_support) @@ -1359,8 +1335,8 @@ wf::gles::for_each_scissor_rect(data.target, data.damage, [&] { GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); + render_cube(GL_CCW, buffers, data.target); + render_cube(GL_CW, buffers, data.target); }); GL_CALL(glDisable(GL_CULL_FACE)); GL_CALL(glDisable(GL_BLEND));