[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Normalize line endings for all files that Git considers text files.\n* text=auto eol=lf\n\n# Only include the addons folder when downloading from the Asset Library.\n/**        export-ignore\n/transition.gdshader    !export-ignore"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: cashewolddew\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\nthanks_dev: # Replace with a single thanks.dev username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".gitignore",
    "content": "# Godot 4+ specific ignores\n.godot/\n/android/\n"
  },
  {
    "path": "LICENSE",
    "content": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n    INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n    HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n  i. the right to reproduce, adapt, distribute, perform, display,\n     communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n     likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n     subject to the limitations in paragraph 4(a), below;\n  v. rights protecting the extraction, dissemination, use and reuse of data\n     in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n     European Parliament and of the Council of 11 March 1996 on the legal\n     protection of databases, and under any national implementation\n     thereof, including any amended or successor version of such\n     directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n     world based on applicable law or treaty, and any national\n     implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n    surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n    warranties of any kind concerning the Work, express, implied,\n    statutory or otherwise, including without limitation warranties of\n    title, merchantability, fitness for a particular purpose, non\n    infringement, or the absence of latent or other defects, accuracy, or\n    the present or absence of errors, whether or not discoverable, all to\n    the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n    that may apply to the Work or any use thereof, including without\n    limitation any person's Copyright and Related Rights in the Work.\n    Further, Affirmer disclaims responsibility for obtaining any necessary\n    consents, permissions or other rights required for any use of the\n    Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n    party to this document and has no duty or obligation with respect to\n    this CC0 or use of the Work."
  },
  {
    "path": "README.md",
    "content": "\n<h1>\n\t<img src=\"https://github.com/user-attachments/assets/0a6cbb58-0594-414d-aa70-d1a90997e7a8\" width=\"48\" align=\"center\"/>\n\tUniversal Transition Shader\n\t<img src=\"https://github.com/user-attachments/assets/0a6cbb58-0594-414d-aa70-d1a90997e7a8\" width=\"48\" align=\"center\"/>\n</h1>\n\nMost common transitions inside one Godot shader. 🎬\n\n## Introduction\n\n_Hint:_ You can watch a brief guide video of the shader on my [youtube channel](https://www.youtube.com/watch?v=PtBZs7OvR2Y), or see it on [Godot Shaders](https://godotshaders.com/shader/universal-transition-shader/?post_id=11966)\n\nThis shader provides a flexible and highly customizable system for creating animated transitions between visual elements in a Godot project. It supports a wide variety of transition styles, such as:\n\n- **Directional Wipes**: (left, right, top, bottom, diagonal)\n- **Clock Wipes**: (radial, with multi-sector support)\n- **Iris/Shape Reveals**: (polygon-based transitions with any number of sides)\n- **Dissolves**\n- **Fades**\n- **Slides**\n- **Combinations & Variations**\n\nTo get started quickly, jump to the [quickstart](#quick-start) or [common transition recipes](#common-transition-recipes) sections.\n\nFor in depth documentation, check the [parameters reference](#parameters-reference) section.\n\n## Quick Start\n\n1. Attach the shader to any `CanvasItem` (like a `Sprite2D` or `TextureRect`).\n- _Alternatively, you can clone this repository and open the included demo project for a ready-to-use example._\n2. In the Godot Inspector, navigate to:\n**Material → Shader → Animation**\n3. Adjust the `progress` value to trigger a transition.\n4. Explore other parameters to try different transition styles and effects.\n\n## Common Transition Recipes\n\n**Let's cook!** 🍜\n\n_Hint: Each recipe contains only the most basic 'ingredients', without which the recipe wouldn't work. Feel free to add any additional parameters to get variations of these recipes._\n\n**Simple Fade**\n- `transition_type`: Basic\n- `grid_size`: (0.0, 0.0)\n- `basic_feather`: Any value > 0.0\n\n<img src=\"https://github.com/user-attachments/assets/765b83b8-2d29-4e20-8704-872bb41d90bb\" width=\"100\" />\n\n**Directional Wipe**\n- `transition_type`: Basic\n- `grid_size`: (1.0, 0.0) or (0.0, 1.0) or (-1.0, 0.0) or (0.0, -1.0)\n\n<img src=\"https://github.com/user-attachments/assets/ae0b2c5e-2be3-48fc-99b8-344aae1a30db\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/3114a42b-805d-4e13-aedd-47e077bd45a9\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/1c517e71-9930-40a4-acb9-73ed1bf4a7e4\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/6597ab7a-e1fb-4c78-aa36-5e5aec883546\" width=\"80\" />\n\n**Corner Wipe**\n- `transition_type`: Basic\n- `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0)\n\n<img src=\"https://github.com/user-attachments/assets/b4870810-99a1-48ae-916a-dc40585dd9e2\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/88c8bb1d-0746-4017-9962-7b3f5cdb05b8\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/741ae9ec-ffda-49f9-8729-87a14cb07d7e\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/255603bb-79e6-494a-95b9-f9cc5f1b7af1\" width=\"80\" />\n\n**Diagonal Wipe**\n- `transition_type`: Basic\n- `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0)\n- `rotation_angle`: 45.0\n\n<img src=\"https://github.com/user-attachments/assets/33281653-ccc1-4b17-9292-165b6317b414\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/37425d76-5a0d-4272-aa80-4674f1960663\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/427df89b-9f5f-42de-95aa-57e0c0a62938\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/59824309-91b8-4df4-a780-517b0bf79deb\" width=\"80\" />\n\n**Center Wipe**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n\n<img src=\"https://github.com/user-attachments/assets/79827e31-7be0-454d-bcf3-cfb5a43a3626\" width=\"100\" />\n\n**Blinder Wipe**\n- `transition_type`: Basic\n- `grid_size`: (0.0, `abs(y) > 2.0`) or (`abs(x) > 2.0`, 0.0)\n\n<img src=\"https://github.com/user-attachments/assets/6fd5d274-379e-40f1-8c74-d9221bc521f6\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/f5e3434b-b2d6-40ba-bd1b-344a04d24dfe\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/065f3022-d058-4007-a2d3-29c6b5b78dba\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/8d530969-59c1-4a2d-b118-9d2dfa67a0a8\" width=\"80\" />\n\n**Grid Reveal**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n- `grid_size`: (10.0, 10.0) - or any abs(x) > 0.0; abs(y) > 0.0;\n\n<img src=\"https://github.com/user-attachments/assets/a4b460e9-c43d-4d57-b656-631e5ea0f4e6\" width=\"100\" />\n\n**Staggered Grid Reveal**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n- `grid_size`: (10.0, 10.0) - or any abs(x) > 0.0; abs(y) > 0.0;\n- `stagger`: (1.0, 0.0) or (0.0, 1.0)\n\n<img src=\"https://github.com/user-attachments/assets/8da31227-416d-41f3-9119-ebbe4e04c84f\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/3da53446-4fa8-4930-bc88-0cda908586a3\" width=\"80\" />\n\n**Mixed Stagger Reveal**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n- `grid_size`: (5.0, 5.0)\n- `stagger`: (1.0, 1.0)\n\n<img src=\"https://github.com/user-attachments/assets/25a972cf-da15-432c-80d7-8e53a8a8d222\" width=\"100\" />\n\n**Cross-shaped Transition (All corners wipe)**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n- `stagger`: (1.0, 1.0)\n\n<img src=\"https://github.com/user-attachments/assets/24819aeb-16a8-40f2-bfd3-e3434fd9d7a7\" width=\"100\" />\n\n**Diagonal Popping Squares**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n- `grid_size`: (5.0, 5.0)\n- `progress_bias`: (10.0, 10.0)\n\n<img src=\"https://github.com/user-attachments/assets/c6d7a904-bd1f-454b-ba73-6c9c463b7f52\" width=\"100\" />\n\n**Step Wipe**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n- `grid_size`: (5.0, 0.0) or (0.0, 5.0)\n- `progress_bias`: (5.0, 0.0) or (0.0, 5.0)\n\n<img src=\"https://github.com/user-attachments/assets/1dcf66f8-970d-4d98-88b3-5df1f2c43a76\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/21ffe263-1385-4de4-8099-2637cb0b3dda\" width=\"80\" />\n\n**Mask Reveal**\n- `transition_type`: Mask\n- `position`: (0.5, 0.5)\n- `mask_texture`: Any black & white texture\n\n<img src=\"https://github.com/user-attachments/assets/645de630-ee98-428e-8b0d-221f6e57446e\" width=\"100\" />\n\n**Alternating Mask Grid**\n- `transition_type`: Mask\n- `position`: (0.5, 0.5)\n- `grid_size`: (5.0, 5.0)\n- `mask_texture`: Any black & white texture\n- `flip_frequency`: (1.0, 2.0)\n\n<img src=\"https://github.com/user-attachments/assets/2d59c7c2-d16e-4c3b-bce4-6b556aaea587\" width=\"100\" />\n\n**Iris Transition**\n- `transition_type`: Shape\n- `position`: (0.5, 0.5)\n- `edges`: 64\n- `shape_feather`: 0.1\n\n<img src=\"https://github.com/user-attachments/assets/0a6cbb58-0594-414d-aa70-d1a90997e7a8\" width=\"100\" />\n\n**Spike Transition**\n- `transition_type`: Shape\n- `position`: (0.5, 0.5)\n- `edges`: 3\n- `grid_size` & `rotation_angle`: `(0.5, y) & 0.0` or `(x, -0.5) & 90.0`\n\n<img src=\"https://github.com/user-attachments/assets/fd6e2d66-f820-469d-bdde-5cb173183eec\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/a6e8f21f-8fe1-49ca-b78f-9b11aef2aa6c\" width=\"80\" />\n\n**Scratch Lines Reveal**\n- `transition_type`: Shape\n- `position`: (0.5, 0.5)\n- `grid_size`: (50.0, 5.0)\n- `edges`: 3\n- `flip_frequency`: (2.0, 1.0)\n\n<img src=\"https://github.com/user-attachments/assets/74211200-a4e2-42b1-89ee-89c395423ac2\" width=\"100\" />\n\n**Overlapping Diamonds**\n- `transition_type`: Shape\n- `position`: (0.5, 0.5)\n- `grid_size`: (0.5, 50.0) or (50.0, 0.5)\n- `edges`: 3\n- `shape_feather`: 0.0\n\n<img src=\"https://github.com/user-attachments/assets/f7e1d688-7d8d-421c-925b-0ac317fe7e4d\" width=\"100\" />\n\n**Corner-Clock Transition**\n- `transition_type`: Clock\n- `invert`: true\n- `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0)\n\n<img src=\"https://github.com/user-attachments/assets/6c5c0c29-2e99-4672-b339-558108412872\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/856b1121-10ba-4d67-ac61-50d6c7a36006\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/60928ab7-b22e-4e3d-ab9f-92583b1178b8\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/f60b90a5-4a09-42bb-b440-def953b0abef\" width=\"80\" />\n\n**Center-Clock Transition**\n- `transition_type`: Clock\n- `invert`: true\n- `position`: (0.5, 0.5)\n- `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0)\n\n<img src=\"https://github.com/user-attachments/assets/bdc012f3-7519-4f3c-bb38-098058a9b737\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/8a717281-f404-4a8e-8b57-61ce9cff12f8\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/75e1b7cb-a863-4432-a8a8-26769ad00cc4\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/03d96d53-f3c3-490d-a868-b18d149786e1\" width=\"80\" />\n\n**Fan Transition**\n- `transition_type`: Clock\n- `invert`: true\n- `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0)\n- `sectors`: 2 or more\n\n<img src=\"https://github.com/user-attachments/assets/471f858f-5186-4f72-ad3d-c546294be25f\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/4b8ec7f4-1665-4fdd-ab89-14d4c47212c8\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/beee2f69-e70b-4785-a883-5ea12b00d722\" width=\"80\" />\n<img src=\"https://github.com/user-attachments/assets/328e20d7-4631-4ed2-a841-d3d70b4e68f9\" width=\"80\" />\n\n**Seamless Striped Flower Transition**\n- `transition_type`: Clock\n- `invert`: true\n- `grid_size`: (5.0, 5.0)\n- `flip_frequency`: (2, 2)\n- `sectors`: 16\n\n<img src=\"https://github.com/user-attachments/assets/723764e1-f130-4a73-aafb-21f94b1d67fc\" width=\"100\" />\n\n**Hourglass Wipe Transition - by tomsterBG**\n- `transition_type`: Clock\n- `position`: (0.5, 0.5)\n- `stagger`: (1.0, 1.0)\n- `flip_frequency`: (2.0, 2.0)\n- `sectors`: 2\n\n<img src=\"assets/recipes/hourglass.gif\" width=\"100\" />\n\n**Cumulative Stagger Transition - by tomsterBG**\n- `transition_type`: Basic\n- `position`: (0.5, 0.5)\n- `grid_size`: (4.0, 6.0)\n- `stagger_frequency`: (1.0, 1.0)\n- `cumulative_stagger`: _Animated_ alongside progress from (1.0, 0.0) to (3.0, 0.0)\n\n<img src=\"assets/recipes/cumulative_stagger.gif\" width=\"100\" />\n\n**Double Diamond Transition**\n- `transition_type`: Clock\n- `position`: (0.5, 0.5)\n- `grid_size`: (2.0, 2.0)\n- `local_x_mirror`: true\n- `local_y_mirror`: true\n- `sectors`: 4\n\n<img src=\"assets/recipes/double_diamond.gif\" width=\"100\" />\n\n**Spike Trap Transition**\n- `transition_type`: Shape\n- `position`: (0.0, 1.0)\n- `grid_size`: (1.0, 3.0)\n- `rotation_angle`: 30.0\n- `global_x_mirror`: true\n- `local_y_mirror`: true\n- `edges`: 3\n\n<img src=\"assets/recipes/spike_trap.gif\" width=\"100\" />\n\n## Parameters Reference\n\nThe shader parameters are organized into the following categories:\n- **[Shader Parameters](#shader-parameters)**: Generic shader settings that apply to the overall behavior of the transition, such as the `Transition Type`\n- **[Positioning](#positioning)**: Used for positioning and orientation of the transition, applying to all transition types\n\t- **[Mirror](#mirror)**: Control the local and global mirroring around the center.\n\t- **[Stagger](#stagger)**: Handle offset of grid rows and columns, allowing for more varied grid patterns.\n- **[Basic Transition Controls](#basic-transition-controls)**: Parameters specific to the \"Basic\" transition type.\n- **[Mask Transition Controls](#mask-transition-controls)**: Parameters specific to the \"Mask\" transition type.\n- **[Shape Transition Controls](#shape-transition-controls)**: Parameters specific to the \"Shape\" transition type.\n- **[Clock Transition Controls](#clock-transition-controls)**: Parameters specific to the \"Clock\" transition type.\n- **[Animation](#animation)**: Control the progression of the animation.\n\n### Shader Parameters\n\n---\n\n`use_sprite_alpha` (`bool`, default: `true`)\n\nWhen `false`, the `CanvasItem`'s original alpha is ignored.\n\nWhen `true`, the final pixel's transparency will be the minimum of the sprite's original alpha and the transition's calculated alpha. This ensures that already-transparent parts of your sprite remain transparent throughout the transition.\n\n---\n\n`use_transition_texture` (`bool`, default: `false`)\n\nWhen `false`, the shader blends from the original `COLOR` of the `CanvasItem` to a fully transparent `COLOR`, effectively revealing what's behind the `CanvasItem`.\n\nWhen `true`, the shader blends from the original `COLOR` of the `CanvasItem` to the `COLOR`of the texture provided under the `transition_texture` parameter.\n\n_Hint: If `transition_texture` is not set, the shader blends between `COLOR` and white._\n\n---\n\n`transition_texture` (`sampler2D`)\n\nThe texture that will be transitioned to when `use_transition_texture` is set to `true`.\n\n_Hint: Has no effect if `use_transition_texture` is `false`._\n\n---\n\n`transition_type` (`int`, hint: `enum(\"Basic\", \"Mask\", \"Shape\", \"Clock\")`, default: `0` (Basic))\n\nSelects the fundamental algorithm or visual style for the transition effect.\n\n- `0` (Basic): Implements simple, rectangular, gradient-based wipes and fades.\n- `1` (Mask): Implements mask-based transitions.\n- `2` (Shape): Generates a regular convex polygon as the transition boundary.\n- `3` (Clock): Creates a radial, clock-like wipe effect.\n\n_Hint: Adjustments to parameters within a specific \"Transition Controls\" group (e.g. \"Basic Transition Controls\") will only affect the transition if the corresponding `transition_type` is selected._\n\n---\n\n### Positioning\n\nThese parameters influence the spatial origin, direction, and tiling of the transition, and they apply universally across all `transition_types`.\n\n---\n\n`invert` (`bool`, default: `false`)\n\nReverses the logical direction of the transition's progression. For example, a transition that normally expands outwards would contract, or a wipe from left to right would become right to left.\n\n---\n\n`position` (`vec2`, default: `(0.0, 0,0)`)\n\nSpecifies the position in each grid cell (see `grid_size` parameter) from where the transition starts.\n- `(0.0, 0.0)` - top left corner\n- `(1.0, 0.0)` - top right corner\n- `(0,0, 1.0)` - bottom left corner\n- `(1.0, 1.0)` - bottom right corner\n- `(0.5, 0.5)` - center\n\n---\n\n`grid_size` (`vec2`, default: `(1.0, 1.0)`)\n\nDivides the `CanvasItem`'s area into a grid of independently animating cells. The `x` component defines the number of horizontal divisions, and the `y` component defines the number of vertical divisions. A value of `(1.0, 1.0)` means no division, treating the entire `CanvasItem` as a single cell.\n\nUsing **negative values**, **zero values**, or **floating-point** numbers for the `grid_size` introduces interesting and powerful behaviors. \n\nFor instance, `(-1.0, 0.0)` will create a right-to-left wipe. Values like `(0.0, 0.0)` can even produce a simple fade, given the transition has a blur.\n\nExperiment to find more special `grid_size` values or check out the [common transition recipes](#common-transition-recipes) section for more examples.\n\n---\n\n`rotation_angle` (`float`, default: `0.0`)\n\nRotates the local coordinate system within each grid cell (or the entire `CanvasItem` if `grid_size` is `(1.0, 1.0)`). The angle is specified in degrees.\n\n\n#### Mirror\n\nThese parameters introduce mirroring of the transition effect around the center.\n\n---\n\n`global_x_mirror` (`bool`, default: `false`)\n\nApplies horizontal mirroring effect to the _entire transition_.\n\n`global_y_mirror` (`bool`, default: `false`)\n\nApplies vertical mirroring effect to the _entire transition_.\n\n---\n\n`local_x_mirror` (`bool`, default: `false`)\n\nApplies horizontal mirroring effect to the transition _within each grid cell_.\n\n`local_y_mirror` (`bool`, default: `false`)\n\nApplies vertical mirroring effect to the transition _within each grid cell_.\n\n#### Stagger\n\nThese parameters introduce offsets and flipping to individual grid cells, creating more complex and dynamic grid-based patterns.\n\n---\n\n`stagger` (`vec2`, default `(0.0, 0.0)`)\n\nApplies a fractional offset to the UV coordinates of alternating grid rows and columns before the transition calculation. This can create a \"checkerboard\" or \"wave\" effect when combined with a `grid_size` greater than `(1.0, 1.0)`.\n\n_Hint 1: When `grid_size` is `(1.0, 1.0)` or smaller, this just offsets the transition location._\n\n_Hint 2: Due to each element having limited space in its cell, applying a stagger on both axis can lead to interesting patterns._\n\n---\n\n`cumulative_stagger_flip` (`bool`, default `false`)\n\nEnabling this allows the `cumulative_stagger` to start from the last row or column.\n\n---\n\n`cumulative_stagger` (`vec2`, default `(0.0, 0.0)`)\n\nAdds an offset between staggered rows or columns. The stagger amount of each row/column will be calculated based on its index.\n\n---\n\n`stagger_frequency` (`ivec2`, default: `(2, 2)`)\n\nDetermines how often the stagger offset is applied to grid cells.\n\n- `x`: Controls the frequency for rows (every `x` rows are staggered).\n- `y`: Controls the frequency for columns (every `y` columns are staggered).\n\nFor example, `(1, 0)` staggers every single row, while `(2, 0)` staggers every second row.\n\n---\n\n`flip_frequency` (`ivec2`, default: `(1, 1)`)\n\nControls the frequency at which the local UV coordinates within grid cells are flipped (mirrored).\n\n- `x`: Flips horizontally every `x` rows.\n- `y`: Flips vertically every `y` columns.\n\nThis introduces visual variation and can create interesting symmetrical or asymmetrical patterns within a grid.\n\n---\n\n### Basic Transition Controls\n\nThese parameters are exclusively relevant when `transition_type` is set to `0` (Basic).\n\n---\n\n`basic_feather` (`float`)\n\nControls the softness or blurriness of the transition's edges.\n\n---\n\n### Mask Transition Controls\n\nThese parameters are exclusively relevant when `transition_type` is set to `1` (Mask).\n\n---\n\n`mask_texture` (`sampler2D`)\n\nThe grayscale image used as a mask for the transition. The white part of the mask shows the current texture and the black part shows the texture (or alpha) transitioned to.\n\n---\n\n`use_mask_size` (`bool`)\n\nWhen `false` the mask texture keeps the aspect ratio of the CanvasItem.\nWhen `true` the mask texture keeps the aspect ratio of the `mask_size` parameter. For example, if `mask_size` equals `(50, 25)`, the mask aspect ratio will stick to `2:1`\n\n_Hint: This is experimental and works best for grids containing a single element. Currently, for larger grids, this introduces horizontal/vertical artifacts because of `fwidth`'s nature._\n\n---\n\n`mask_size` (`vec2`)\n\nIf `use_mask_size` is `true`, the `mask_texture`'s aspect ratio will be determined by `mask_size.x`:`mask_size.y`.\n\n---\n\n### Shape Transition Controls\n\nThese parameters are exclusively relevant when `transition_type` is set to `2` (Shape).\n\n---\n\n`edges` (`int`, hint: `range(3, 64)`, default: `6`)\n\nSpecifies the number of sides (edges) of the regular polygon shape that forms the transition boundary. A value of `3` creates a triangle, `4` a square (or diamond if rotated), `5` a pentagon, and so on. Higher values approximate a circle.\n\n---\n\n`shape_feather` (`float`, hint: `range(0.0, 10.0)`, default: `0.1`)\n\nControls the softness or blurriness of the polygon's edges. A value of `0.0` results in a perfectly sharp polygon outline, while increasing the value expands the feathered transition zone, creating a softer blend.\n\n---\n\n### Clock Transition Controls\n\nThese parameters are exclusively relevant when `transition_type` is set to `3` (Clock).\n\n---\n\n`sectors` (`int`, hint: `range(1, 128)`, default: `1`)\n\nDetermines how many radial segments (sectors) the clock-wipe effect is divided into.\n\n---\n\n`clock_feather` (`float`, hint: `range(0.0, 16.0)`, default `0.0`)\n\nControls the amount of feathering or blur applied to the edges of the individual clock sectors. A `0.0` value produces sharp, distinct sector edges, while higher values create a smoother transition.\n\n---\n\n### Animation\n\nThese parameters are the backbone of this shader as they control the timing of the transition.\n\n---\n\n`progress` (`float`, default: `0.0`)\n\nThe primary control for advancing the transition. It is the overall stage of the animation.\n\nTypically, `0.0` means the transition has not started, and `1.0` means the transition is complete. (or the other way around if `invert` is set to `true`)\n\n_Hint: Sometimes this is not the case. For example, one might provide a mask that's too small and needs values greater than `1.0` to fully cover the `CanvasItem` area. In such cases, play around with the property to figure out when the animation starts and when it ends._\n\n---\n\n`progress_bias` (`vec2`, default: `(0.0, 0.0)`)\n\nApplies an additional offset to the progress value for each individual grid cell. \n\n- The `x` component biases progress across columns.\n- the `y` component biases progress across rows. \n\nThis allows for creating \"wave-like\" or \"staggered\" animation effects where grid cells transition at slightly different times based on their position.\n\n## Contributing\n\nIf you find any bugs, improvement ideas, interesting recipes, feel free to [fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#about-forks) this repository and suggest a change. Since the shader is quite simple, exploring and contributing to it can be a valuable learning experience for beginners.\n\nIf you'd like to see an improvement, but don't know how to contribute, you can [create an Issue](https://github.com/cashew-olddew/Universal-Transition-Shader/issues/new).\n\n### Recipe Contributions\n\nTo add a new recipe to the [Common Transition Recipes](#common-transition-recipes) section, write down the recipe in the README.md file and add under it one ore more gifs showcasing variations of the transitions.\n\nTo generate a gif:\n1. Configure the AnimationPlayer in the main scene so that at time 1.0 & 1.5 the progress value reaches the value at which the transition completes.\n2. [Enable Movie Maker mode](https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#enabling-movie-maker-mode).\n3. Run the scene. This will generate a \"recipe.avi\" file under the [assets/recipes](assets/recipes) folder.\n4. Convert the generated .avi file to a .gif.\n\t- This is how I generated the other gifs. Feel free to generate them in any way.\n\t```bash\n\tffmpeg -i recipe.avi -vf \"fps=24,scale=160:160:flags=lanczos,palettegen\" -y palette.png\n\n\tffmpeg -i recipe.avi -i palette.png -filter_complex \"fps=24,scale=160:160:flags=lanczos[x];[x][1:v]paletteuse=dither=none\" -y recipe.gif\n\t```\n\t- Recipes with a single gif should have a width of 100px, while recipes with multiple variations should have a width of 80px. \n5. Add the generated .gif to the [assets/recipes](assets/recipes) folder and link it to the README.md file similarly to how the others are linked.\n\n## License\n\nThis project and shader falls under the [CC0](LICENSE) license, meaning that you can do anything you want with the code here, even use it commercially. You do not have any obligation to credit me, but doing so would be highly appreciated.\n\n## Support\n\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H2XSCXW)\n\nDonations are appreciated and help me continue creating free content. Please donate only what you can afford. 🥜\n"
  },
  {
    "path": "assets/day.jpg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bbvtgdx0l2gs2\"\npath=\"res://.godot/imported/day.jpg-38d54002cde6a7513e5dd5fa3f3756ba.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://assets/day.jpg\"\ndest_files=[\"res://.godot/imported/day.jpg-38d54002cde6a7513e5dd5fa3f3756ba.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "assets/icon.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bg4r54wgjqnhg\"\npath=\"res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://assets/icon.svg\"\ndest_files=[\"res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "assets/mask.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cjgwxc45vn007\"\npath=\"res://.godot/imported/mask.png-9f44758278b9ba6cd146ee809a9389e4.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://assets/mask.png\"\ndest_files=[\"res://.godot/imported/mask.png-9f44758278b9ba6cd146ee809a9389e4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "assets/night.jpg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dtcrggrkee5tb\"\npath=\"res://.godot/imported/night.jpg-1829c8ff2570590826aeda305085a4e0.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://assets/night.jpg\"\ndest_files=[\"res://.godot/imported/night.jpg-1829c8ff2570590826aeda305085a4e0.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "main.tscn",
    "content": "[gd_scene format=3 uid=\"uid://bkij6lxw3m3qx\"]\n\n[ext_resource type=\"Shader\" uid=\"uid://ddxgjcn15adry\" path=\"res://transition.gdshader\" id=\"1_0xm2m\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bg4r54wgjqnhg\" path=\"res://assets/icon.svg\" id=\"1_ig7tw\"]\n\n[sub_resource type=\"ShaderMaterial\" id=\"ShaderMaterial_h2yge\"]\nshader = ExtResource(\"1_0xm2m\")\nshader_parameter/use_sprite_alpha = true\nshader_parameter/use_transition_texture = false\nshader_parameter/transition_type = 0\nshader_parameter/position = Vector2(0, 0)\nshader_parameter/invert = false\nshader_parameter/grid_size = Vector2(1, 1)\nshader_parameter/rotation_angle = 0.0\nshader_parameter/global_x_mirror = false\nshader_parameter/global_y_mirror = false\nshader_parameter/local_x_mirror = false\nshader_parameter/local_y_mirror = false\nshader_parameter/stagger = Vector2(0, 0)\nshader_parameter/cumulative_stagger_flip = false\nshader_parameter/cumulative_stagger = Vector2(0, 0)\nshader_parameter/stagger_frequency = Vector2i(2, 2)\nshader_parameter/flip_frequency = Vector2i(1, 1)\nshader_parameter/basic_feather = 0.0\nshader_parameter/use_mask_size = false\nshader_parameter/mask_size = Vector2(100, 100)\nshader_parameter/edges = 6\nshader_parameter/shape_feather = 0.1\nshader_parameter/sectors = 1\nshader_parameter/clock_feather = 0.0\nshader_parameter/progress = 2.0\nshader_parameter/progress_bias = Vector2(0, 0)\n\n[sub_resource type=\"Animation\" id=\"Animation_0xm2m\"]\nlength = 0.001\ntracks/0/type = \"value\"\ntracks/0/imported = false\ntracks/0/enabled = true\ntracks/0/path = NodePath(\"Transition:material:shader_parameter/progress\")\ntracks/0/interp = 1\ntracks/0/loop_wrap = true\ntracks/0/keys = {\n\"times\": PackedFloat32Array(0),\n\"transitions\": PackedFloat32Array(1),\n\"update\": 0,\n\"values\": [2.0]\n}\ntracks/1/type = \"value\"\ntracks/1/imported = false\ntracks/1/enabled = true\ntracks/1/path = NodePath(\"AnimationPlayer:movie_quit_on_finish\")\ntracks/1/interp = 1\ntracks/1/loop_wrap = true\ntracks/1/keys = {\n\"times\": PackedFloat32Array(0),\n\"transitions\": PackedFloat32Array(1),\n\"update\": 1,\n\"values\": [false]\n}\n\n[sub_resource type=\"Animation\" id=\"Animation_h2yge\"]\nresource_name = \"transition\"\nlength = 3.0\ntracks/0/type = \"value\"\ntracks/0/imported = false\ntracks/0/enabled = true\ntracks/0/path = NodePath(\"Transition:material:shader_parameter/progress\")\ntracks/0/interp = 1\ntracks/0/loop_wrap = true\ntracks/0/keys = {\n\"times\": PackedFloat32Array(0, 1, 1.5, 2.5),\n\"transitions\": PackedFloat32Array(1, 1, 1, 1),\n\"update\": 0,\n\"values\": [0.0, 2.0, 2.0, 0.0]\n}\ntracks/1/type = \"value\"\ntracks/1/imported = false\ntracks/1/enabled = true\ntracks/1/path = NodePath(\"AnimationPlayer:movie_quit_on_finish\")\ntracks/1/interp = 1\ntracks/1/loop_wrap = true\ntracks/1/keys = {\n\"times\": PackedFloat32Array(3),\n\"transitions\": PackedFloat32Array(1),\n\"update\": 1,\n\"values\": [true]\n}\n\n[sub_resource type=\"AnimationLibrary\" id=\"AnimationLibrary_1bvp3\"]\n_data = {\n&\"RESET\": SubResource(\"Animation_0xm2m\"),\n&\"transition\": SubResource(\"Animation_h2yge\")\n}\n\n[node name=\"Main\" type=\"Control\" unique_id=26648888]\nlayout_mode = 3\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\n\n[node name=\"Background\" type=\"ColorRect\" parent=\".\" unique_id=874746378]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ncolor = Color(0.050980393, 0.06666667, 0.09019608, 1)\n\n[node name=\"Transition\" type=\"TextureRect\" parent=\".\" unique_id=1517388025]\nmaterial = SubResource(\"ShaderMaterial_h2yge\")\nlayout_mode = 1\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -64.0\noffset_top = -64.0\noffset_right = 64.0\noffset_bottom = 64.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntexture = ExtResource(\"1_ig7tw\")\n\n[node name=\"AnimationPlayer\" type=\"AnimationPlayer\" parent=\".\" unique_id=39034180]\nlibraries/ = SubResource(\"AnimationLibrary_1bvp3\")\nautoplay = &\"transition\"\n"
  },
  {
    "path": "project.godot",
    "content": "; Engine configuration file.\n; It's best edited using the editor UI and not directly,\n; since the parameters that go here are not all obvious.\n;\n; Format:\n;   [section] ; section goes between []\n;   param=value ; assign values to parameters\n\nconfig_version=5\n\n[animation]\n\ncompatibility/default_parent_skeleton_in_mesh_instance_3d=true\n\n[application]\n\nconfig/name=\"Universal Transition Shader\"\nrun/main_scene=\"uid://bkij6lxw3m3qx\"\nconfig/features=PackedStringArray(\"4.6\", \"Forward Plus\")\nconfig/icon=\"res://assets/icon.svg\"\n\n[display]\n\nwindow/size/viewport_width=160\nwindow/size/viewport_height=160\n\n[editor]\n\nmovie_writer/movie_file=\"E:/Projects/Games/Universal-Transition-Shader/assets/recipes/recipe.avi\"\n"
  },
  {
    "path": "transition.gdshader",
    "content": "shader_type canvas_item;\n\nuniform bool use_sprite_alpha = true;\nuniform bool use_transition_texture = false;\nuniform sampler2D transition_texture;\nuniform int transition_type: hint_enum(\"Basic\", \"Mask\", \"Shape\", \"Clock\") = 0;\n\ngroup_uniforms positioning;\nuniform vec2 position = vec2(0,0);\nuniform bool invert = false;\nuniform vec2 grid_size = vec2(1.0, 1.0);\nuniform float rotation_angle = 0.0;\n\ngroup_uniforms positioning.mirror;\nuniform bool global_x_mirror = false;\nuniform bool global_y_mirror = false;\nuniform bool local_x_mirror = false;\nuniform bool local_y_mirror = false;\n\ngroup_uniforms positioning.stagger;\nuniform vec2 stagger = vec2(0.0, 0.0);\nuniform bool cumulative_stagger_flip = false;\nuniform vec2 cumulative_stagger = vec2(0, 0);\nuniform ivec2 stagger_frequency = ivec2(2, 2);\nuniform ivec2 flip_frequency = ivec2(1, 1);\n\ngroup_uniforms basic_transition_controls;\nuniform float basic_feather = 0.0;\n\ngroup_uniforms mask_transition_controls;\nuniform sampler2D mask_texture;\nuniform bool use_mask_size = false;\nuniform vec2 mask_size = vec2(100.0);\n\ngroup_uniforms shape_transition_controls;\nuniform int edges : hint_range(3, 64) = 6; // default hexagon\nuniform float shape_feather : hint_range(0.0, 10.0) = 0.1;\n\ngroup_uniforms clock_transition_controls;\nuniform int sectors : hint_range(1, 128) = 1;\nuniform float clock_feather : hint_range(0.0, 16.0) = 0.0;\n\ngroup_uniforms animation;\nuniform float progress = 0.0;\nuniform vec2 progress_bias = vec2(0.0);\n\nvarying vec4 modulate;\n\nvec2 use_actual_texture_size(vec2 uv, vec2 texture_size, vec2 raw_uv_deriv) {\n\tuv -= 0.5;\n\tfloat screen_ratio = raw_uv_deriv.x / raw_uv_deriv.y;\n\tfloat texture_ratio = texture_size.x / texture_size.y;\n\tfloat mixed_ratio = texture_ratio * screen_ratio;\n\n\tif (screen_ratio > texture_ratio) {\n\t\tuv.x /= mixed_ratio;\n\t} else {\n\t\tuv.y *= mixed_ratio;\n\t}\n\n\treturn uv + 0.5;\n}\n\nvec2 get_stagger_offset(vec2 grid) {\n\tvec2 cells = floor(grid);\n\tfloat offset_row = mod(cells.y, float(stagger_frequency.x)) == 0.0 ? 0.5 : 0.0;\n\tfloat offset_col = mod(cells.x, float(stagger_frequency.y)) == 0.0 ? 0.5 : 0.0;\n\n\tfloat row_index = cumulative_stagger_flip ? (grid_size.y - 1.0 - cells.y) : cells.y;\n\tfloat col_index = cumulative_stagger_flip ? (grid_size.x - 1.0 - cells.x) : cells.x;\n\n\treturn vec2(\n\t\toffset_row * (stagger.x + row_index * cumulative_stagger.x),\n\t\toffset_col * (stagger.y + col_index * cumulative_stagger.y)\n\t);\n}\n\nvec2 get_grid_flip(vec2 grid) {\n\tvec2 cells = floor(grid);\n\tfloat flip_row = mod(cells.y, float(flip_frequency.x)) == 0.0 ? 1.0 : -1.0;\n\tfloat flip_col = mod(cells.x, float(flip_frequency.y)) == 0.0 ? 1.0 : -1.0;\n\n\treturn vec2(\n\t\tflip_row * grid.x,\n\t\tflip_col * grid.y\n\t);\n}\n\nvec2 rotate(vec2 v, float angle) {\n\treturn mat2(\n\t\tvec2(cos(angle), sin(angle)),\n\t\tvec2(-sin(angle), cos(angle))\n\t\t) * v;\n}\n\nvec2 get_edges(float center, float width) {\n\tfloat half_width = width * 0.5;\n\tfloat edge0 = center - half_width;\n\tfloat edge1 = center + half_width + 1e-5;\n\treturn vec2(edge0, edge1);\n}\n\nfloat get_local_progress(vec2 grid) {\n    vec2 cell = floor(grid);\n\t// Easier to control bias values\n\tvec2 pretty_bias = progress_bias / 10.0;\n    float offset = dot(cell, pretty_bias);\n    return progress - offset;\n}\n\nvec2 apply_mirror(vec2 uv, bool mirror_x, bool mirror_y) {\n    vec2 mirrored = 0.5 - abs(uv - 0.5);\n    vec2 toggle = vec2(float(mirror_x), float(mirror_y));\n    return mix(uv, mirrored, toggle);\n}\n\nvoid vertex() {\n\tmodulate = COLOR;\n}\n\nvoid fragment() {\n\tvec2 uv_deriv = fwidth(UV); // derivate used for keeping mask texture size regardless of sprite ratio\n\n\tvec2 mirrored_uv = apply_mirror(UV, global_x_mirror, global_y_mirror);\n\n\tvec2 grid = mirrored_uv * grid_size;\n\tvec2 offset_uv = grid + get_stagger_offset(grid);\n\tvec2 grid_flipped_uv = get_grid_flip(offset_uv);\n\tvec2 tiled_uv = fract(grid_flipped_uv);\n\n\ttiled_uv = apply_mirror(tiled_uv, local_x_mirror, local_y_mirror);\n\n\tvec2 uv = (tiled_uv - position) * 2.0;\n\tuv = rotate(uv, radians(rotation_angle));\n\n\tfloat local_progress = get_local_progress(grid);\n\n\tCOLOR = texture(TEXTURE, UV) * modulate;\n\tfloat alpha = COLOR.a;\n\tfloat transition_progress = 0.0;\n\n\tif (transition_type == 1) {\n\t\tvec2 mask_zoom_uv = uv / local_progress;\n\t\tvec2 mask_zoom_uv_01 = mask_zoom_uv * 0.5 + 0.5;\n\t\tmask_zoom_uv_01 = use_mask_size ? use_actual_texture_size(mask_zoom_uv_01, mask_size, uv_deriv) : mask_zoom_uv_01;\n\n\t\ttransition_progress = texture(mask_texture, mask_zoom_uv_01).r;\n\n\t} else if(transition_type == 2) {\n    \tfloat radius = length(uv);\n\t    float angle = atan(uv.y, uv.x);\n\t\tfloat sector_angle = 2.0 * PI / float(edges);\n\t\tfloat half_sector = sector_angle / 2.0;\n\t\t// Define polygon sectors using the power of trigonometry\n\t\tfloat d = cos(half_sector) / cos(mod(angle + half_sector, sector_angle) - half_sector);\n\n\t\tvec2 smooth_edges = get_edges(local_progress, shape_feather);\n\t\ttransition_progress = smoothstep(smooth_edges.x, smooth_edges.y, radius / d);\n\n\t} else if (transition_type == 3) {\n\t\tfloat radius = length(uv);\n\t\tfloat angle = atan(uv.y, uv.x);\n\t\tfloat sector_angle = 2.0 * PI / float(sectors);\n\t\tfloat half_sector = sector_angle / 2.0;\n\t\tangle = mod(angle - half_sector, sector_angle);\n\n\t\tfloat progress_angle = local_progress * 2.0 * PI / float(sectors);\n\t\tfloat smooth_angle = smoothstep(0.0, clock_feather, progress_angle - angle);\n\t\ttransition_progress = smooth_angle;\n\n\t} else {\n\t\tvec2 smooth_edges = get_edges(local_progress, basic_feather);\n\n\t\tfloat separation_x = smoothstep(smooth_edges.x, smooth_edges.y, abs(uv.x));\n\t\tfloat separation_y = smoothstep(smooth_edges.x, smooth_edges.y, abs(uv.y));\n\t\ttransition_progress = max(separation_x, separation_y);\n\t}\n\n\talpha = invert ? 1.0 - transition_progress : transition_progress;\n\talpha = use_sprite_alpha ? min(COLOR.a, alpha) : alpha;\n\n\tvec4 transition_color = texture(transition_texture, UV);\n\n\tif (use_transition_texture) {\n\t\tvec4 chosen_color = mix(transition_color, COLOR, alpha);\n\t\tCOLOR = chosen_color;\n\t} else {\n\t\tCOLOR.a = alpha;\n\t}\n}\n"
  },
  {
    "path": "transition.gdshader.uid",
    "content": "uid://ddxgjcn15adry\n"
  }
]