├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── SCsub ├── animation_node_flash.cpp ├── animation_node_flash.h ├── config.py ├── flash_player.cpp ├── flash_player.h ├── flash_resources.cpp ├── flash_resources.h ├── register_types.cpp ├── register_types.h ├── resource_importer_flash.cpp └── resource_importer_flash.h /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | # Sequence of patterns matched against refs/tags 5 | tags: 6 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 7 | env: 8 | GODOT_BASE_BRANCH: 3.2 9 | SCONS_CACHE_MSVC_CONFIG: true 10 | SCONS_CACHE_LIMIT: 4096 11 | 12 | jobs: 13 | build-linux-editor: 14 | runs-on: "ubuntu-20.04" 15 | steps: 16 | - name: Checkout Godot Engine 3.2 17 | uses: actions/checkout@v2 18 | with: 19 | repository: 'godotengine/godot' 20 | ref: '3.2.3-stable' 21 | - name: Checkout Flash Module 22 | uses: actions/checkout@v2 23 | with: 24 | path: 'modules/flash' 25 | 26 | # Azure repositories are not reliable, we need to prevent azure giving us packages. 27 | - name: Make apt sources.list use the default Ubuntu repositories 28 | run: | 29 | sudo cp -f misc/ci/sources.list /etc/apt/sources.list 30 | sudo apt-get update 31 | 32 | # Install all packages (except scons) 33 | - name: Configure dependencies 34 | run: | 35 | sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \ 36 | libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm 37 | 38 | # Upload cache on completion and check it out now 39 | - name: Load .scons_cache directory 40 | id: linux-editor-cache 41 | uses: actions/cache@v2 42 | with: 43 | path: ${{github.workspace}}/.scons_cache/ 44 | key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}} 45 | 46 | # Use python 3.x release (works cross platform; best to keep self contained in it's own step) 47 | - name: Set up Python 3.x 48 | uses: actions/setup-python@v2 49 | with: 50 | # Semantic version range syntax or exact version of a Python version 51 | python-version: '3.x' 52 | # Optional - x64 or x86 architecture, defaults to x64 53 | architecture: 'x64' 54 | 55 | # Setup scons, print python version and scons version info, so if anything is broken it won't run the build. 56 | - name: Configuring Python packages 57 | run: | 58 | python -c "import sys; print(sys.version)" 59 | python -m pip install scons 60 | python --version 61 | scons --version 62 | 63 | # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags 64 | - name: Compilation 65 | env: 66 | SCONS_CACHE: ${{github.workspace}}/.scons_cache/ 67 | run: | 68 | scons -j2 verbose=yes warnings=all werror=yes platform=x11 tools=yes target=release_debug 69 | - name: List output 70 | run: find bin 71 | - name: Upload artifact 72 | uses: actions/upload-artifact@v2 73 | with: 74 | name: editor.linux 75 | path: bin/* 76 | 77 | build-osx-editor: 78 | runs-on: "macos-latest" 79 | steps: 80 | - name: Checkout Godot Engine 3.2 81 | uses: actions/checkout@v2 82 | with: 83 | repository: 'godotengine/godot' 84 | ref: '3.2.3-stable' 85 | - name: Checkout Flash Module 86 | uses: actions/checkout@v2 87 | with: 88 | path: 'modules/flash' 89 | 90 | # Upload cache on completion and check it out now 91 | - name: Load .scons_cache directory 92 | id: macos-editor-cache 93 | uses: actions/cache@v2 94 | with: 95 | path: ${{github.workspace}}/.scons_cache/ 96 | key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}} 97 | 98 | # Use python 3.x release (works cross platform; best to keep self contained in it's own step) 99 | - name: Set up Python 3.x 100 | uses: actions/setup-python@v2 101 | with: 102 | # Semantic version range syntax or exact version of a Python version 103 | python-version: '3.x' 104 | # Optional - x64 or x86 architecture, defaults to x64 105 | architecture: 'x64' 106 | 107 | # Setup scons, print python version and scons version info, so if anything is broken it won't run the build. 108 | - name: Configuring Python packages 109 | run: | 110 | python -c "import sys; print(sys.version)" 111 | python -m pip install scons 112 | python --version 113 | scons --version 114 | 115 | # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags 116 | - name: Compilation 117 | env: 118 | SCONS_CACHE: ${{github.workspace}}/.scons_cache/ 119 | run: | 120 | scons -j2 verbose=yes warnings=all werror=yes platform=osx tools=yes target=release_debug 121 | - name: Upload artifact 122 | uses: actions/upload-artifact@v2 123 | with: 124 | name: editor.osx 125 | path: bin/* 126 | 127 | build-windows-editor: 128 | # Windows 10 with latest image 129 | runs-on: "windows-latest" 130 | steps: 131 | - name: Checkout Godot Engine 3.2 132 | uses: actions/checkout@v2 133 | with: 134 | repository: 'godotengine/godot' 135 | ref: '3.2.3-stable' 136 | - name: Checkout Flash Module 137 | uses: actions/checkout@v2 138 | with: 139 | path: 'modules/flash' 140 | 141 | # Upload cache on completion and check it out now 142 | # Editing this is pretty dangerous for Windows since it can break and needs to be properly tested with a fresh cache. 143 | - name: Load .scons_cache directory 144 | id: windows-editor-cache 145 | uses: RevoluPowered/cache@v2.1 146 | with: 147 | path: /.scons_cache/ 148 | key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}} 149 | 150 | # Use python 3.x release (works cross platform; best to keep self contained in it's own step) 151 | - name: Set up Python 3.x 152 | uses: actions/setup-python@v2 153 | with: 154 | # Semantic version range syntax or exact version of a Python version 155 | python-version: '3.x' 156 | # Optional - x64 or x86 architecture, defaults to x64 157 | architecture: 'x64' 158 | 159 | # Setup scons, print python version and scons version info, so if anything is broken it won't run the build. 160 | - name: Configuring Python packages 161 | run: | 162 | python -c "import sys; print(sys.version)" 163 | python -m pip install scons pywin32 164 | python --version 165 | scons --version 166 | 167 | # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags 168 | - name: Compilation 169 | env: 170 | SCONS_CACHE: /.scons_cache/ 171 | run: | 172 | scons -j2 verbose=yes warnings=all werror=yes platform=windows tools=yes target=release_debug 173 | 174 | - name: Upload artifact 175 | uses: actions/upload-artifact@v2 176 | with: 177 | name: editor.windows 178 | path: bin/* 179 | 180 | archive: 181 | runs-on: ubuntu-latest 182 | needs: [build-linux-editor, build-osx-editor, build-windows-editor] 183 | steps: 184 | - run: mkdir -p release 185 | - name: Download Linux editor 186 | uses: actions/download-artifact@v2 187 | with: 188 | name: editor.linux 189 | path: release 190 | - name: Download OSX Editor 191 | uses: actions/download-artifact@v2 192 | with: 193 | name: editor.osx 194 | path: release 195 | - name: Download Windows Editor 196 | uses: actions/download-artifact@v2 197 | with: 198 | name: editor.windows 199 | path: release 200 | - name: Show contents 201 | run: find release 202 | - name: Prepare assets 203 | run: | 204 | mv release/godot.windows.opt.tools.64.exe release/godot-flash.exe 205 | zip -r release/godot-flash.windows.zip release/godot-flash.exe 206 | mv release/godot.x11.opt.tools.64 release/godot-flash 207 | zip -r release/godot-flash.linux.zip release/godot-flash 208 | mv release/godot.osx.opt.tools.64 release/godot-flash 209 | zip -r release/godot-flash.osx.zip release/godot-flash 210 | - name: Create Release 211 | id: create_release 212 | uses: actions/create-release@v1 213 | env: 214 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 215 | with: 216 | tag_name: ${{ github.ref }} 217 | release_name: Release ${{ github.ref }} 218 | draft: false 219 | prerelease: false 220 | - name: Upload Windows Editor 221 | id: upload-windows-release 222 | uses: actions/upload-release-asset@v1 223 | env: 224 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 225 | with: 226 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 227 | asset_path: ./release/godot-flash.windows.zip 228 | asset_name: godot-flash.windows.zip 229 | asset_content_type: application/zip 230 | - name: Upload OSX Editor 231 | id: upload-osx-release 232 | uses: actions/upload-release-asset@v1 233 | env: 234 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 235 | with: 236 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 237 | asset_path: ./release/godot-flash.osx.zip 238 | asset_name: godot-flash.osx.zip 239 | asset_content_type: application/zip 240 | - name: Upload Linux Editor 241 | id: upload-linux-release 242 | uses: actions/upload-release-asset@v1 243 | env: 244 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 245 | with: 246 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 247 | asset_path: ./release/godot-flash.linux.zip 248 | asset_name: godot-flash.linux.zip 249 | asset_content_type: application/zip 250 | 251 | 252 | 253 | # build-toolkit-win: 254 | # runs-on: ubuntu-latest 255 | # steps: 256 | # - name: Install mingw 257 | # run: sudo apt-get install gcc-mingw-w64 258 | # - uses: actions/checkout@v2 259 | # - uses: actions/cache@v2 260 | # with: 261 | # path: | 262 | # ~/.cargo/registry 263 | # ~/.cargo/git 264 | # toolkit/target 265 | # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 266 | # - uses: actions-rs/toolchain@v1 267 | # with: 268 | # toolchain: stable 269 | # target: x86_64-pc-windows-gnu 270 | # - uses: actions-rs/cargo@v1 271 | # with: 272 | # use-cross: true 273 | # command: build 274 | # args: --release --target x86_64-pc-windows-gnu --manifest-path=toolkit/Cargo.toml 275 | # - name: Show content 276 | # run: find . 277 | # - name: Upload library 278 | # uses: actions/upload-artifact@v2 279 | # with: 280 | # name: toolkit.win 281 | # path: toolkit/target/x86_64-pc-windows-gnu/release/toolkit.exe 282 | 283 | # build-toolkit-osx: 284 | # runs-on: macos-10.15 285 | # steps: 286 | # - uses: actions/checkout@v2 287 | # - uses: actions/cache@v2 288 | # with: 289 | # path: | 290 | # ~/.cargo/registry 291 | # ~/.cargo/git 292 | # toolkit/target 293 | # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 294 | # - uses: actions-rs/toolchain@v1 295 | # with: 296 | # toolchain: stable 297 | # - uses: actions-rs/cargo@v1 298 | # with: 299 | # command: build 300 | # args: --release --manifest-path=toolkit/Cargo.toml 301 | # - name: Upload library 302 | # uses: actions/upload-artifact@v2 303 | # with: 304 | # name: toolkit.osx 305 | # path: toolkit/target/release/toolkit 306 | # archive: 307 | # runs-on: ubuntu-latest 308 | # needs: [build-node, build-toolkit-win, build-toolkit-osx] 309 | # steps: 310 | # - run: mkdir -p "funexpected-tools/Funexpected Tools" 311 | # - uses: actions/checkout@v2 312 | # - name: Download compiled js 313 | # uses: actions/download-artifact@v2 314 | # with: 315 | # name: tools 316 | # path: funexpected-tools/Funexpected Tools 317 | # - name: Download toolkit.win 318 | # uses: actions/download-artifact@v2 319 | # with: 320 | # name: toolkit.win 321 | # path: funexpected-tools/Funexpected Tools 322 | # - name: Download toolkit.osx 323 | # uses: actions/download-artifact@v2 324 | # with: 325 | # name: toolkit.osx 326 | # path: funexpected-tools/Funexpected Tools 327 | 328 | # - name: Compress release 329 | # run: zip -r funexpected-tools funexpected-tools 330 | 331 | # - name: Create Release 332 | # id: create_release 333 | # uses: actions/create-release@v1 334 | # env: 335 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 336 | # with: 337 | # tag_name: ${{ github.ref }} 338 | # release_name: Release ${{ github.ref }} 339 | # draft: false 340 | # prerelease: false 341 | 342 | # - name: Upload Release Asset 343 | # id: upload-release-asset 344 | # uses: actions/upload-release-asset@v1 345 | # env: 346 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 347 | # with: 348 | # upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 349 | # asset_path: ./funexpected-tools.zip 350 | # asset_name: funexpected-tools.zip 351 | # asset_content_type: application/zip 352 | 353 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.pyc 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | This module adds [Adobe Animate](https://www.adobe.com/products/animate.html) animation support for Godot Engine. `FlashPlayer` node available to draw a flash document (imported as` FlashDocument` resource) within a single draw call. You can use the precompiled editor (see [releases](https://github.com/funexpected/godot-flash-module/releases)) or build editor and templates by yourself. 3 | 4 | ## Compilation 5 | Add this repo to `modules/flash` folder of Godot Engine source. See engine [compilation instructions](https://docs.godotengine.org/en/stable/development/compiling/index.html) instructions. 6 | ``` bash 7 | cd /path/to/godot 8 | git submodule add https://github.com/funexpected/godot-flash-module.git modules/flash 9 | scons -j8 10 | ``` 11 | 12 | ## Usage 13 | - Install [Funexpected Flash Tools](https://github.com/funexpected/flash-tools) plugin. 14 | - Export your `Amazing Project.xfl` Flash project using `Commands/Funepxected Tools/Export` to` amazing_project.zfl` 15 | - Add `amazing_project.zfl` to Godot project 16 | - Add `FlashPlayer` node to the scene 17 | - Set `Resource` property of` FlashPlayer` to `res: // amazing_project.zfl` 18 | - You can switch active timeline, label (if it present), and change skins of symbols. 19 | 20 | ## Supported and planed features 21 | - [x] Bitmaps (full rasterization prepared by [Funexpected Flash Tools](https://github.com/funexpected/flash-tools)) 22 | - [x] Groups (decomposed and rasterized) 23 | - [x] Name Labels (for splitting single animation to multiply parts) 24 | - [x] Anchor Labels (for skinning, e.g. forcing custom frames in target symbols) 25 | - [x] User-defined signals (`animation_event` using comment-labels) 26 | - [x] Predefined signals (`animation_completed`) 27 | - [x] Looping (loop, once, single frame) 28 | - [x] Color effects 29 | - [x] Tweening (for properties together) 30 | - [x] Masks (up to 4 masking elements per masked element) 31 | - [x] Mutltiply spritesheets packed by [Funexpected Flash Tools](https://github.com/funexpected/flash-tools) 32 | - [x] Downscaling spritesheets on import time 33 | - [x] Custom properties for importing textures (loseless/vram/uncompressed, mipmaps, filter) 34 | - [x] Compressing VRAM textures (reducing disk space of exported Godot project) 35 | 36 | ## Unsupported features: 37 | 38 | - [ ] Shapes 39 | - [ ] Tweening (for properties separately) 40 | - [ ] Motion guides 41 | - [ ] Warp tool 42 | - [ ] Sounds 43 | - [ ] Custom material (for now, you can't change material for `FlashPlayer` node) 44 | - [ ] Blending 45 | - [ ] Filters 46 | -------------------------------------------------------------------------------- /SCsub: -------------------------------------------------------------------------------- 1 | Import('env') 2 | Import('env_modules') 3 | 4 | module_env = env_modules.Clone() 5 | module_env.add_source_files(env.modules_sources,"*.cpp") 6 | 7 | Export('env_modules') 8 | Export('env') 9 | 10 | 11 | -------------------------------------------------------------------------------- /animation_node_flash.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "animation_node_flash.h" 24 | #include "flash_player.h" 25 | #include "core/message_queue.h" 26 | #include "scene/gui/control.h" 27 | #ifdef TOOLS_ENABLED 28 | #include "editor/plugins/animation_tree_editor_plugin.h" 29 | #endif 30 | 31 | String FlashMachine::get_configuration_warning() const { 32 | String warning = AnimationTree::get_configuration_warning(); 33 | 34 | if (!has_node(flash_player)) { 35 | if (warning != String()) { 36 | warning += "\n\n"; 37 | } 38 | warning += TTR("Path to an FlashPlayer node containing flash animations is not set."); 39 | return warning; 40 | } 41 | 42 | FlashPlayer* fp = Object::cast_to(get_node(flash_player)); 43 | if (!fp) { 44 | if (warning != String()) { 45 | warning += "\n\n"; 46 | } 47 | warning += TTR("Path set for FlashPlayer does not lead to an FlashPlayer node."); 48 | return warning; 49 | } 50 | 51 | if (!fp->get_resource().is_valid()) { 52 | if (warning != String()) { 53 | warning += "\n\n"; 54 | } 55 | warning += TTR("FlashPlayer node missing resource with animations."); 56 | return warning; 57 | } 58 | 59 | return warning; 60 | } 61 | 62 | void FlashMachine::set_flash_player(const NodePath &p_player) { 63 | flash_player = p_player; 64 | #ifdef TOOLS_ENABLED 65 | property_list_changed_notify(); 66 | update_configuration_warning(); 67 | FlashPlayer* fp = Object::cast_to(get_node(flash_player)); 68 | if (fp) { 69 | fp->connect("resource_changed", this, "update_configuration_warning"); 70 | fp->connect("resource_changed", this, "property_list_changed_notify"); 71 | } 72 | #endif 73 | } 74 | 75 | NodePath FlashMachine::get_flash_player() const { 76 | return flash_player; 77 | } 78 | 79 | void FlashMachine::_bind_methods() { 80 | ClassDB::bind_method(D_METHOD("set_flash_player", "flash_player"), &FlashMachine::set_flash_player); 81 | ClassDB::bind_method(D_METHOD("get_flash_player"), &FlashMachine::get_flash_player); 82 | 83 | ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "flash_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "FlashPlayer"), "set_flash_player", "get_flash_player"); 84 | } 85 | 86 | 87 | bool FlashMachine::_set(const StringName &p_name, const Variant &p_value) { 88 | if (p_name == "track") { 89 | track = p_value; 90 | return true; 91 | } 92 | return false; 93 | } 94 | 95 | bool FlashMachine::_get(const StringName &p_name, Variant &r_ret) const { 96 | if (p_name == "track") { 97 | r_ret = track; 98 | return true; 99 | } 100 | return false; 101 | } 102 | 103 | void FlashMachine::_get_property_list(List *p_list) const { 104 | if (!has_node(flash_player)) return; 105 | FlashPlayer* fp = Object::cast_to(get_node(flash_player)); 106 | if (!fp) return; 107 | Vector tracks; 108 | PoolStringArray existed_tracks = fp->get_clips_tracks(); 109 | tracks.push_back("[main]"); 110 | for (int i=0; i < existed_tracks.size(); i++) { 111 | tracks.push_back(existed_tracks[i]); 112 | } 113 | p_list->push_back(PropertyInfo(Variant::STRING, "track", PROPERTY_HINT_ENUM, String(",").join(tracks))); 114 | } 115 | 116 | FlashMachine::FlashMachine() { 117 | track = "[main]"; 118 | } 119 | 120 | 121 | /* 122 | * Flash Clip 123 | */ 124 | 125 | bool AnimationNodeFlashClip::_set(const StringName &p_name, const Variant &p_value) { 126 | if (p_name == "clip") { 127 | clip = p_value; 128 | return true; 129 | } else { 130 | return false; 131 | } 132 | } 133 | 134 | bool AnimationNodeFlashClip::_get(const StringName &p_name, Variant &r_ret) const { 135 | if (p_name == "clip") { 136 | r_ret = clip; 137 | return true; 138 | } else if (p_name == "warning") { 139 | r_ret = ""; 140 | return true; 141 | } else { 142 | return false; 143 | } 144 | } 145 | 146 | void AnimationNodeFlashClip::_default_property_list(List *p_list) const { 147 | p_list->push_back(PropertyInfo(Variant::STRING, "clip", PROPERTY_HINT_NONE, "")); 148 | p_list->push_back(PropertyInfo(Variant::STRING, "warning", PROPERTY_HINT_PLACEHOLDER_TEXT, "Invalid flash symbol state")); 149 | } 150 | 151 | void AnimationNodeFlashClip::_get_property_list(List *p_list) const { 152 | FlashPlayer *fp; 153 | FlashMachine *tree; 154 | #ifdef TOOLS_ENABLED 155 | AnimationTreeEditor *editor = AnimationTreeEditor::get_singleton(); 156 | if (!editor || !editor->is_visible()) return _default_property_list(p_list); 157 | 158 | tree = Object::cast_to(editor->get_tree()); 159 | if (!tree || !tree->has_node(tree->get_animation_player())) return _default_property_list(p_list); 160 | 161 | fp = Object::cast_to(tree->get_node(tree->get_flash_player())); 162 | if (!fp) return _default_property_list(p_list); 163 | #else 164 | return; 165 | #endif 166 | 167 | StringName track = tree->get_track(); 168 | Vector clips; 169 | StringName default_value; 170 | if (track == "[main]") { 171 | p_list->push_back(PropertyInfo(Variant::STRING, "Track: [main]", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); 172 | PoolStringArray symbols = fp->get_symbols(); 173 | for (int i=0; i < symbols.size(); i++) { 174 | String symbol = symbols[i]; 175 | if (i == 0) default_value = symbol; 176 | PoolStringArray symbol_clips = fp->get_clips(symbol); 177 | clips.push_back(symbol); 178 | for (int j=0; j < symbol_clips.size(); j++) { 179 | clips.push_back(symbol + "/" + symbol_clips[j]); 180 | } 181 | } 182 | } else { 183 | p_list->push_back(PropertyInfo(Variant::STRING, String("Track: ") + track, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); 184 | PoolStringArray track_clips = fp->get_clips_for_track(track); 185 | for (int i=0; i< track_clips.size(); i++) { 186 | if (i == 0) default_value = symbol; 187 | clips.push_back(track_clips[i]); 188 | } 189 | } 190 | p_list->push_back(PropertyInfo(Variant::STRING, "clip", PROPERTY_HINT_ENUM, String(",").join(clips))); 191 | 192 | if (clip == StringName()) { 193 | MessageQueue::get_singleton()->push_call(get_instance_id(), "set", "clip", default_value); 194 | } 195 | } 196 | 197 | void AnimationNodeFlashClip::get_parameter_list(List *r_list) const { 198 | r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0)); 199 | } 200 | 201 | String AnimationNodeFlashClip::get_caption() const { 202 | return "Flash Clip"; 203 | } 204 | 205 | float AnimationNodeFlashClip::process(float p_time, bool p_seek) { 206 | if (clip == StringName()) return 0.0; 207 | 208 | FlashMachine *fm = Object::cast_to(state->tree); 209 | ERR_FAIL_COND_V(!fm, 0); 210 | FlashPlayer *fp = Object::cast_to(fm->get_node(fm->get_flash_player())); 211 | ERR_FAIL_COND_V(!fp, 0); 212 | StringName track = fm->get_track(); 213 | 214 | float time = get_parameter(this->time); 215 | 216 | float step = p_time; 217 | 218 | if (p_seek) { 219 | time = p_time; 220 | step = 0; 221 | } else { 222 | time = MAX(0, time + p_time); 223 | } 224 | float elapsed; 225 | float remaining; 226 | if (track == "[main]") { 227 | Vector clip_path = String(clip).split("/", true, 1); 228 | String s = clip_path[0]; 229 | String c = clip_path.size() == 2 ? clip_path[1] : ""; 230 | float anim_size = fp->get_duration(s, c) / fp->get_frame_rate(); 231 | if (time > anim_size) { 232 | elapsed = anim_size; 233 | } else { 234 | elapsed = time; 235 | } 236 | remaining = anim_size - elapsed; 237 | fp->set_active_symbol(s); 238 | fp->set_active_clip(c); 239 | fp->advance(step, p_seek, false); 240 | } else { 241 | fp->advance_clip_for_track(track, clip, step, p_seek, &elapsed, &remaining); 242 | } 243 | set_parameter(this->time, elapsed); 244 | return remaining; 245 | } 246 | 247 | AnimationNodeFlashClip::AnimationNodeFlashClip() { 248 | time = "time"; 249 | } -------------------------------------------------------------------------------- /animation_node_flash.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 24 | #ifndef ANIMATION_NODE_FLASH_H 25 | #define ANIMATION_NODE_FLASH_H 26 | 27 | #include "scene/animation/animation_tree.h" 28 | 29 | class FlashMachine: public AnimationTree { 30 | GDCLASS(FlashMachine, AnimationTree); 31 | 32 | NodePath flash_player; 33 | String track; 34 | 35 | protected: 36 | static void _bind_methods(); 37 | bool _set(const StringName &p_name, const Variant &p_value); 38 | bool _get(const StringName &p_name, Variant &r_ret) const; 39 | void _get_property_list(List *p_list) const; 40 | 41 | 42 | public: 43 | virtual String get_configuration_warning() const; 44 | void set_flash_player(const NodePath &p_player); 45 | NodePath get_flash_player() const; 46 | String get_track() const { return track; } 47 | 48 | FlashMachine(); 49 | }; 50 | 51 | 52 | class AnimationNodeFlashClip: public AnimationRootNode { 53 | GDCLASS(AnimationNodeFlashClip, AnimationRootNode); 54 | 55 | 56 | StringName symbol; 57 | StringName clip; 58 | StringName time; 59 | 60 | protected: 61 | bool _set(const StringName &p_name, const Variant &p_value); 62 | bool _get(const StringName &p_name, Variant &r_ret) const; 63 | void _get_property_list(List *p_list) const; 64 | void _default_property_list(List *p_list) const; 65 | 66 | // static void _bind_methods(); 67 | 68 | public: 69 | void get_parameter_list(List *r_list) const; 70 | 71 | virtual String get_caption() const; 72 | virtual float process(float p_time, bool p_seek); 73 | 74 | // void set_timeline(const StringName &p_name); 75 | // StringName get_timeline() const; 76 | // void set_clip(const StringName &p_clip); 77 | // StringName get_clip() const; 78 | 79 | AnimationNodeFlashClip(); 80 | }; 81 | 82 | 83 | #endif // ANIMATION_NODE_FLASH_H -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def can_build(platform): 4 | return True 5 | 6 | 7 | def configure(env): 8 | godot_feature_importer_version = False 9 | with open("editor/import/editor_import_plugin.h", 'r') as f: 10 | godot_feature_importer_version = "get_importer_version" in f.read() 11 | if godot_feature_importer_version: 12 | env.Append(CPPFLAGS=['-DGODOT_FEATURE_IMPORTER_VERSION']) 13 | print("Importer Version supported") 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /flash_player.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 24 | #include "flash_player.h" 25 | 26 | #ifdef TOOLS_ENABLED 27 | #include 28 | #endif 29 | 30 | RID FlashPlayer::flash_shader = RID(); 31 | 32 | void FlashPlayer::_notification(int p_what) { 33 | switch (p_what) { 34 | case NOTIFICATION_ENTER_TREE : { 35 | if (!clipping_texture.is_valid()) { 36 | clipping_texture.instance(); 37 | clipping_data.instance(); 38 | clipping_data->create(32, 32, false, Image::FORMAT_RGBAH); 39 | clipping_texture->create_from_image(clipping_data); 40 | VisualServer::get_singleton()->material_set_param(flash_material, "CLIPPING_TEXTURE", clipping_texture); 41 | } 42 | if (resource.is_valid()) { 43 | VisualServer::get_singleton()->material_set_param(flash_material, "ATLAS_SIZE", resource->get_atlas_size()); 44 | VisualServer::get_singleton()->material_set_param(flash_material, "ATLAS", resource->get_atlas()); 45 | } 46 | } break; 47 | case NOTIFICATION_READY: { 48 | set_process(true); 49 | } break; 50 | 51 | case NOTIFICATION_PROCESS: { 52 | performance_triangles_generated = 0; 53 | if (playing && active_symbol.is_valid()) { 54 | advance(get_process_delta_time(), false, true); 55 | } 56 | } break; 57 | 58 | case NOTIFICATION_DRAW: { 59 | if (active_symbol.is_valid() && points.size() > 0 && resource.is_valid()) { 60 | update_clipping_data(); 61 | VisualServer::get_singleton()->mesh_clear(mesh); 62 | Array arrays; 63 | arrays.resize(Mesh::ARRAY_MAX); 64 | arrays[Mesh::ARRAY_VERTEX] = points; 65 | arrays[Mesh::ARRAY_INDEX] = indices; 66 | arrays[Mesh::ARRAY_COLOR] = colors; 67 | arrays[Mesh::ARRAY_TEX_UV] = uvs; 68 | VisualServer::get_singleton()->mesh_add_surface_from_arrays( 69 | mesh, 70 | VisualServer::PRIMITIVE_TRIANGLES, 71 | arrays, Array(), 72 | VisualServer::ARRAY_FLAG_USE_2D_VERTICES 73 | ); 74 | VisualServer::get_singleton()->canvas_item_add_mesh(get_canvas_item(), mesh); 75 | performance_triangles_drawn = indices.size() / 3; 76 | } 77 | } break; 78 | 79 | case NOTIFICATION_VISIBILITY_CHANGED: { 80 | performance_triangles_drawn = 0; 81 | performance_triangles_generated = 0; 82 | } break; 83 | } 84 | }; 85 | void FlashPlayer::override_frame(String p_symbol, Variant p_value) { 86 | //ERR_FAIL_COND_MSG(resource.is_null(), "Can't override symbol without resource"); 87 | if (resource.is_null()) return; 88 | Ref symbol = resource->get_symbols().get(p_symbol, Ref()); 89 | if (symbol.is_null()) return; 90 | if (symbol->get_variation_idx() < 0) return; 91 | if (p_value.get_type() == Variant::NIL) { 92 | frame_overrides.set(symbol->get_variation_idx(), -1); 93 | queue_process(); 94 | } else if (p_value.get_type() == Variant::REAL || p_value.get_type() == Variant::INT) { 95 | frame_overrides.set(symbol->get_variation_idx(), p_value); 96 | queue_process(); 97 | } 98 | } 99 | void FlashPlayer::set_variant(String variant, Variant value) { 100 | if (value == Variant() || value == "[default]") { 101 | if(active_variants.has(variant)) active_variants.erase(variant); 102 | } else { 103 | active_variants[variant] = value; 104 | } 105 | 106 | if (!resource.is_valid()) return; 107 | Dictionary variants = resource->get_variants(); 108 | if (variants.has(variant)) { 109 | Dictionary symbols_by_variant = variants[variant]; 110 | if (symbols_by_variant.has(value)) { 111 | if (value == "[default]") { 112 | for (int i=0; i symbol = resource->get_symbols().get(symbols_by_variant.get_key_at_index(i), Variant()); 114 | if (symbol.is_null() || symbol->get_variation_idx() < 0) continue; 115 | frame_overrides.set(symbol->get_variation_idx(), -1); 116 | } 117 | } else { 118 | Dictionary frames_by_symbol = symbols_by_variant[value]; 119 | for (int i=0; i symbol = resource->get_symbols().get(token, Variant()); 122 | if (symbol.is_null() || symbol->get_variation_idx() < 0) continue; 123 | int frame = frames_by_symbol.get_value_at_index(i); 124 | frame_overrides.set(symbol->get_variation_idx(), frame); 125 | } 126 | } 127 | } 128 | } 129 | queue_process(); 130 | } 131 | String FlashPlayer::get_variant(String variant) const { 132 | return active_variants.has(variant) ? active_variants[variant] : "[default]"; 133 | } 134 | 135 | void FlashPlayer::set_clip(String clip, Variant value) { 136 | if (!resource.is_valid()) return; 137 | if (value == Variant() || value == "[default]") { 138 | if(clips_state.has(clip)) clips_state.erase(clip); 139 | if(active_clips.has(clip)) active_clips.erase(clip); 140 | } else { 141 | String *track_clip = active_clips.getptr(clip); 142 | if (track_clip == NULL || *track_clip == value) return; 143 | active_clips[clip] = value; 144 | Array timelines = resource->get_symbols().values(); 145 | for (int i=0; i tl = timelines[i]; 147 | if (clip != tl->get_clips_header()) continue; 148 | Vector2 clip_data = tl->get_clips()[value]; 149 | clips_state[clip] = Vector3(clip_data.x, clip_data.y, 0.0); 150 | break; 151 | } 152 | } 153 | tracks_dirty = true; 154 | queue_process(); 155 | } 156 | 157 | String FlashPlayer::get_clip(String clip) const { 158 | return active_clips.has(clip) ? active_clips[clip] : "[default]"; 159 | } 160 | 161 | float FlashPlayer::get_symbol_frame(FlashTimeline* p_symbol, float p_default) { 162 | if (p_symbol == NULL) { 163 | return p_default; 164 | } 165 | 166 | Vector3 *clip = clips_state.getptr(p_symbol->get_clips_header()); 167 | if (clip != NULL) { 168 | return clip->x + clip->z; 169 | } 170 | 171 | if (p_symbol->get_variation_idx() < 0) { 172 | return p_default; 173 | } 174 | 175 | float frame = frame_overrides[p_symbol->get_variation_idx()]; 176 | return frame < 0 ? p_default : frame; 177 | } 178 | 179 | Dictionary FlashPlayer::get_variants() const { 180 | if (!resource.is_valid()) return Dictionary(); 181 | return resource->get_variants(); 182 | } 183 | 184 | bool FlashPlayer::_set(const StringName &p_name, const Variant &p_value) { 185 | String n = p_name; 186 | if (n.begins_with("variants/")) { 187 | String variant = n.substr(strlen("variants/")); 188 | set_variant(variant, p_value); 189 | return true; 190 | } 191 | if (n.begins_with("clips/")) { 192 | String clip = n.substr(strlen("clips/")); 193 | set_clip(clip, p_value); 194 | return true; 195 | } 196 | return false; 197 | } 198 | 199 | bool FlashPlayer::_get(const StringName &p_name, Variant &r_ret) const { 200 | String n = p_name; 201 | if (n.begins_with("variants/")) { 202 | String variant = n.substr(strlen("variants/")); 203 | r_ret = get_variant(variant); 204 | return true; 205 | } else if (n.begins_with("clips/")) { 206 | String clip = n.substr(strlen("clips/")); 207 | r_ret = get_clip(clip); 208 | return true; 209 | } else if (p_name == "performance/triangles_drawn") { 210 | r_ret = performance_triangles_drawn; 211 | return true; 212 | } else if (p_name == "performance/triangles_generated") { 213 | r_ret = performance_triangles_generated; 214 | return true; 215 | } 216 | return false; 217 | } 218 | 219 | void FlashPlayer::_get_property_list(List *p_list) const { 220 | if (!resource.is_valid()) return; 221 | Dictionary variants = resource->get_variants(); 222 | for (int i=0; ipush_back(PropertyInfo(Variant::STRING, "variants/" + key, PROPERTY_HINT_ENUM, options_string)); 231 | } 232 | HashMap> clips; 233 | Array timelines = resource->get_symbols().values(); 234 | for (int i=0; i tl = timelines[i]; 236 | String clips_header = tl->get_clips_header(); 237 | if (clips_header == String()) continue; 238 | Array clip_names = tl->get_clips().keys(); 239 | for (int i=0; i clip_keys; 247 | clips.get_key_list(&clip_keys); 248 | for (List::Element *E = clip_keys.front(); E; E = E->next()) { 249 | String clips_key = E->get(); 250 | Vector timeline_clips = clips[clips_key]; 251 | timeline_clips.insert(0, "[default]"); 252 | p_list->push_back(PropertyInfo(Variant::STRING, "clips/" + clips_key, PROPERTY_HINT_ENUM, String(",").join(timeline_clips))); 253 | } 254 | } 255 | 256 | PoolStringArray FlashPlayer::get_clips_tracks() const { 257 | PoolStringArray result; 258 | Dictionary unique; 259 | if (!resource.is_valid()) return result; 260 | Array timelines = resource->get_symbols().values(); 261 | for (int i=0; i tl = timelines[i]; 263 | String clips_track = tl->get_clips_header(); 264 | if (clips_track == String()) continue; 265 | if (!unique.has(clips_track)) { 266 | unique[clips_track] = true; 267 | result.push_back(clips_track); 268 | } 269 | } 270 | return result; 271 | } 272 | 273 | PoolStringArray FlashPlayer::get_clips_for_track(const String &track) const { 274 | PoolStringArray result; 275 | Dictionary cache; 276 | if (!resource.is_valid()) return result; 277 | Array timelines = resource->get_symbols().values(); 278 | for (int i=0; i tl = timelines[i]; 280 | String clips_track = tl->get_clips_header(); 281 | if (clips_track != track) continue; 282 | Array clip_names = tl->get_clips().keys(); 283 | for (int i=0; iget_symbols().values(); 303 | for (int i=0; i symbol = symbols[i]; 305 | if (symbol->get_local_path().find("/") >= 0) continue; 306 | symbols_hint += "," + symbol->get_token(); 307 | } 308 | } 309 | prop.hint_string = symbols_hint; 310 | } 311 | 312 | if (prop.name == "active_clip") { 313 | String clips_hint = "[full]"; 314 | if (active_symbol.is_valid()) { 315 | Array clips = active_symbol->get_clips().keys(); 316 | if (clips.size() > 0) { 317 | prop.usage = PROPERTY_USAGE_DEFAULT; 318 | } else { 319 | prop.usage = PROPERTY_USAGE_NOEDITOR; 320 | return; 321 | } 322 | 323 | clips.sort_custom((FlashPlayer*)this, "_sort_clips"); 324 | for (int i=0; iget_clips()[a]; 342 | Vector2 db = active_symbol->get_clips()[b]; 343 | return da.x < db.x; 344 | } 345 | 346 | void FlashPlayer::set_resource(const Ref &doc) { 347 | if (doc != resource) active_symbol_name = "[document]"; 348 | resource = doc; 349 | frame = 0; 350 | processed_frame = -1; 351 | playback_start = 0; 352 | playback_end = 0; 353 | frame_overrides.clear(); 354 | active_variants.clear(); 355 | if (resource.is_valid()) { 356 | frame_overrides.resize(resource->get_variated_symbols_count()); 357 | for (int i=0; iget_main_timeline(); 359 | if (active_symbol.is_valid()) 360 | playback_end = active_symbol->get_duration(); 361 | VisualServer::get_singleton()->material_set_param(flash_material, "ATLAS_SIZE", resource->get_atlas_size()); 362 | VisualServer::get_singleton()->material_set_param(flash_material, "ATLAS", resource->get_atlas()); 363 | } else { 364 | frame_overrides.resize(0); 365 | } 366 | queue_process(); 367 | _change_notify(); 368 | emit_signal("resource_changed"); 369 | } 370 | 371 | Ref FlashPlayer::get_resource() const { 372 | return resource; 373 | } 374 | 375 | void FlashPlayer::_bind_methods() { 376 | ClassDB::bind_method(D_METHOD("override_frame", "symbol", "frame"), &FlashPlayer::override_frame); 377 | ClassDB::bind_method(D_METHOD("set_playing", "playing"), &FlashPlayer::set_playing); 378 | ClassDB::bind_method(D_METHOD("is_playing"), &FlashPlayer::is_playing); 379 | ClassDB::bind_method(D_METHOD("set_loop", "loop"), &FlashPlayer::set_loop); 380 | ClassDB::bind_method(D_METHOD("is_loop"), &FlashPlayer::is_loop); 381 | ClassDB::bind_method(D_METHOD("set_frame_rate", "frame_rate"), &FlashPlayer::set_frame_rate); 382 | ClassDB::bind_method(D_METHOD("get_frame_rate"), &FlashPlayer::get_frame_rate); 383 | ClassDB::bind_method(D_METHOD("set_frame", "frame"), &FlashPlayer::set_frame); 384 | ClassDB::bind_method(D_METHOD("get_frame"), &FlashPlayer::get_frame); 385 | ClassDB::bind_method(D_METHOD("set_variant", "variant", "value"), &FlashPlayer::set_variant); 386 | ClassDB::bind_method(D_METHOD("get_variant", "variant"), &FlashPlayer::get_variant); 387 | ClassDB::bind_method(D_METHOD("get_variants"), &FlashPlayer::get_variants); 388 | ClassDB::bind_method(D_METHOD("set_resource", "resource"), &FlashPlayer::set_resource); 389 | ClassDB::bind_method(D_METHOD("get_resource"), &FlashPlayer::get_resource); 390 | ClassDB::bind_method(D_METHOD("get_duration"), &FlashPlayer::get_duration, DEFVAL(String()), DEFVAL(String())); 391 | ClassDB::bind_method(D_METHOD("set_active_symbol", "active_symbol"), &FlashPlayer::set_active_symbol); 392 | ClassDB::bind_method(D_METHOD("get_active_symbol"), &FlashPlayer::get_active_symbol); 393 | ClassDB::bind_method(D_METHOD("set_active_clip", "active_clip"), &FlashPlayer::set_active_clip); 394 | ClassDB::bind_method(D_METHOD("get_active_clip"), &FlashPlayer::get_active_clip); 395 | 396 | ClassDB::bind_method(D_METHOD("_animation_process"), &FlashPlayer::_animation_process); 397 | 398 | ClassDB::bind_method(D_METHOD("_sort_clips"), &FlashPlayer::_sort_clips); 399 | 400 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, ""), "set_playing", "is_playing"); 401 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, ""), "set_loop", "is_loop"); 402 | ADD_PROPERTY(PropertyInfo(Variant::REAL, "frame_rate", PROPERTY_HINT_NONE, ""), "set_frame_rate", "get_frame_rate"); 403 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "FlashDocument"), "set_resource", "get_resource"); 404 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "active_symbol", PROPERTY_HINT_ENUM, ""), "set_active_symbol", "get_active_symbol"); 405 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "active_clip", PROPERTY_HINT_ENUM, ""), "set_active_clip", "get_active_clip"); 406 | 407 | ADD_SIGNAL(MethodInfo("resource_changed")); 408 | ADD_SIGNAL(MethodInfo("animation_completed")); 409 | ADD_SIGNAL(MethodInfo("animation_event", PropertyInfo(Variant::STRING, "name"))); 410 | 411 | // compatiblity 412 | ClassDB::bind_method(D_METHOD("set_active_label", "active_label"), &FlashPlayer::set_active_clip); 413 | ClassDB::bind_method(D_METHOD("get_active_label"), &FlashPlayer::get_active_clip); 414 | ClassDB::bind_method(D_METHOD("set_active_timeline", "active_timeline"), &FlashPlayer::set_active_symbol); 415 | ClassDB::bind_method(D_METHOD("get_active_timeline"), &FlashPlayer::get_active_symbol); 416 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "active_label", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_NOEDITOR), "set_active_label", "get_active_label"); 417 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "active_timeline", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_NOEDITOR), "set_active_timeline", "get_active_timeline"); 418 | 419 | } 420 | 421 | float FlashPlayer::get_duration(String p_symbol, String p_clip) { 422 | if (!resource.is_valid()) return 0; 423 | return resource->get_duration(p_symbol, p_clip); 424 | } 425 | 426 | void FlashPlayer::set_active_symbol(String p_value) { 427 | if (p_value == "[document]") p_value = ""; 428 | if (active_symbol_name == p_value) return; 429 | active_symbol_name = p_value; 430 | active_clip = ""; 431 | frame = 0; 432 | processed_frame = -1; 433 | playback_start = 0; 434 | if (resource.is_valid() && resource->get_symbols().has(active_symbol_name)) { 435 | active_symbol = resource->get_symbols()[active_symbol_name]; 436 | } else if (resource.is_valid()){ 437 | active_symbol = resource->get_main_timeline(); 438 | } else { 439 | active_symbol_name = ""; 440 | } 441 | 442 | if (active_symbol.is_valid()) { 443 | playback_end = active_symbol->get_duration(); 444 | } else { 445 | playback_end = 0; 446 | } 447 | queue_process(); 448 | _change_notify(); 449 | } 450 | String FlashPlayer::get_active_symbol() const { 451 | return active_symbol_name == String() ? "[document]" : active_symbol_name; 452 | } 453 | 454 | void FlashPlayer::set_active_clip(String p_value) { 455 | if (p_value == "[full]") p_value = ""; 456 | if (active_clip == p_value) return; 457 | active_clip = p_value; 458 | if (active_symbol.is_valid()) { 459 | Dictionary clips = active_symbol->get_clips(); 460 | if (clips.has(p_value)) { 461 | Vector2 clip = clips[p_value]; 462 | playback_start = clip.x; 463 | playback_end = clip.y; 464 | 465 | } else { 466 | playback_start = 0; 467 | playback_end = active_symbol->get_duration(); 468 | } 469 | frame = playback_start; 470 | } else { 471 | active_clip = ""; 472 | } 473 | queue_process(); 474 | update(); 475 | } 476 | 477 | String FlashPlayer::get_active_clip() const { 478 | return active_clip == String() ? "[full]" : active_clip; 479 | } 480 | 481 | PoolStringArray FlashPlayer::get_symbols() const { 482 | PoolStringArray result; 483 | if (!resource.is_valid()) { 484 | return result; 485 | } 486 | Array symbols = resource->get_symbols().values(); 487 | for (int i=0; i symbol = symbols[i]; 489 | if (symbol->get_local_path().find("/") >= 0) continue; 490 | result.push_back(symbol->get_token()); 491 | } 492 | return result; 493 | } 494 | 495 | PoolStringArray FlashPlayer::get_clips(String p_symbol) const { 496 | PoolStringArray result; 497 | if (!resource.is_valid()) return result; 498 | Ref symbol; 499 | if (p_symbol == String()) { 500 | symbol = active_symbol; 501 | } else { 502 | Array symbols = resource->get_symbols().values(); 503 | for (int i=0; i s = symbols[i]; 505 | if (s->get_local_path().find("/") >= 0) continue; 506 | if (s->get_token() == p_symbol) { 507 | symbol = s; 508 | break; 509 | } 510 | } 511 | } 512 | if (!symbol.is_valid()) return result; 513 | Array clips = symbol->get_clips().keys(); 514 | for (int i=0; ianimation_process(this, frame, queued_delta); 554 | update(); 555 | performance_triangles_generated = indices.size() / 3; 556 | 557 | for (List::Element *E = events.front(); E; E = E->next()) { 558 | // always emit user events in deferred mode 559 | // to prevent recursive `animation_process` invocation 560 | #ifndef TOOLS_ENABLED 561 | call_deferred("emit_signal", "animation_event", E->get()); 562 | #else 563 | if (!Engine::get_singleton()->is_editor_hint()) 564 | call_deferred("emit_signal", "animation_event", E->get()); 565 | #endif 566 | } 567 | animation_process_queued = false; 568 | queued_delta = 0.0; 569 | tracks_dirty = false; 570 | } 571 | 572 | void FlashPlayer::advance(float p_time, bool p_seek, bool advance_all_frames) { 573 | if (!active_symbol.is_valid()) return; 574 | bool animation_completed = false; 575 | float delta = p_time*frame_rate; 576 | if (p_seek) { 577 | frame = playback_start + delta; 578 | } else { 579 | frame += delta; 580 | } 581 | 582 | 583 | if (advance_all_frames) { 584 | List clip_keys; 585 | clips_state.get_key_list(&clip_keys); 586 | for (List::Element *E = clip_keys.front(); E; E = E->next()) { 587 | String clips_key = E->get(); 588 | Vector3 *clip = clips_state.getptr(clips_key); 589 | if (clip == NULL) { 590 | continue; 591 | } 592 | if (p_seek) { 593 | clip->z = delta; 594 | } else { 595 | clip->z += delta; 596 | } 597 | float duration = clip->y - clip->x; 598 | if (duration <= 0) { 599 | clip->z = 0.0; 600 | } else if (loop) while (clip->z > duration) { 601 | clip->z -= duration; 602 | } 603 | } 604 | } 605 | 606 | if (p_seek) { 607 | delta = 0.0; 608 | } 609 | 610 | float duration = playback_end - playback_start; 611 | if (!loop && frame > playback_end) { 612 | animation_completed = true; 613 | frame = playback_end - 0.0001; 614 | } else if (loop && duration >= 0) while (frame > playback_end) { 615 | animation_completed = true; 616 | frame -= playback_end - playback_start; 617 | } 618 | queue_process(delta); 619 | if (animation_completed) { 620 | #ifndef TOOLS_ENABLED 621 | call_deferred("emit_signal", "animation_completed"); 622 | #else 623 | if (!Engine::get_singleton()->is_editor_hint()) 624 | call_deferred("emit_signal", "animation_completed"); 625 | #endif 626 | 627 | } 628 | } 629 | 630 | void FlashPlayer::advance_clip_for_track(const String &p_track, const String &p_clip, float p_time, bool p_seek, float *r_elapsed, float *r_remaining) { 631 | if (!resource.is_valid()) return; 632 | 633 | if (p_clip == Variant() || p_clip == "[default]") { 634 | if(clips_state.has(p_track)) clips_state.erase(p_track); 635 | if(active_clips.has(p_track)) active_clips.erase(p_track); 636 | if (r_elapsed != NULL) *r_elapsed = 0.0; 637 | if (r_remaining != NULL) *r_remaining = 0.0; 638 | return; 639 | } 640 | 641 | float delta = p_time*frame_rate; 642 | String *current_clip = active_clips.getptr(p_track); 643 | if (current_clip == NULL || *current_clip != p_clip) { 644 | active_clips[p_track] = p_clip; 645 | Array timelines = resource->get_symbols().values(); 646 | for (int i=0; i tl = timelines[i]; 648 | if (p_track != tl->get_clips_header()) continue; 649 | Vector2 clip_data = tl->get_clips()[p_clip]; 650 | clips_state[p_track] = Vector3(clip_data.x, clip_data.y, 0.0); 651 | break; 652 | } 653 | } 654 | 655 | Vector3 *current_state = clips_state.getptr(p_track); 656 | if (current_state == NULL) { 657 | if (r_elapsed != NULL) *r_elapsed = 0.0; 658 | if (r_remaining != NULL) *r_remaining = 0.0; 659 | return; 660 | } 661 | float duration = current_state->y - current_state->x; 662 | if (p_seek) { 663 | if (current_state->z != delta) { 664 | tracks_dirty = true; 665 | current_state->z = delta; 666 | } 667 | } else { 668 | float next_state = MIN(duration, current_state->z + delta); 669 | if (duration <= 0.0) { 670 | next_state = 0.0; 671 | } else if (loop) while (next_state > duration) { 672 | next_state -= duration; 673 | } 674 | if (next_state != current_state->z) { 675 | tracks_dirty = true; 676 | current_state->z = next_state; 677 | } 678 | } 679 | queue_process(); 680 | if (r_elapsed != NULL) *r_elapsed = MIN(duration, current_state->z) / frame_rate; 681 | if (r_remaining != NULL) *r_remaining = (duration - current_state->z) / frame_rate; 682 | } 683 | 684 | void FlashPlayer::add_polygon(Vector p_points, Vector p_colors, Vector p_uvs, int p_texture_idx) { 685 | Vector local_indices = Geometry::triangulate_polygon(p_points); 686 | for (int i=0; ilock(); 710 | Vector2i pos = Vector2i(0, 0); 711 | Transform2D scale; 712 | //scale.scale(Vector2(2.0, 2.0)); 713 | 714 | Transform2D glob = get_viewport_transform() * get_global_transform_with_canvas(); 715 | for (List::Element *E = clipping_cache.front(); E; E = E->next()) { 716 | FlashMaskItem item = E->get(); 717 | Transform2D tr = (glob * item.transform * scale).affine_inverse(); 718 | Color xy = Color(tr[0].x, tr[0].y, tr[1].x, tr[1].y); 719 | Color origin = Color(tr[2].x, tr[2].y, item.texture_idx, 0); 720 | Color region = Color( 721 | item.texture_region.position.x, 722 | item.texture_region.position.y, 723 | item.texture_region.size.width, 724 | item.texture_region.size.height 725 | ); 726 | clipping_data->set_pixel(pos.x, pos.y, xy); 727 | clipping_data->set_pixel(pos.x+1, pos.y, origin); 728 | clipping_data->set_pixel(pos.x+2, pos.y, region); 729 | pos.x += 4; 730 | if (pos.x >= 32) { 731 | pos.x = 0; 732 | pos.y += 1; 733 | if (pos.y >= 32) break; 734 | } 735 | } 736 | clipping_data->unlock(); 737 | clipping_texture->set_data(clipping_data); 738 | } 739 | 740 | void FlashPlayer::mask_begin(int mask_id) { 741 | if (!current_mask) current_mask = mask_id; 742 | masks.set(current_mask, List()); 743 | mask_stack.push_back(mask_id); 744 | } 745 | void FlashPlayer::mask_end(int mask_id) { 746 | if (current_mask == mask_id) { 747 | mask_stack.pop_front(); 748 | if (mask_stack.size() > 0) { 749 | current_mask = mask_stack.back()->get(); 750 | } else { 751 | current_mask = 0; 752 | } 753 | } 754 | } 755 | bool FlashPlayer::is_masking() { 756 | return current_mask > 0; 757 | } 758 | void FlashPlayer::mask_add(Transform2D p_transform, Rect2i p_texture_region, int p_texture_idx) { 759 | FlashMaskItem item; 760 | item.texture_idx = p_texture_idx; 761 | item.texture_region = p_texture_region; 762 | item.transform = p_transform; 763 | if (!masks.has(current_mask)) { 764 | masks.set(current_mask, List()); 765 | } 766 | masks[current_mask].push_back(item); 767 | } 768 | void FlashPlayer::clip_begin(int mask_id) { 769 | if (!masks.has(mask_id)) { 770 | print_line("No flash mask found, id=" + itos(mask_id)); 771 | return; 772 | } 773 | for (List::Element *E = clipping_items.front(); E; E = E->next()) { 774 | clipping_cache.push_back(E->get()); 775 | } 776 | for (List::Element *E = masks[mask_id].front(); E; E = E->next()) { 777 | clipping_items.push_back(E->get()); 778 | } 779 | } 780 | void FlashPlayer::clip_end(int mask_id) { 781 | if (!masks.has(mask_id)) return; 782 | for (List::Element *E = clipping_items.front(); E; E = E->next()) { 783 | clipping_cache.push_back(E->get()); 784 | } 785 | for (List::Element *E = masks[mask_id].front(); E; E = E->next()) { 786 | clipping_items.pop_back(); 787 | } 788 | } 789 | 790 | FlashPlayer::~FlashPlayer() { 791 | VisualServer *vs = VisualServer::get_singleton(); 792 | vs->free(flash_material); 793 | vs->free(mesh); 794 | } 795 | 796 | FlashPlayer::FlashPlayer() { 797 | frame = 0; 798 | frame_rate = 30; 799 | queued_delta = 0.0; 800 | playing = false; 801 | playback_start = 0; 802 | playback_end = 0; 803 | active_symbol_name = "[document]"; 804 | active_clip = ""; 805 | loop = false; 806 | tracks_dirty = true; 807 | animation_process_queued = false; 808 | 809 | processed_frame = -1; 810 | current_mask = 0; 811 | cliping_depth = 0; 812 | 813 | performance_triangles_generated = 0; 814 | performance_triangles_drawn = 0; 815 | 816 | VisualServer *vs = VisualServer::get_singleton(); 817 | flash_material = vs->material_create(); 818 | mesh = vs->mesh_create(); 819 | if (flash_shader == RID()) { 820 | print_line("creating new shader"); 821 | flash_shader = vs->shader_create(); 822 | vs->shader_set_code(flash_shader, 823 | "shader_type canvas_item;\n" 824 | 825 | "uniform sampler2DArray ATLAS;\n" 826 | "uniform sampler2D CLIPPING_TEXTURE;\n" 827 | "uniform vec2 ATLAS_SIZE;\n" 828 | "varying float CLIPPING_SIZE;\n" 829 | "varying float CLIPPING_IDX[4];" 830 | "varying vec4 CLIPPING_UV[4];\n" 831 | "varying float TEX_IDX;\n" 832 | 833 | "void vertex() {\n" 834 | " float clipping_size_with_tex_idx = 0.0;\n" 835 | " float clipping_id = 0.0;\n" 836 | " UV.x = 2.0 * modf(UV.x, clipping_id);\n" 837 | " UV.y = 2.0 * modf(UV.y, clipping_size_with_tex_idx);\n" 838 | " TEX_IDX = float(int(clipping_size_with_tex_idx) & 255);\n" 839 | " float clipping_size = float(int(clipping_size_with_tex_idx) >> 8);\n" 840 | " CLIPPING_SIZE = min(clipping_size, 4.0);\n" 841 | " for (int i=0; i 0) masked = 0.0;\n" 867 | " for (int i=0; i= 0.0 && CLIPPING_UV[i].x < 1.0 && CLIPPING_UV[i].y >= 0.0 && CLIPPING_UV[i].y < 1.0) {\n" 869 | " vec4 mask = textureLod(ATLAS, vec3(CLIPPING_UV[i].zw, CLIPPING_IDX[i]), 0.0);\n" 870 | " if (mask.a >= 1.0) {\n" 871 | " masked = 1.0;\n" 872 | " break;\n" 873 | " }\n" 874 | " masked = max(masked, mask.a);\n" 875 | " }\n" 876 | " }\n" 877 | " if (masked > 0.0) {\n" 878 | " vec4 add;\n" 879 | " vec4 c = texture(ATLAS, vec3(UV, TEX_IDX));\n" 880 | " vec4 mult = 2.0*modf(COLOR, add);\n" 881 | " COLOR = clamp(abs(c * mult) + add / 255.0, vec4(0.0), vec4(1.0));\n" 882 | " if (c.a <= 0.0) {\n" 883 | " COLOR.a = 0.0;\n" 884 | " } else {\n" 885 | " COLOR.a = min(COLOR.a, masked);\n" 886 | " }\n" 887 | " } else {\n" 888 | " COLOR = vec4(0.0);\n" 889 | " }\n" 890 | "}\n" 891 | ); 892 | } 893 | vs->material_set_shader(flash_material, flash_shader); 894 | vs->canvas_item_set_material(get_canvas_item(), flash_material); 895 | } -------------------------------------------------------------------------------- /flash_player.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #ifndef FLASH_PLAYER_H 24 | #define FLASH_PLAYER_H 25 | 26 | #include 27 | 28 | #include "flash_resources.h" 29 | 30 | class FlashDocument; 31 | class FlashTimeline; 32 | 33 | struct FlashMaskItem { 34 | Transform2D transform; 35 | Rect2 texture_region; 36 | int texture_idx; 37 | }; 38 | 39 | class FlashPlayer: public Node2D { 40 | GDCLASS(FlashPlayer, Node2D); 41 | 42 | // renderer part 43 | float frame; 44 | float frame_rate; 45 | bool playing; 46 | float playback_start; 47 | float playback_end; 48 | bool tracks_dirty; 49 | float queued_delta; 50 | bool animation_process_queued; 51 | Ref resource; 52 | String active_symbol_name; 53 | Ref active_symbol; 54 | String active_clip; 55 | bool loop; 56 | RID flash_material; 57 | RID mesh; 58 | static RID flash_shader; 59 | 60 | // batcher part 61 | float processed_frame; 62 | int cliping_depth; 63 | Vector points; 64 | Vector uvs; 65 | Vector colors; 66 | Vector indices; 67 | List events; 68 | 69 | HashMap clips_state; 70 | HashMap active_clips; 71 | Ref clipping_data; 72 | Ref clipping_texture; 73 | HashMap> masks; 74 | List mask_stack; 75 | Vector frame_overrides; 76 | HashMap active_variants; 77 | List clipping_cache; 78 | List clipping_items; 79 | int current_mask; 80 | 81 | 82 | int performance_triangles_drawn; 83 | int performance_triangles_generated; 84 | 85 | protected: 86 | void _notification(int p_what); 87 | bool _set(const StringName &p_name, const Variant &p_value); 88 | bool _get(const StringName &p_name, Variant &r_ret) const; 89 | void _get_property_list(List *p_list) const; 90 | virtual void _validate_property(PropertyInfo &prop) const; 91 | static void _bind_methods(); 92 | bool _sort_clips(Variant a, Variant b) const; 93 | 94 | public: 95 | FlashPlayer(); 96 | ~FlashPlayer(); 97 | 98 | float get_frame() const { return frame; } 99 | void set_frame(float p_frame) { frame = p_frame; update(); } 100 | void override_frame(String p_symbol, Variant p_frame); 101 | void set_variant(String key, Variant value); 102 | String get_variant(String key) const; 103 | void set_clip(String header, Variant value); 104 | String get_clip(String header) const; 105 | PoolStringArray get_clips_tracks() const; 106 | PoolStringArray get_clips_for_track(const String &track) const; 107 | float get_clip_duration(const String &track, const String &clip) const; 108 | Dictionary get_variants() const; 109 | float get_symbol_frame(FlashTimeline* symbol, float p_default); 110 | float get_frame_rate() const { return frame_rate; } 111 | void set_frame_rate(float p_frame_rate) { frame_rate = p_frame_rate; } 112 | bool is_playing() const { return playing; } 113 | void set_playing(bool p_playing) { playing = p_playing; } 114 | bool is_loop() const { return loop; } 115 | void set_loop(bool p_loop) { loop = p_loop; } 116 | Ref get_resource() const; 117 | void set_resource(const Ref &doc); 118 | float get_duration(String symbol=String(), String label=String()); 119 | String get_active_symbol() const; 120 | void set_active_symbol(String p_symbol); 121 | String get_active_clip() const; 122 | void set_active_clip(String p_clip); 123 | PoolStringArray get_symbols() const; 124 | PoolStringArray get_clips(String p_symbol=String()) const; 125 | 126 | // batcher part 127 | void queue_animation_process(); 128 | void queue_process(float delta=0.0); 129 | void _animation_process(); 130 | void advance(float p_delta, bool p_skip=false, bool advance_all_tracks=false); 131 | void advance_clip_for_track(const String &p_track, const String &p_clip, float delta=0.0, bool p_skip=false, float *r_elapsed=NULL, float *r_ramaining=NULL); 132 | void update_clipping_data(); 133 | void add_polygon(Vector p_points, Vector p_colors, Vector p_uvs, int p_texture_idx); 134 | void queue_animation_event(const String &p_name, bool p_reversed=false); 135 | 136 | bool is_masking(); 137 | void mask_begin(int layer); 138 | void mask_add(Transform2D p_transform, Rect2i p_texture_region, int p_texture_idx); 139 | void mask_end(int layer); 140 | 141 | void clip_begin(int layer); 142 | void clip_end(int layer); 143 | }; 144 | 145 | 146 | #endif -------------------------------------------------------------------------------- /flash_resources.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "flash_resources.h" 24 | #include "core/io/compression.h" 25 | #include "core/io/marshalls.h" 26 | 27 | FlashDocument *FlashElement::get_document() const { 28 | return document; 29 | } 30 | void FlashElement::set_document(FlashDocument *p_document) { 31 | document = p_document; 32 | } 33 | FlashElement *FlashElement::get_parent() const { 34 | return parent; 35 | } 36 | void FlashElement::set_parent(FlashElement *p_parent) { 37 | parent = p_parent; 38 | } 39 | template T* FlashElement::find_parent() const { 40 | FlashElement *ptr = parent; 41 | while (ptr != NULL) { 42 | T* obj = Object::cast_to(ptr); 43 | if (obj != NULL) { 44 | return obj; 45 | } 46 | ptr = ptr->parent; 47 | } 48 | return NULL; 49 | } 50 | void FlashElement::_bind_methods() { 51 | ClassDB::bind_method(D_METHOD("get_eid"), &FlashElement::get_eid); 52 | ClassDB::bind_method(D_METHOD("set_eid", "path"), &FlashElement::set_eid); 53 | 54 | ADD_PROPERTY(PropertyInfo(Variant::INT, "eid", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_eid", "get_eid"); 55 | } 56 | void FlashElement::setup(FlashDocument *p_document, FlashElement *p_parent) { 57 | document = p_document; 58 | parent = p_parent; 59 | } 60 | 61 | template Ref FlashElement::add_child(Ref parser, List< Ref > *elements) { 62 | Ref elem = document->element(this); 63 | if (elements != NULL) elements->push_back(elem); 64 | Error err = elem->parse(parser); 65 | if (err == Error::ERR_SKIP) { 66 | if (elements != NULL) elements->erase(elem); 67 | elem.unref(); 68 | } 69 | return elem; 70 | } 71 | 72 | Color FlashElement::parse_color(const String &p_color) const { 73 | return Color::html(p_color.substr(1, p_color.length())); 74 | } 75 | 76 | FlashColorEffect FlashElement::parse_color_effect(Ref xml) const { 77 | FlashColorEffect color_effect; 78 | if (xml->has_attribute("tintColor") || xml->has_attribute("tintMultiplier")) { 79 | Color tint = xml->has_attribute("tintColor") ? 80 | parse_color(xml->get_attribute_value_safe("tintColor")) : Color(0, 0, 0, 1); 81 | float amount = xml->has_attribute("tintMultiplier") ? 82 | xml->get_attribute_value_safe("tintMultiplier").to_float() : 0.0; 83 | 84 | color_effect.add.r = tint.r * amount; 85 | color_effect.add.g = tint.g * amount; 86 | color_effect.add.b = tint.b * amount; 87 | color_effect.mult.r = 1 - amount; 88 | color_effect.mult.g = 1 - amount; 89 | color_effect.mult.b = 1 - amount; 90 | } else if ( 91 | xml->has_attribute("redMultiplier") 92 | || xml->has_attribute("greenMultiplier") 93 | || xml->has_attribute("blueMultiplier") 94 | || xml->has_attribute("alphaMultiplier") 95 | || xml->has_attribute("redOffset") 96 | || xml->has_attribute("greenOffset") 97 | || xml->has_attribute("blueOffset") 98 | || xml->has_attribute("alphaOffset") 99 | ) { 100 | color_effect.mult.r = xml->has_attribute("redMultiplier") ? xml->get_attribute_value_safe("redMultiplier").to_float() : 1.0; 101 | color_effect.mult.g = xml->has_attribute("greenMultiplier") ? xml->get_attribute_value_safe("greenMultiplier").to_float() : 1.0; 102 | color_effect.mult.b = xml->has_attribute("blueMultiplier") ? xml->get_attribute_value_safe("blueMultiplier").to_float() : 1.0; 103 | color_effect.mult.a = xml->has_attribute("alphaMultiplier") ? xml->get_attribute_value_safe("alphaMultiplier").to_float() : 1.0; 104 | color_effect.add.r = xml->has_attribute("greenOffset") ? xml->get_attribute_value_safe("redOffset").to_float()/255.0 : 0.0; 105 | color_effect.add.g = xml->has_attribute("greenOffset") ? xml->get_attribute_value_safe("greenOffset").to_float()/255.0 : 0.0; 106 | color_effect.add.b = xml->has_attribute("blueOffset") ? xml->get_attribute_value_safe("blueOffset").to_float()/255.0 : 0.0; 107 | color_effect.add.a = xml->has_attribute("alphaOffset") ? xml->get_attribute_value_safe("alphaOffset").to_float()/255.0 : 0.0; 108 | } else if (xml->has_attribute("alphaMultiplier")) { 109 | color_effect.mult.a = xml->get_attribute_value_safe("alphaMultiplier").to_float(); 110 | } else if (xml->has_attribute("brightness")) { 111 | float b = xml->get_attribute_value_safe("brightness").to_float(); 112 | if (b < 0) { 113 | color_effect.mult.r = 1 + b; 114 | color_effect.mult.g = 1 + b; 115 | color_effect.mult.b = 1 + b; 116 | } else { 117 | color_effect.mult.r = 1 - b; 118 | color_effect.mult.g = 1 - b; 119 | color_effect.mult.b = 1 - b; 120 | color_effect.add.r = b; 121 | color_effect.add.g = b; 122 | color_effect.add.b = b; 123 | } 124 | } 125 | return color_effect; 126 | } 127 | 128 | Transform2D FlashElement::parse_transform(Ref xml) { 129 | ERR_FAIL_COND_V_MSG(xml->get_node_type() != XMLParser::NODE_ELEMENT, Transform2D(), "Not matrix node"); 130 | ERR_FAIL_COND_V_MSG(xml->get_node_name() != "Matrix", Transform2D(), "Not Matrix node"); 131 | float tx = 0, ty = 0, a = 1, b = 0, c = 0, d = 1; 132 | if (xml->has_attribute("tx")) 133 | tx = xml->get_attribute_value_safe("tx").to_float(); 134 | if (xml->has_attribute("ty")) 135 | ty = xml->get_attribute_value_safe("ty").to_float(); 136 | if (xml->has_attribute("a")) 137 | a = xml->get_attribute_value_safe("a").to_float(); 138 | if (xml->has_attribute("b")) 139 | b = xml->get_attribute_value_safe("b").to_float(); 140 | if (xml->has_attribute("c")) 141 | c = xml->get_attribute_value_safe("c").to_float(); 142 | if (xml->has_attribute("d")) 143 | d = xml->get_attribute_value_safe("d").to_float(); 144 | return Transform2D(a, b, c, d, tx, ty); 145 | } 146 | 147 | Error FlashElement::parse(Ref xml) { 148 | return Error::ERR_METHOD_NOT_FOUND; 149 | } 150 | void FlashDocument::_bind_methods() { 151 | ClassDB::bind_method(D_METHOD("load_file", "path"), &FlashDocument::load_file); 152 | ClassDB::bind_method(D_METHOD("get_atlas"), &FlashDocument::get_atlas); 153 | ClassDB::bind_method(D_METHOD("set_atlas", "atlas"), &FlashDocument::set_atlas); 154 | ClassDB::bind_method(D_METHOD("get_symbols"), &FlashDocument::get_symbols); 155 | ClassDB::bind_method(D_METHOD("set_symbols", "symbols"), &FlashDocument::set_symbols); 156 | ClassDB::bind_method(D_METHOD("get_bitmaps"), &FlashDocument::get_bitmaps); 157 | ClassDB::bind_method(D_METHOD("set_bitmaps", "bitmaps"), &FlashDocument::set_bitmaps); 158 | ClassDB::bind_method(D_METHOD("get_timelines"), &FlashDocument::get_timelines); 159 | ClassDB::bind_method(D_METHOD("set_timelines", "timelines"), &FlashDocument::set_timelines); 160 | ClassDB::bind_method(D_METHOD("get_duration"), &FlashDocument::get_duration, DEFVAL(String()), DEFVAL(String())); 161 | ClassDB::bind_method(D_METHOD("get_variants"), &FlashDocument::get_variants); 162 | 163 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas", PROPERTY_HINT_RESOURCE_TYPE, "TextureArray", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_atlas", "get_atlas"); 164 | ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "symbols", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_symbols", "get_symbols"); 165 | ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "bitmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_bitmaps", "get_bitmaps"); 166 | ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "timelines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_timelines", "get_timelines"); 167 | } 168 | template Ref FlashDocument::element(FlashElement *parent) { 169 | Ref elem; elem.instance(); 170 | elem->set_eid(last_eid++); 171 | elem->set_parent(parent); 172 | elem->set_document(this); 173 | return elem; 174 | } 175 | String FlashDocument::invalid_character = ". : @ / \" ' [ ]"; 176 | String FlashDocument::validate_token(String token) { 177 | Vector chars = FlashDocument::invalid_character.split(" "); 178 | for (int i = 0; i < chars.size(); i++) { 179 | token = token.replace(chars[i], " "); 180 | } 181 | return token; 182 | } 183 | Array FlashDocument::get_timelines() { 184 | Array l; 185 | for (List>::Element *E = timelines.front(); E; E = E->next()) { 186 | l.push_back(E->get()); 187 | } 188 | return l; 189 | } 190 | void FlashDocument::set_timelines(Array p_timelines) { 191 | timelines.clear(); 192 | for (int i=0; i timeline = p_timelines[i]; 194 | if (timeline.is_valid()) { 195 | timelines.push_back(timeline); 196 | } 197 | } 198 | setup(this, NULL); 199 | } 200 | float FlashDocument::get_duration(String timeline, String label) { 201 | Ref tl = get_main_timeline(); 202 | if (timeline != String() && symbols.has(timeline)) tl = symbols[timeline]; 203 | if (label == String() || !tl->get_clips().has(label)) return tl->get_duration(); 204 | Vector2 lb = tl->get_clips()[label]; 205 | return lb.y - lb.x; 206 | } 207 | Dictionary FlashDocument::get_variants() const { 208 | return variants; 209 | } 210 | void FlashDocument::cache_variants() { 211 | Set variated_symbols; 212 | for (int i=0; i timeline = symbols.get_value_at_index(i); 214 | String token = timeline->get_token(); 215 | for (List>::Element *L = timeline->layers.front(); L; L = L->next()) { 216 | Ref layer = L->get(); 217 | for (List>::Element *F = layer->frames.front(); F; F = F->next()) { 218 | Ref frame = F->get(); 219 | if (frame->label_type == "anchor") { 220 | Dictionary variants_by_layer; 221 | if (variants.has(layer->get_layer_name())) { 222 | variants_by_layer = variants[layer->get_layer_name()]; 223 | } else { 224 | variants[layer->get_layer_name()] = variants_by_layer; 225 | } 226 | Dictionary symbols_by_variant; 227 | if (variants_by_layer.has(frame->frame_name)) { 228 | symbols_by_variant = variants_by_layer[frame->frame_name]; 229 | } else { 230 | variants_by_layer[frame->frame_name] = symbols_by_variant; 231 | } 232 | symbols_by_variant[token] = frame->index; 233 | variated_symbols.insert(token); 234 | } 235 | } 236 | } 237 | } 238 | variated_symbols_count = variated_symbols.size(); 239 | int variant_idx = 0; 240 | for (Set::Element *E = variated_symbols.front(); E != NULL; E = E->next()) { 241 | Ref symbol = get_symbols()[E->get()]; 242 | symbol->set_variation_idx(variant_idx); 243 | variant_idx++; 244 | } 245 | } 246 | Ref FlashDocument::from_file(const String &p_path) { 247 | Ref doc; doc.instance(); 248 | Error err = doc->load_file(p_path); 249 | ERR_FAIL_COND_V_MSG(err != Error::OK, Ref(), "Can't open " + p_path); 250 | return doc; 251 | } 252 | Vector2 FlashDocument::get_atlas_size() const { 253 | return atlas.is_valid() ? Vector2(atlas->get_width(), atlas->get_height()) : Vector2(); 254 | } 255 | Error FlashDocument::load_file(const String &p_path) { 256 | Ref xml; xml.instance(); 257 | Error err = xml->open(p_path); 258 | ERR_FAIL_COND_V_MSG(err != Error::OK, err, "Can't open " + p_path); 259 | xml->set_meta("path", p_path); 260 | parent = NULL; 261 | document = this; 262 | document_path = p_path.get_base_dir(); 263 | err = parse(xml); 264 | ERR_FAIL_COND_V_MSG(err != Error::OK, err, "Can't parse " + p_path); 265 | return OK; 266 | } 267 | 268 | FlashTimeline* FlashDocument::get_timeline(String token) { 269 | Ref tl = symbols.get(token, Variant()); 270 | return tl.is_valid() ? tl.ptr() : nullptr; 271 | } 272 | 273 | void FlashDocument::parse_timeline(const String &path) { 274 | String symbol_path = document_path + "/LIBRARY/" + path; 275 | Ref xml; xml.instance(); 276 | Error err = xml->open(symbol_path); 277 | Ref timeline = element(); 278 | if (err != OK) { 279 | return; 280 | } 281 | ERR_FAIL_COND_MSG(err != Error::OK, "Can't open " + symbol_path); 282 | xml->set_meta("path", symbol_path); 283 | timeline->parse(xml); 284 | timeline->set_local_path(path); 285 | symbols[timeline->token] = timeline; 286 | } 287 | 288 | void FlashDocument::setup(FlashDocument *p_document, FlashElement *p_parent) { 289 | document = this; 290 | parent = NULL; 291 | 292 | Array symbols_array = symbols.values(); 293 | for (int i=0; i timeline = symbols_array[i]; 295 | if (timeline.is_valid()) 296 | timeline->setup(this, this); 297 | } 298 | 299 | Array bitmaps_array = bitmaps.values(); 300 | for (int i=0; i bi = bitmaps_array[i]; 302 | if (bi.is_valid()) 303 | bi->setup(this, this); 304 | } 305 | 306 | for (List>::Element *E = timelines.front(); E; E = E->next()) { 307 | E->get()->setup(this, this); 308 | } 309 | 310 | cache_variants(); 311 | } 312 | Ref FlashDocument::get_bitmap_rect(const String &p_name) { 313 | ERR_FAIL_COND_V_MSG(!bitmaps.has(p_name), Ref(), "No bitmap found for " + p_name); 314 | Ref item = bitmaps[p_name]; 315 | return item->get_texture(); 316 | } 317 | Ref FlashDocument::get_main_timeline() { 318 | return timelines.front() ? timelines.front()->get() : Ref(); 319 | } 320 | Error FlashDocument::parse(Ref xml) { 321 | while (xml->read() == Error::OK) { 322 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 323 | String n = xml->get_node_name(); 324 | if (xml->get_node_type() == XMLParser::NODE_ELEMENT && xml->get_node_name() == "DOMTimeline") { 325 | add_child(xml, &timelines); 326 | } 327 | else if (xml->get_node_type() == XMLParser::NODE_ELEMENT && xml->get_node_name() == "DOMBitmapItem") { 328 | Ref bitmap = add_child(xml); 329 | bitmaps[bitmap->get_name()] = bitmap; 330 | } 331 | else if (xml->get_node_type() == XMLParser::NODE_ELEMENT && xml->get_node_name() == "Include" && xml->has_attribute("href")) { 332 | String path = xml->get_attribute_value_safe("href"); 333 | parse_timeline(path); 334 | } 335 | } 336 | return Error::OK; 337 | } 338 | void FlashDocument::animation_process(FlashPlayer* node, float time, float delta, Transform2D tr, FlashColorEffect effect) { 339 | for (List>::Element *E = timelines.front(); E; E = E->next()) { 340 | E->get()->animation_process(node, time, delta, tr, effect); 341 | } 342 | } 343 | 344 | void FlashBitmapItem::_bind_methods() { 345 | ClassDB::bind_method(D_METHOD("get_texture"), &FlashBitmapItem::get_texture); 346 | ClassDB::bind_method(D_METHOD("set_texture", "texture"), &FlashBitmapItem::set_texture); 347 | 348 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_texture", "get_texture"); 349 | } 350 | Error FlashBitmapItem::parse(Ref xml) { 351 | if (xml->has_attribute("name") && xml->has_attribute("href")) { 352 | name = xml->get_attribute_value_safe("name"); 353 | bitmap_path = "LIBRARY/" + xml->get_attribute_value_safe("href"); 354 | return Error::OK; 355 | } else { 356 | return Error::ERR_INVALID_DATA; 357 | } 358 | } 359 | 360 | void FlashTimeline::_bind_methods() { 361 | ClassDB::bind_method(D_METHOD("get_layers"), &FlashTimeline::get_layers); 362 | ClassDB::bind_method(D_METHOD("set_layers", "layers"), &FlashTimeline::set_layers); 363 | ClassDB::bind_method(D_METHOD("get_variants"), &FlashTimeline::get_variants); 364 | ClassDB::bind_method(D_METHOD("set_variants", "variants"), &FlashTimeline::set_variants); 365 | ClassDB::bind_method(D_METHOD("get_clips"), &FlashTimeline::get_clips); 366 | ClassDB::bind_method(D_METHOD("set_clips", "clips"), &FlashTimeline::set_clips); 367 | ClassDB::bind_method(D_METHOD("get_events"), &FlashTimeline::get_events); 368 | ClassDB::bind_method(D_METHOD("set_events", "labels"), &FlashTimeline::set_events); 369 | ClassDB::bind_method(D_METHOD("get_token"), &FlashTimeline::get_token); 370 | ClassDB::bind_method(D_METHOD("set_token", "token"), &FlashTimeline::set_token); 371 | ClassDB::bind_method(D_METHOD("get_local_path"), &FlashTimeline::get_local_path); 372 | ClassDB::bind_method(D_METHOD("set_local_path", "local_path"), &FlashTimeline::set_local_path); 373 | ClassDB::bind_method(D_METHOD("get_clips_header"), &FlashTimeline::get_clips_header); 374 | ClassDB::bind_method(D_METHOD("set_clips_header", "clips_header"), &FlashTimeline::set_clips_header); 375 | ClassDB::bind_method(D_METHOD("get_duration"), &FlashTimeline::get_duration); 376 | ClassDB::bind_method(D_METHOD("set_duration", "duration"), &FlashTimeline::set_duration); 377 | 378 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "layers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_layers", "get_layers"); 379 | ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variants", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_variants", "get_variants"); 380 | ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "clips", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_clips", "get_clips"); 381 | ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "events", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_events", "get_events"); 382 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "token", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_token", "get_token"); 383 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "local_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_local_path", "get_local_path"); 384 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "clips_header", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_clips_header", "get_clips_header"); 385 | ADD_PROPERTY(PropertyInfo(Variant::INT, "duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ), "set_duration", "get_duration"); 386 | } 387 | Array FlashTimeline::get_layers() { 388 | Array l; 389 | for (List>::Element *E = masks.front(); E; E = E->next()) { 390 | l.push_back(E->get()); 391 | } 392 | for (List>::Element *E = layers.front(); E; E = E->next()) { 393 | l.push_back(E->get()); 394 | } 395 | return l; 396 | } 397 | void FlashTimeline::set_layers(Array p_layers) { 398 | layers.clear(); 399 | for (int i=0; i layer = p_layers[i]; 401 | if (layer.is_valid()) { 402 | if (layer->get_type() == "mask") 403 | masks.push_back(layer); 404 | else 405 | layers.push_back(layer); 406 | } 407 | } 408 | } 409 | Ref FlashTimeline::get_layer(int index) { 410 | for (List>::Element *E = masks.front(); E; E = E->next()) { 411 | if (E->get()->get_index() == index) return E->get(); 412 | } 413 | for (List>::Element *E = layers.front(); E; E = E->next()) { 414 | if (E->get()->get_index() == index) return E->get(); 415 | } 416 | return Ref(); 417 | } 418 | void FlashTimeline::add_label(const String &name, const String &label_type, float start, float duration) { 419 | if (label_type == "anchor") { 420 | variants[name] = start; 421 | } else if (label_type == "comment") { 422 | PoolRealArray timings; 423 | if (events.has(name)) { 424 | timings = events[name]; 425 | } else { 426 | events[name] = timings; 427 | } 428 | timings.push_back(start); 429 | events[name] = timings; 430 | //events[name] = Vector2(start, start+duration); 431 | } else { 432 | clips[name] = Vector2(start, start+duration); 433 | } 434 | //labels[name] = Vector2(start, start+duration); 435 | } 436 | void FlashTimeline::setup(FlashDocument *p_document, FlashElement *p_parent) { 437 | FlashElement::setup(p_document, p_parent); 438 | for (List>::Element *E = layers.front(); E; E = E->next()) { 439 | E->get()->setup(document, this); 440 | } 441 | for (List>::Element *E = masks.front(); E; E = E->next()) { 442 | E->get()->setup(document, this); 443 | } 444 | 445 | } 446 | Error FlashTimeline::parse(Ref xml) { 447 | if (xml->is_empty()) return Error::OK; 448 | int layer_index = 0; 449 | while (xml->read() == Error::OK) { 450 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 451 | if (xml->get_node_name() == "DOMSymbolItem" && xml->get_node_type() == XMLParser::NODE_ELEMENT) { 452 | token = FlashDocument::validate_token(xml->get_attribute_value_safe("name")); 453 | } else if (xml->get_node_name() == "DOMTimeline") { 454 | if (xml->get_node_type() == XMLParser::NODE_ELEMENT_END || xml->is_empty()){ 455 | return Error::OK; 456 | } else { 457 | //token = FlashDocument::validate_token(xml->get_attribute_value_safe("name")); 458 | } 459 | } else if (xml->get_node_type() == XMLParser::NODE_ELEMENT && xml->get_node_name() == "DOMLayer") { 460 | Ref layer = add_child(xml); 461 | if (layer.is_null()) { 462 | layer_index++; 463 | continue; 464 | } 465 | if (layer->get_type() == "mask") { 466 | masks.push_back(layer); 467 | } else { 468 | layers.push_back(layer); 469 | } 470 | if (layer->get_duration() > get_duration()) set_duration(layer->get_duration()); 471 | layer->set_index(layer_index); 472 | layer_index++; 473 | } 474 | } 475 | return Error::OK; 476 | } 477 | void FlashTimeline::animation_process(FlashPlayer* node, float time, float delta, Transform2D tr, FlashColorEffect effect) { 478 | if (events.size() && delta > 0.0) { 479 | float event_frame_start = -2.0; 480 | float event_frame_end = -2.0; 481 | float current_frame = floor(time); 482 | float prev_frame = floor(time-delta); 483 | 484 | if (current_frame != prev_frame) { 485 | event_frame_start = prev_frame; 486 | event_frame_end = current_frame; 487 | 488 | for (int i=0; i= 0 && timestamp >= event_frame_start && timestamp < event_frame_end) { 494 | node->queue_animation_event(event); 495 | } else if (event_frame_start < 0 && (timestamp >= duration + event_frame_start || timestamp < event_frame_end)) { 496 | node->queue_animation_event(event, true); 497 | } 498 | } 499 | } 500 | } 501 | } 502 | 503 | for (List>::Element *E = masks.front(); E; E = E->next()) { 504 | E->get()->animation_process(node, time, delta, tr, effect); 505 | } 506 | for (List>::Element *E = layers.back(); E; E = E->prev()) { 507 | E->get()->animation_process(node, time, delta, tr, effect); 508 | } 509 | } 510 | 511 | void FlashLayer::_bind_methods() { 512 | ClassDB::bind_method(D_METHOD("get_index"), &FlashLayer::get_index); 513 | ClassDB::bind_method(D_METHOD("set_index", "index"), &FlashLayer::set_index); 514 | ClassDB::bind_method(D_METHOD("get_layer_name"), &FlashLayer::get_layer_name); 515 | ClassDB::bind_method(D_METHOD("set_layer_name", "layer_name"), &FlashLayer::set_layer_name); 516 | ClassDB::bind_method(D_METHOD("get_type"), &FlashLayer::get_type); 517 | ClassDB::bind_method(D_METHOD("set_type", "type"), &FlashLayer::set_type); 518 | ClassDB::bind_method(D_METHOD("get_duration"), &FlashLayer::get_duration); 519 | ClassDB::bind_method(D_METHOD("set_duration", "duration"), &FlashLayer::set_duration); 520 | ClassDB::bind_method(D_METHOD("get_mask_id"), &FlashLayer::get_mask_id); 521 | ClassDB::bind_method(D_METHOD("set_mask_id", "mask_id"), &FlashLayer::set_mask_id); 522 | ClassDB::bind_method(D_METHOD("get_frames"), &FlashLayer::get_frames); 523 | ClassDB::bind_method(D_METHOD("set_frames", "frames"), &FlashLayer::set_frames); 524 | 525 | ADD_PROPERTY(PropertyInfo(Variant::INT, "index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_index", "get_index"); 526 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "layer_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_layer_name", "get_layer_name"); 527 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_type", "get_type"); 528 | ADD_PROPERTY(PropertyInfo(Variant::INT, "duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_duration", "get_duration"); 529 | ADD_PROPERTY(PropertyInfo(Variant::INT, "mask_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_mask_id", "get_mask_id"); 530 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_frames", "get_frames"); 531 | } 532 | Array FlashLayer::get_frames() { 533 | Array l; 534 | for (List>::Element *E = frames.front(); E; E = E->next()) { 535 | l.push_back(E->get()); 536 | } 537 | return l; 538 | } 539 | void FlashLayer::set_frames(Array p_frames) { 540 | frames.clear(); 541 | for (int i=0; i frame = p_frames[i]; 543 | if (frame.is_valid()) { 544 | frames.push_back(frame); 545 | } 546 | } 547 | } 548 | void FlashLayer::setup(FlashDocument *p_document, FlashElement *p_parent) { 549 | FlashElement::setup(p_document, p_parent); 550 | for (List>::Element *E = frames.front(); E; E = E->next()) { 551 | E->get()->setup(document, this); 552 | } 553 | } 554 | Error FlashLayer::parse(Ref xml) { 555 | if (xml->has_attribute("name")) 556 | layer_name = xml->get_attribute_value_safe("name"); 557 | if (xml->has_attribute("layerType")) 558 | type = xml->get_attribute_value_safe("layerType"); 559 | if (type == "guide") { 560 | if (xml->is_empty()) return ERR_SKIP; 561 | while (xml->read() == OK) { 562 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 563 | if (xml->get_node_name() == "DOMLayer" && xml->get_node_type() == XMLParser::NODE_ELEMENT_END) 564 | return Error::ERR_SKIP; 565 | } 566 | } 567 | if (xml->has_attribute("color")) 568 | color = parse_color(xml->get_attribute_value_safe("color")); 569 | if (xml->has_attribute("parentLayerIndex")) { 570 | int layer_index = xml->get_attribute_value_safe("parentLayerIndex").to_int(); 571 | FlashTimeline *tl = find_parent(); 572 | Ref parent_layer = tl->get_layer(layer_index); 573 | if (parent_layer.is_valid() && parent_layer->type == "mask") { 574 | mask_id = parent_layer->get_eid(); 575 | } 576 | } 577 | 578 | if (xml->is_empty()) return Error::OK; 579 | while (xml->read() == OK) { 580 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 581 | if (xml->get_node_name() == "DOMLayer" && (xml->get_node_type() == XMLParser::NODE_ELEMENT_END || xml->is_empty())) 582 | return Error::OK; 583 | if (xml->get_node_name() == "DOMFrame") { 584 | Ref frame = add_child(xml, &frames); 585 | duration += frame->get_duration(); 586 | } 587 | } 588 | return Error::OK; 589 | }; 590 | void FlashLayer::animation_process(FlashPlayer* node, float time, float delta, Transform2D parent_transform, FlashColorEffect parent_effect) { 591 | if (type == "guide") return; 592 | if (type == "folder") return; 593 | if (type == "mask") node->mask_begin(get_eid()); 594 | if (mask_id) node->clip_begin(mask_id); 595 | 596 | float frame_time = time; 597 | while (duration > 0 && frame_time > duration) frame_time -= duration; 598 | int frame_idx = static_cast(floor(frame_time)); 599 | 600 | Ref current; 601 | Ref next; 602 | for (List>::Element *E = frames.front(); E; E = E->next()) { 603 | if (E->get()->get_index() > frame_idx) { 604 | break; 605 | } 606 | current = E->get(); 607 | if (E->next()) next = E->next()->get(); 608 | } 609 | 610 | if (!current.is_valid()) return; 611 | 612 | float interpolation = 0; 613 | float current_time = frame_time - current->get_index(); 614 | if (current->tweens.size() > 0){ 615 | Reftween = current->tweens.front()->get(); 616 | interpolation = tween->interpolate(current_time/current->get_duration()); 617 | } 618 | 619 | int idx = 0; 620 | for (List>::Element *E = current->elements.front(); E; E = E->next()) { 621 | Ref elem = E->get(); 622 | Transform2D tr = elem->get_transform(); 623 | FlashColorEffect effect = current->color_effect; 624 | FlashInstance *inst = Object::cast_to(elem.ptr()); 625 | if (inst != NULL) { 626 | effect = inst->color_effect * effect; 627 | } 628 | FlashColorEffect next_effect = effect; 629 | 630 | if (next.is_valid() && next->elements.size() >= idx+1) { 631 | Ref next_elem = next->elements[idx]; 632 | Transform2D to = next_elem->get_transform(); 633 | Vector2 x = tr[0].linear_interpolate(to[0], interpolation); 634 | Vector2 y = tr[1].linear_interpolate(to[1], interpolation); 635 | Vector2 o = tr[2].linear_interpolate(to[2], interpolation); 636 | tr = Transform2D(x.x, x.y, y.x, y.y, o.x, o.y); 637 | FlashInstance *next_inst = Object::cast_to(next_elem.ptr()); 638 | next_effect = next->color_effect; 639 | if (next_inst != NULL) { 640 | next_effect = next_inst->color_effect*next_effect; 641 | } 642 | } 643 | effect = effect.interpolate(next_effect, interpolation); 644 | 645 | 646 | elem->animation_process(node, frame_time - current->get_index(), delta, parent_transform * tr, effect*parent_effect); 647 | idx++; 648 | } 649 | if (type == "mask") node->mask_end(get_eid()); 650 | if (mask_id) node->clip_end(mask_id); 651 | } 652 | 653 | void FlashDrawing::_bind_methods() { 654 | ClassDB::bind_method(D_METHOD("get_transform"), &FlashDrawing::get_transform); 655 | ClassDB::bind_method(D_METHOD("set_transform", "transform"), &FlashDrawing::set_transform); 656 | 657 | ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_transform", "get_transform"); 658 | } 659 | void FlashDrawing::animation_process(FlashPlayer* node, float time, float delta, Transform2D tr, FlashColorEffect effect) { 660 | } 661 | 662 | void FlashFrame::_bind_methods() { 663 | ClassDB::bind_method(D_METHOD("get_index"), &FlashFrame::get_index); 664 | ClassDB::bind_method(D_METHOD("set_index", "index"), &FlashFrame::set_index); 665 | ClassDB::bind_method(D_METHOD("get_duration"), &FlashFrame::get_duration); 666 | ClassDB::bind_method(D_METHOD("set_duration", "duration"), &FlashFrame::set_duration); 667 | ClassDB::bind_method(D_METHOD("get_frame_name"), &FlashFrame::get_frame_name); 668 | ClassDB::bind_method(D_METHOD("set_frame_name", "frame_name"), &FlashFrame::set_frame_name); 669 | ClassDB::bind_method(D_METHOD("get_label_type"), &FlashFrame::get_label_type); 670 | ClassDB::bind_method(D_METHOD("set_label_type", "label_type"), &FlashFrame::set_label_type); 671 | ClassDB::bind_method(D_METHOD("get_keymode"), &FlashFrame::get_keymode); 672 | ClassDB::bind_method(D_METHOD("set_keymode", "keymode"), &FlashFrame::set_keymode); 673 | ClassDB::bind_method(D_METHOD("get_tween_type"), &FlashFrame::get_tween_type); 674 | ClassDB::bind_method(D_METHOD("set_tween_type", "tween_type"), &FlashFrame::set_tween_type); 675 | ClassDB::bind_method(D_METHOD("get_color_effect"), &FlashFrame::get_color_effect); 676 | ClassDB::bind_method(D_METHOD("set_color_effect", "color_effect"), &FlashFrame::set_color_effect); 677 | ClassDB::bind_method(D_METHOD("get_elements"), &FlashFrame::get_elements); 678 | ClassDB::bind_method(D_METHOD("set_elements", "elements"), &FlashFrame::set_elements); 679 | ClassDB::bind_method(D_METHOD("get_tweens"), &FlashFrame::get_tweens); 680 | ClassDB::bind_method(D_METHOD("set_tweens", "tweens"), &FlashFrame::set_tweens); 681 | 682 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_index", "get_index"); 683 | ADD_PROPERTY(PropertyInfo(Variant::INT, "duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_duration", "get_duration"); 684 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "frame_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_frame_name", "get_frame_name"); 685 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "label_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_label_type", "get_label_type"); 686 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "keymode", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_keymode", "get_keymode"); 687 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "tween_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_tween_type", "get_tween_type"); 688 | ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_effect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_color_effect", "get_color_effect"); 689 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "elements", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_elements", "get_elements"); 690 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "tweens", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_tweens", "get_tweens"); 691 | } 692 | PoolColorArray FlashFrame::get_color_effect() const { 693 | PoolColorArray effect; 694 | if (color_effect.is_empty()) { 695 | return effect; 696 | } 697 | effect.push_back(color_effect.add); 698 | effect.push_back(color_effect.mult); 699 | return effect; 700 | } 701 | void FlashFrame::set_color_effect(PoolColorArray p_color_effect) { 702 | if (p_color_effect.size() > 0) { 703 | color_effect.add = p_color_effect[0]; 704 | } else { 705 | color_effect.add = Color(); 706 | } 707 | if (p_color_effect.size() > 1) { 708 | color_effect.mult = p_color_effect[1]; 709 | } else { 710 | color_effect.mult = Color(1,1,1,1); 711 | } 712 | } 713 | Array FlashFrame::get_elements() { 714 | Array l; 715 | for (List>::Element *E = elements.front(); E; E = E->next()) { 716 | l.push_back(E->get()); 717 | } 718 | return l; 719 | } 720 | void FlashFrame::set_elements(Array p_elements) { 721 | elements.clear(); 722 | for (int i=0; i element = p_elements[i]; 724 | if (element.is_valid()) 725 | elements.push_back(element); 726 | } 727 | } 728 | Array FlashFrame::get_tweens() { 729 | Array l; 730 | for (List>::Element *E = tweens.front(); E; E = E->next()) { 731 | l.push_back(E->get()); 732 | } 733 | return l; 734 | } 735 | void FlashFrame::set_tweens(Array p_tweens) { 736 | tweens.clear(); 737 | for (int i=0; i tween = p_tweens[i]; 739 | if (tween.is_valid()) { 740 | tweens.push_back(tween); 741 | } 742 | } 743 | } 744 | void FlashFrame::setup(FlashDocument *p_document, FlashElement *p_parent) { 745 | FlashElement::setup(p_document, p_parent); 746 | for (List>::Element *E = elements.front(); E; E = E->next()) { 747 | E->get()->setup(document, this); 748 | } 749 | for (List>::Element *E = tweens.front(); E; E = E->next()) { 750 | E->get()->setup(document, this); 751 | } 752 | } 753 | Error FlashFrame::parse(Ref xml) { 754 | if (xml->has_attribute("index")) index = xml->get_attribute_value_safe("index").to_int(); 755 | if (xml->has_attribute("duration")) duration = xml->get_attribute_value_safe("duration").to_int(); 756 | if (xml->has_attribute("keymode")) keymode = xml->get_attribute_value_safe("keymode"); 757 | if (xml->has_attribute("tweenType")) tween_type = xml->get_attribute_value_safe("tweenType"); 758 | if (xml->has_attribute("name")) frame_name = xml->get_attribute_value_safe("name").strip_edges(true, true); 759 | if (xml->has_attribute("labelType")) { 760 | label_type = xml->get_attribute_value_safe("labelType"); 761 | } else { 762 | label_type = "name"; 763 | } 764 | if (xml->is_empty()) return Error::OK; 765 | while (xml->read() == Error::OK) { 766 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 767 | if (xml->get_node_name() == "DOMFrame" && (xml->get_node_type() == XMLParser::NODE_ELEMENT_END || xml->is_empty())) 768 | break; 769 | if (xml->get_node_name() == "DOMGroup") 770 | elements.push_back(add_child(xml)); 771 | else if (xml->get_node_name() == "DOMShape") 772 | elements.push_back(add_child(xml)); 773 | else if (xml->get_node_name() == "DOMSymbolInstance") 774 | elements.push_back(add_child(xml)); 775 | else if (xml->get_node_name() == "DOMBitmapInstance") 776 | elements.push_back(add_child(xml)); 777 | else if (xml->get_node_name() == "CustomEase" || xml->get_node_name() == "Ease") 778 | tweens.push_back(add_child(xml)); 779 | else if (xml->get_node_name() == "Color") 780 | color_effect = parse_color_effect(xml); 781 | 782 | } 783 | if (tween_type == "motion" && tweens.size() == 0){ 784 | Ref linear = document->element(this); 785 | tweens.push_back(linear); 786 | } 787 | if (frame_name != "") { 788 | FlashTimeline *tl = find_parent(); 789 | FlashLayer *layer = find_parent(); 790 | if (tl != NULL) { 791 | tl->add_label(frame_name, label_type, index, duration); 792 | if (layer != NULL) { 793 | tl->set_clips_header(layer->get_layer_name()); 794 | } 795 | } 796 | } 797 | return Error::OK; 798 | } 799 | 800 | Error FlashShape::parse(Ref xml) { 801 | return FAILED; 802 | //// Even if there visible shapes in document or symbols, 803 | //// it should never be rendered if doc has been exported 804 | //// with Funexpected Flash Tools 805 | //// TODO: report error in `FlashShape::animation_process` calls instead 806 | // String layer_name = find_parent()->get_layer_name(); 807 | // int frame_idx = find_parent()->get_index(); 808 | // ERR_FAIL_V_MSG(FAILED, String("Vector Shape not supported at ") + xml->get_meta("path") + " in layer '" + layer_name + "' at frame " + itos(frame_idx)); 809 | } 810 | 811 | void FlashGroup::_bind_methods() { 812 | ClassDB::bind_method(D_METHOD("get_members"), &FlashGroup::get_members); 813 | ClassDB::bind_method(D_METHOD("set_members", "members"), &FlashGroup::set_members); 814 | 815 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "members", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_members", "get_members"); 816 | 817 | } 818 | Array FlashGroup::get_members() { 819 | Array l; 820 | for (List>::Element *E = members.front(); E; E = E->next()) { 821 | l.push_back(E->get()); 822 | } 823 | return l; 824 | } 825 | void FlashGroup::set_members(Array p_members) { 826 | members.clear(); 827 | for (int i=0; i member = p_members[i]; 829 | if (member.is_valid()) { 830 | members.push_back(member); 831 | } 832 | } 833 | } 834 | List> FlashGroup::all_members() const { 835 | List> result; 836 | List groups; 837 | groups.push_back(this); 838 | for (const List::Element *E = groups.front(); E; E = E->next()) { 839 | const FlashGroup *group = E->get(); 840 | for (const List>::Element *M = group->members.front(); M; M = M->next()) { 841 | const FlashGroup *member = Object::cast_to(M->get().ptr()); 842 | if (member) { 843 | groups.push_back(member); 844 | } else { 845 | result.push_back(M->get()); 846 | } 847 | } 848 | } 849 | return result; 850 | } 851 | void FlashGroup::setup(FlashDocument *p_document, FlashElement *p_parent) { 852 | FlashElement::setup(p_document, p_parent); 853 | for (List>::Element *E = members.front(); E; E = E->next()) { 854 | E->get()->setup(document, this); 855 | } 856 | } 857 | Error FlashGroup::parse(Ref xml) { 858 | if (xml->is_empty()) return Error::OK; 859 | while (xml->read() == Error::OK){ 860 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 861 | if (xml->get_node_name() == "DOMGroup" && (xml->get_node_type() == XMLParser::NODE_ELEMENT_END || xml->is_empty())) 862 | return Error::OK; 863 | if (xml->get_node_name() == "DOMGroup" && xml->get_node_type() == XMLParser::NODE_ELEMENT) 864 | members.push_back(add_child(xml)); 865 | else if (xml->get_node_name() == "DOMBitmapInstance") 866 | members.push_back(add_child(xml)); 867 | else if (xml->get_node_name() == "DOMShape") 868 | members.push_back(add_child(xml)); 869 | else if (xml->get_node_name() == "DOMSymbolInstance") 870 | members.push_back(add_child(xml)); 871 | } 872 | return Error::OK; 873 | } 874 | void FlashGroup::animation_process(FlashPlayer* node, float time, float delta, Transform2D tr, FlashColorEffect effect) { 875 | List> ms = all_members(); 876 | for (List>::Element *E = ms.front(); E; E = E->next()) { 877 | E->get()->animation_process(node, time, delta, tr, effect); 878 | } 879 | } 880 | 881 | void FlashInstance::_bind_methods() { 882 | ClassDB::bind_method(D_METHOD("get_first_frame"), &FlashInstance::get_first_frame); 883 | ClassDB::bind_method(D_METHOD("set_first_frame", "first_frame"), &FlashInstance::set_first_frame); 884 | ClassDB::bind_method(D_METHOD("get_loop"), &FlashInstance::get_loop); 885 | ClassDB::bind_method(D_METHOD("set_loop", "loop"), &FlashInstance::set_loop); 886 | ClassDB::bind_method(D_METHOD("get_color_effect"), &FlashInstance::get_color_effect); 887 | ClassDB::bind_method(D_METHOD("set_color_effect", "color_effect"), &FlashInstance::set_color_effect); 888 | ClassDB::bind_method(D_METHOD("get_timeline_token"), &FlashInstance::get_timeline_token); 889 | ClassDB::bind_method(D_METHOD("set_timeline_token", "timeline_token"), &FlashInstance::set_timeline_token); 890 | 891 | ADD_PROPERTY(PropertyInfo(Variant::INT, "first_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_first_frame", "get_first_frame"); 892 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_loop", "get_loop"); 893 | ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_effect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_color_effect", "get_color_effect"); 894 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "timeline_token", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_timeline_token", "get_timeline_token"); 895 | } 896 | void FlashInstance::setup(FlashDocument *p_document, FlashElement *p_parent) { 897 | FlashDrawing::setup(p_document, p_parent); 898 | layer_name = find_parent()->get_layer_name(); 899 | } 900 | FlashTimeline* FlashInstance::get_timeline() { 901 | if (timeline != nullptr) return timeline; 902 | timeline = document->get_timeline(timeline_token); 903 | return timeline; 904 | } 905 | PoolColorArray FlashInstance::get_color_effect() const { 906 | PoolColorArray effect; 907 | effect.push_back(color_effect.add); 908 | effect.push_back(color_effect.mult); 909 | return effect; 910 | } 911 | void FlashInstance::set_color_effect(PoolColorArray p_color_effect) { 912 | if (p_color_effect.size() > 0) { 913 | color_effect.add = p_color_effect[0]; 914 | } else { 915 | color_effect.add = Color(); 916 | } 917 | if (p_color_effect.size() > 1) { 918 | color_effect.mult = p_color_effect[1]; 919 | } else { 920 | color_effect.mult = Color(1,1,1,1); 921 | } 922 | } 923 | Error FlashInstance::parse(Ref xml) { 924 | if (xml->has_attribute("libraryItemName")) { 925 | timeline_token = FlashDocument::validate_token(xml->get_attribute_value_safe("libraryItemName")); 926 | } 927 | if (xml->has_attribute("firstFrame")) 928 | first_frame = xml->get_attribute_value_safe("firstFrame").to_int(); 929 | if (xml->has_attribute("loop")) 930 | loop = xml->get_attribute_value_safe("loop"); 931 | if (xml->has_attribute("centerPoint3DX")) 932 | center_point.x = xml->get_attribute_value_safe("centerPoint3DX").to_float(); 933 | if (xml->has_attribute("centerPoint3DY")) 934 | center_point.y = xml->get_attribute_value_safe("centerPoint3DY").to_float(); 935 | if (xml->is_empty()) return Error::OK; 936 | while (xml->read() == Error::OK) { 937 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 938 | if (xml->get_node_name() == "DOMSymbolInstance" && (xml->get_node_type() == XMLParser::NODE_ELEMENT_END || xml->is_empty())) 939 | return Error::OK; 940 | if (xml->get_node_name() == "Matrix") 941 | transform = parse_transform(xml); 942 | if (xml->get_node_name() == "Point") { 943 | if (xml->has_attribute("x")) 944 | transformation_point.x = xml->get_attribute_value_safe("x").to_float(); 945 | if (xml->has_attribute("y")) 946 | transformation_point.y = xml->get_attribute_value_safe("y").to_float(); 947 | } 948 | if (xml->get_node_name() == "Color") { 949 | color_effect = parse_color_effect(xml); 950 | } 951 | } 952 | return Error::OK; 953 | } 954 | void FlashInstance::animation_process(FlashPlayer* node, float time, float delta, Transform2D tr, FlashColorEffect effect) { 955 | FlashTimeline* tl = get_timeline(); 956 | if (tl == NULL) return; 957 | float instance_time = 958 | loop == "single frame" ? first_frame : 959 | loop == "play once" ? MIN(first_frame + time, tl->get_duration()-0.001) : 960 | first_frame + time; 961 | 962 | instance_time = node->get_symbol_frame(tl, instance_time); 963 | 964 | tl->animation_process(node, instance_time, delta, tr, effect); 965 | 966 | } 967 | 968 | void FlashBitmapInstance::_bind_methods(){ 969 | ClassDB::bind_method(D_METHOD("get_library_item_name"), &FlashBitmapInstance::get_library_item_name); 970 | ClassDB::bind_method(D_METHOD("set_library_item_name", "library_item_name"), &FlashBitmapInstance::set_library_item_name); 971 | 972 | ADD_PROPERTY(PropertyInfo(Variant::INT, "library_item_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_library_item_name", "get_library_item_name"); 973 | } 974 | Error FlashBitmapInstance::parse(Ref xml) { 975 | if(xml->has_attribute("libraryItemName")) 976 | library_item_name = xml->get_attribute_value_safe("libraryItemName"); 977 | if (xml->is_empty()) return Error::OK; 978 | while (xml->read() == OK) { 979 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 980 | if (xml->get_node_name() == "DOMBitmapInstance" && (xml->get_node_type() == XMLParser::NODE_ELEMENT_END || xml->is_empty())) 981 | return Error::OK; 982 | if (xml->get_node_name() == "Matrix") 983 | transform = parse_transform(xml); 984 | } 985 | return Error::OK; 986 | } 987 | 988 | Ref FlashBitmapInstance::get_texture() { 989 | if (texture.is_null()) { 990 | texture = document->get_bitmap_rect(library_item_name); 991 | } 992 | return texture; 993 | } 994 | 995 | void FlashBitmapInstance::animation_process(FlashPlayer* node, float time, float delta, Transform2D tr, FlashColorEffect effect) { 996 | Ref tex = get_texture(); 997 | if (!tex.is_valid()) { 998 | return; 999 | } 1000 | if (node->is_masking()) { 1001 | Transform2D scale; 1002 | scale.scale(tex->get_original_size()/tex->get_region().size); 1003 | node->mask_add(tr * scale, tex->get_region(), tex->get_index()); 1004 | return; 1005 | } 1006 | // if (node->is_masking()) { 1007 | // FlashClippingItem item; 1008 | // item.transform = tr; 1009 | // item.texture = document->load_bitmap(timeline_token); 1010 | // node->add_clipping_item(item); 1011 | // return; 1012 | // } 1013 | 1014 | //node->draw_set_transform_matrix(tr); 1015 | Vector colors; 1016 | Color color = effect.mult * 0.5; 1017 | color.r += floor(effect.add.r * 255); 1018 | color.g += floor(effect.add.g * 255); 1019 | color.b += floor(effect.add.b * 255); 1020 | color.a += floor(effect.add.a * 255); 1021 | colors.push_back(color); 1022 | colors.push_back(color); 1023 | colors.push_back(color); 1024 | colors.push_back(color); 1025 | Vector points; 1026 | Vector2 size = tex->get_original_size(); 1027 | points.push_back(tr.xform(Vector2())); 1028 | points.push_back(tr.xform(Vector2(size.x, 0))); 1029 | points.push_back(tr.xform(size)); 1030 | points.push_back(tr.xform(Vector2(0, size.y))); 1031 | 1032 | if (uvs.size() == 0) { 1033 | Vector2 as = document->get_atlas_size(); 1034 | Rect2 r = tex->get_region(); 1035 | Vector2 start = r.position / as; 1036 | Vector2 end = (r.position + r.size) / as; 1037 | uvs.push_back(start); 1038 | uvs.push_back(Vector2(end.x, start.y)); 1039 | uvs.push_back(end); 1040 | uvs.push_back(Vector2(start.x, end.y)); 1041 | } 1042 | 1043 | node->add_polygon(points, colors, uvs, tex->get_index()); 1044 | } 1045 | 1046 | void FlashTween::_bind_methods() { 1047 | ClassDB::bind_method(D_METHOD("get_target"), &FlashTween::get_target); 1048 | ClassDB::bind_method(D_METHOD("set_target", "target"), &FlashTween::set_target); 1049 | ClassDB::bind_method(D_METHOD("get_intensity"), &FlashTween::get_intensity); 1050 | ClassDB::bind_method(D_METHOD("set_intensity", "intensity"), &FlashTween::set_intensity); 1051 | ClassDB::bind_method(D_METHOD("get_method"), &FlashTween::get_method); 1052 | ClassDB::bind_method(D_METHOD("set_method", "method"), &FlashTween::set_method); 1053 | ClassDB::bind_method(D_METHOD("get_points"), &FlashTween::get_points); 1054 | ClassDB::bind_method(D_METHOD("set_points", "points"), &FlashTween::set_points); 1055 | 1056 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "target", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_target", "get_target"); 1057 | ADD_PROPERTY(PropertyInfo(Variant::REAL, "intensity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_intensity", "get_intensity"); 1058 | ADD_PROPERTY(PropertyInfo(Variant::INT, "method", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_method", "get_method"); 1059 | ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_points", "get_points"); 1060 | 1061 | BIND_ENUM_CONSTANT(NONE); 1062 | BIND_ENUM_CONSTANT(CLASSIC); 1063 | BIND_ENUM_CONSTANT(IN_QUAD); 1064 | BIND_ENUM_CONSTANT(OUT_QUAD); 1065 | BIND_ENUM_CONSTANT(INOUT_QUAD); 1066 | BIND_ENUM_CONSTANT(IN_CUBIC); 1067 | BIND_ENUM_CONSTANT(OUT_CUBIC); 1068 | BIND_ENUM_CONSTANT(INOUT_CUBIC); 1069 | BIND_ENUM_CONSTANT(IN_QUART); 1070 | BIND_ENUM_CONSTANT(OUT_QUART); 1071 | BIND_ENUM_CONSTANT(INOUT_QUART); 1072 | BIND_ENUM_CONSTANT(IN_QUINT); 1073 | BIND_ENUM_CONSTANT(OUT_QUINT); 1074 | BIND_ENUM_CONSTANT(INOUT_QUINT); 1075 | BIND_ENUM_CONSTANT(IN_SINE); 1076 | BIND_ENUM_CONSTANT(OUT_SINE); 1077 | BIND_ENUM_CONSTANT(INOUT_SINE); 1078 | BIND_ENUM_CONSTANT(IN_BACK); 1079 | BIND_ENUM_CONSTANT(OUT_BACK); 1080 | BIND_ENUM_CONSTANT(INOUT_BACK); 1081 | BIND_ENUM_CONSTANT(IN_CIRC); 1082 | BIND_ENUM_CONSTANT(OUT_CIRC); 1083 | BIND_ENUM_CONSTANT(INOUT_CIRC); 1084 | BIND_ENUM_CONSTANT(IN_BOUNCE); 1085 | BIND_ENUM_CONSTANT(OUT_BOUNCE); 1086 | BIND_ENUM_CONSTANT(INOUT_BOUNCE); 1087 | BIND_ENUM_CONSTANT(IN_ELASTIC); 1088 | BIND_ENUM_CONSTANT(OUT_ELASTIC); 1089 | BIND_ENUM_CONSTANT(INOUT_ELASTIC); 1090 | BIND_ENUM_CONSTANT(CUSTOM); 1091 | } 1092 | Error FlashTween::parse(Ref xml) { 1093 | String n = xml->get_node_name(); 1094 | if (xml->has_attribute("target")) 1095 | target = xml->get_attribute_value_safe("target"); 1096 | if (xml->has_attribute("intensity")) { 1097 | method = CLASSIC; 1098 | intensity = xml->get_attribute_value_safe("intensity").to_int(); 1099 | } 1100 | if (n == "CustomEase") { 1101 | method = CUSTOM; 1102 | } else if (xml->has_attribute("method")) { 1103 | String mname = xml->get_attribute_value_safe("method"); 1104 | method = 1105 | mname == "quadIn" ? IN_QUINT : 1106 | mname == "quadOut" ? OUT_QUINT : 1107 | mname == "quadInOut" ? INOUT_QUINT : 1108 | mname == "cubicIn" ? IN_CUBIC : 1109 | mname == "cubicOut" ? OUT_CUBIC : 1110 | mname == "cubicInOut" ? INOUT_CUBIC : 1111 | mname == "quartIn" ? IN_QUART : 1112 | mname == "quartOut" ? OUT_QUART : 1113 | mname == "quartInOut" ? INOUT_QUART : 1114 | mname == "quintIn" ? IN_QUINT : 1115 | mname == "quintOut" ? OUT_QUINT : 1116 | mname == "quintInOut" ? INOUT_QUINT : 1117 | mname == "sineIn" ? IN_SINE : 1118 | mname == "sineOut" ? OUT_SINE : 1119 | mname == "sineInOut" ? INOUT_SINE : 1120 | mname == "backIn" ? IN_BACK : 1121 | mname == "backOut" ? OUT_BACK : 1122 | mname == "backInOut" ? INOUT_BACK : 1123 | mname == "circIn" ? IN_CIRC : 1124 | mname == "circOut" ? OUT_CIRC : 1125 | mname == "circInOut" ? INOUT_CIRC : 1126 | mname == "bounceIn" ? IN_BOUNCE : 1127 | mname == "bounceOut" ? OUT_BOUNCE : 1128 | mname == "bounceInOut" ? INOUT_BOUNCE : 1129 | mname == "elasticIn" ? IN_ELASTIC : 1130 | mname == "elasticOut" ? OUT_ELASTIC : 1131 | mname == "elasticInOut" ? INOUT_ELASTIC : 1132 | NONE; 1133 | } else { 1134 | method = CLASSIC; 1135 | } 1136 | 1137 | if (xml->is_empty()) return Error::OK; 1138 | while (xml->read() == OK) { 1139 | if (xml->get_node_type() == XMLParser::NODE_TEXT) continue; 1140 | if (xml->get_node_name() == n && (xml->get_node_type() == XMLParser::NODE_ELEMENT_END || xml->is_empty())) 1141 | return Error::OK; 1142 | if (xml->get_node_name() == "Point") { 1143 | Vector2 p = Vector2(); 1144 | if (xml->has_attribute("x")) p.x = xml->get_attribute_value_safe("x").to_float(); 1145 | if (xml->has_attribute("y")) p.y = xml->get_attribute_value_safe("y").to_float(); 1146 | points.push_back(p); 1147 | } 1148 | } 1149 | return Error::OK; 1150 | } 1151 | static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) { 1152 | /* Formula from Wikipedia article on Bezier curves. */ 1153 | real_t omt = (1.0 - t); 1154 | real_t omt2 = omt * omt; 1155 | real_t omt3 = omt2 * omt; 1156 | real_t t2 = t * t; 1157 | real_t t3 = t2 * t; 1158 | 1159 | return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; 1160 | } 1161 | 1162 | inline float __ease_out_bounce(float x) { 1163 | const float n1 = 7.5625; 1164 | const float d1 = 2.75; 1165 | if (x < 1 / d1) { 1166 | return n1 * x * x; 1167 | } else if (x < 2 / d1) { 1168 | return n1 * pow(x - 1.5 / d1, 2) + 0.75; 1169 | } else if (x < 2.5 / d1) { 1170 | return n1 * pow(x - 2.25 / d1, 2) + 0.9375; 1171 | } else { 1172 | return n1 * pow(x - 2.625 / d1, 2) + 0.984375; 1173 | } 1174 | } 1175 | 1176 | // easing calculations taken from https://easings.net/ 1177 | float FlashTween::interpolate(float time) { 1178 | switch (method) { 1179 | case NONE: return time; 1180 | case CLASSIC: return Math::ease(time, intensity); 1181 | case IN_QUAD: return time * time; 1182 | case OUT_QUAD: return 1 - (1 - time) * (1 - time); 1183 | case INOUT_QUAD: return time < 0.5 ? 2 * time * time : 1 - pow(-2 * time + 2, 2) / 2; 1184 | case IN_CUBIC: return time * time * time; 1185 | case OUT_CUBIC: return 1 - pow(1 - time, 3); 1186 | case INOUT_CUBIC: return time < 0.5 ? 4 * time * time * time : 1 - pow(-2 * time + 2, 3) / 2; 1187 | case IN_QUART: return pow(time, 4); 1188 | case OUT_QUART: return 1 - pow(1 - time, 4); 1189 | case INOUT_QUART: return time < 0.5 ? 8 * pow(time, 4) : 1 - pow(-2 * time + 2, 4) / 2; 1190 | case IN_QUINT: return pow(time, 5); 1191 | case OUT_QUINT: return 1 - pow(1 - time, 5); 1192 | case INOUT_QUINT: return time < 0.5 ? 16 * pow(time, 5) : 1 - pow(-2 * time + 2, 5) / 2; 1193 | case IN_SINE: return 1 - cos((time * Math_PI) / 2); 1194 | case OUT_SINE: return sin((time * Math_PI) / 2); 1195 | case INOUT_SINE: return -(cos(Math_PI * time) - 1) / 2; 1196 | case IN_BACK: { 1197 | const float c1 = 1.70158; 1198 | const float c3 = c1 + 1; 1199 | return c3 * time * time * time - c1 * time * time; 1200 | }; 1201 | case OUT_BACK: { 1202 | const float c1 = 1.70158; 1203 | const float c3 = c1 + 1; 1204 | return 1 + c3 * pow(time - 1, 3) + c1 * pow(time - 1, 2); 1205 | }; 1206 | case INOUT_BACK: { 1207 | const float c1 = 1.70158; 1208 | const float c2 = c1 * 1.525; 1209 | return time < 0.5 1210 | ? (pow(2 * time, 2) * ((c2 + 1) * 2 * time - c2)) / 2 1211 | : (pow(2 * time - 2, 2) * ((c2 + 1) * (time * 2 - 2) + c2) + 2) / 2; 1212 | }; 1213 | case IN_CIRC: return 1 - sqrt(1 - pow(time, 2)); 1214 | case OUT_CIRC: return sqrt(1 - pow(time - 1, 2)); 1215 | case INOUT_CIRC: return time < 0.5 1216 | ? (1 - sqrt(1 - pow(2 * time, 2))) / 2 1217 | : (sqrt(1 - pow(-2 * time + 2, 2)) + 1) / 2; 1218 | case IN_ELASTIC: { 1219 | const float c4 = (2 * Math_PI) / 3; 1220 | return time == 0 ? 0 1221 | : time == 1 ? 1 1222 | : -pow(2, 10 * time - 10) * sin((time * 10 - 10.75) * c4); 1223 | } 1224 | case OUT_ELASTIC: { 1225 | const float c4 = (2 * Math_PI) / 3; 1226 | return time == 0 ? 0 1227 | : time == 1 ? 1 1228 | : pow(2, -10 * time) * sin((time * 10 - 0.75) * c4) + 1; 1229 | }; 1230 | case INOUT_ELASTIC: { 1231 | const float c5 = (2 * Math_PI) / 4.5; 1232 | return time == 0 ? 0 1233 | : time == 1 ? 1 1234 | : time < 0.5 1235 | ? -(pow(2, 20 * time - 10) * sin((20 * time - 11.125) * c5)) / 2 1236 | : (pow(2, -20 * time + 10) * sin((20 * time - 11.125) * c5)) / 2 + 1; 1237 | }; \ 1238 | case IN_BOUNCE: return 1 - __ease_out_bounce(1 - time); 1239 | case OUT_BOUNCE: return __ease_out_bounce(time); 1240 | case INOUT_BOUNCE: return time < 0.5 1241 | ? (1 - __ease_out_bounce(1 - 2 * time)) / 2 1242 | : (1 + __ease_out_bounce(2 * time - 1)) / 2; 1243 | case CUSTOM: { 1244 | if (points.size() < 4) return time; 1245 | float low = 0; 1246 | float high = 1; 1247 | Vector2 start; 1248 | Vector2 start_out; 1249 | Vector2 end_in; 1250 | Vector2 end; 1251 | 1252 | for (int i=0; i < (points.size()-1)/3; i++){ 1253 | start = points[3*i]; 1254 | start_out = points[3*i+1]; 1255 | end_in = points[3*i+2]; 1256 | end = points[3*i+3]; 1257 | if (time < end.x) break; 1258 | } 1259 | //time = time - low; 1260 | //narrow high and low as much as possible 1261 | float middle; 1262 | for (int i = 0; i < 10; i++) { 1263 | middle = (low + high) / 2.0; 1264 | Vector2 interp = _bezier_interp(middle, start, start_out, end_in, end); 1265 | if (interp.x < time) { 1266 | low = middle; 1267 | } else { 1268 | high = middle; 1269 | } 1270 | } 1271 | 1272 | //interpolate the result: 1273 | Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end); 1274 | Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end); 1275 | 1276 | float c = (time - low_pos.x) / (high_pos.x - low_pos.x); 1277 | 1278 | return low_pos.linear_interpolate(high_pos, c).y; 1279 | } 1280 | default: return time; 1281 | } 1282 | } 1283 | 1284 | void FlashTextureRect::_bind_methods() { 1285 | 1286 | ClassDB::bind_method(D_METHOD("set_index", "index"), &FlashTextureRect::set_index); 1287 | ClassDB::bind_method(D_METHOD("get_index"), &FlashTextureRect::get_index); 1288 | ClassDB::bind_method(D_METHOD("set_region", "region"), &FlashTextureRect::set_region); 1289 | ClassDB::bind_method(D_METHOD("get_region"), &FlashTextureRect::get_region); 1290 | ClassDB::bind_method(D_METHOD("set_margin", "margin"), &FlashTextureRect::set_margin); 1291 | ClassDB::bind_method(D_METHOD("get_margin"), &FlashTextureRect::get_margin); 1292 | ClassDB::bind_method(D_METHOD("set_original_size", "original_size"), &FlashTextureRect::set_original_size); 1293 | ClassDB::bind_method(D_METHOD("get_original_size"), &FlashTextureRect::get_original_size); 1294 | 1295 | ADD_PROPERTY(PropertyInfo(Variant::INT, "index"), "set_index", "get_index"); 1296 | ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region"), "set_region", "get_region"); 1297 | ADD_PROPERTY(PropertyInfo(Variant::RECT2, "margin"), "set_margin", "get_margin"); 1298 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "original_size"), "set_original_size", "get_original_size"); 1299 | } 1300 | 1301 | RES ResourceFormatLoaderFlashTexture::load(const String &p_path, const String &p_original_path, Error *r_error) { 1302 | FileAccess *f = FileAccess::open(p_path, FileAccess::READ); 1303 | int decompressed_size = f->get_32(); 1304 | int bytes; 1305 | PoolVector buff; 1306 | buff.resize(f->get_len()-f->get_position()); 1307 | { 1308 | PoolVector::Write w = buff.write(); 1309 | bytes = f->get_buffer(w.ptr(), buff.size()); 1310 | } 1311 | 1312 | PoolVector decompressed; 1313 | decompressed.resize(decompressed_size); 1314 | Compression::decompress(decompressed.write().ptr(), decompressed.size(), buff.read().ptr(), bytes, Compression::MODE_FASTLZ); 1315 | 1316 | PoolVector::Read r = decompressed.read(); 1317 | Variant texture_info_var; 1318 | decode_variant(texture_info_var, r.ptr(), decompressed.size(), NULL, true); 1319 | Dictionary texture_info = texture_info_var; 1320 | Array images = texture_info["images"]; 1321 | Ref texture; 1322 | texture.instance(); 1323 | texture->create((int)texture_info["width"], (int)texture_info["height"], images.size(), (Image::Format)(int)texture_info["format"], (int)texture_info["flags"]); 1324 | 1325 | for (int i=0; i img = images[i]; 1327 | texture->set_layer_data(img, i); 1328 | } 1329 | memdelete(f); 1330 | return texture; 1331 | } 1332 | 1333 | void ResourceFormatLoaderFlashTexture::get_recognized_extensions(List *p_extensions) const { 1334 | p_extensions->push_back("ftex"); 1335 | } 1336 | bool ResourceFormatLoaderFlashTexture::handles_type(const String &p_type) const { 1337 | return p_type == "TextureArray"; 1338 | } 1339 | String ResourceFormatLoaderFlashTexture::get_resource_type(const String &p_path) const { 1340 | if (p_path.get_extension().to_lower() == "ftex") 1341 | return "TextureArray"; 1342 | return ""; 1343 | } -------------------------------------------------------------------------------- /flash_resources.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #ifndef FLASH_RESOURCES_H 24 | #define FLASH_RESOURCES_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "flash_player.h" 32 | 33 | class FlashPlayer; 34 | class FlashDocument; 35 | class FlashTimeline; 36 | class FlashLayer; 37 | class FlashFrame; 38 | class FlashTween; 39 | 40 | struct FlashColorEffect { 41 | Color add; 42 | Color mult; 43 | 44 | FlashColorEffect() : 45 | add(Color(0,0,0,0)), 46 | mult(Color(1,1,1,1)) {} 47 | 48 | inline FlashColorEffect interpolate(FlashColorEffect effect, float amount) { 49 | FlashColorEffect new_effect; 50 | new_effect.mult = mult.linear_interpolate(effect.mult, amount); 51 | new_effect.add = add.linear_interpolate(effect.add, amount); 52 | return new_effect; 53 | } 54 | 55 | inline FlashColorEffect operator *(FlashColorEffect effect) { 56 | FlashColorEffect new_effect; 57 | new_effect.mult = mult * effect.mult; 58 | new_effect.add = add * effect.mult + effect.add; 59 | return new_effect; 60 | } 61 | 62 | inline bool is_empty() const { 63 | return add == Color(0,0,0,0) && mult == Color(1,1,1,1); 64 | } 65 | }; 66 | 67 | class FlashElement: public Resource { 68 | GDCLASS(FlashElement, Resource); 69 | 70 | protected: 71 | FlashDocument *document; 72 | FlashElement *parent; 73 | int eid; 74 | 75 | public: 76 | FlashDocument *get_document() const; 77 | void set_document(FlashDocument *p_document); 78 | FlashElement *get_parent() const; 79 | void set_parent(FlashElement *parent); 80 | int get_eid() const { return eid; } 81 | void set_eid(int p_eid) { eid = p_eid; } 82 | template T* find_parent() const; 83 | 84 | static void _bind_methods(); 85 | template Ref add_child(Ref parser, List< Ref > *elements = NULL); 86 | Color parse_color(const String &p_color) const; 87 | FlashColorEffect parse_color_effect(Ref parser) const; 88 | Transform2D parse_transform(Ref parser); 89 | 90 | virtual void setup(FlashDocument *p_document, FlashElement *p_parent); 91 | 92 | template 93 | void setup(FlashDocument *p_document, FlashElement *p_parent, List> *children); 94 | template 95 | void setup(List> *children); 96 | virtual Error parse(Ref parser); 97 | }; 98 | 99 | class FlashTextureRect: public Resource { 100 | GDCLASS(FlashTextureRect, Resource); 101 | 102 | int index; 103 | Rect2 region; 104 | Rect2 margin; 105 | Vector2 original_size; 106 | 107 | static void _bind_methods(); 108 | 109 | public: 110 | FlashTextureRect(): 111 | index(0), 112 | region(Rect2()), 113 | margin(Rect2()), 114 | original_size(Vector2()){} 115 | 116 | void set_index(const int p_index) { index = p_index; } 117 | int get_index() const { return index; } 118 | void set_region(const Rect2 &p_region) { region = p_region; } 119 | Rect2 get_region() const { return region; } 120 | void set_margin(const Rect2 &p_margin) { margin = p_margin; } 121 | Rect2 get_margin() const { return margin; } 122 | void set_original_size(const Vector2 &p_original_size) { original_size = p_original_size; } 123 | Vector2 get_original_size() const { return original_size; } 124 | }; 125 | 126 | class FlashDocument: public FlashElement { 127 | GDCLASS(FlashDocument, FlashElement); 128 | 129 | String document_path; 130 | Dictionary symbols; 131 | Dictionary bitmaps; 132 | float frame_size; 133 | List > timelines; 134 | int last_eid; 135 | Ref atlas; 136 | Dictionary variants; 137 | int variated_symbols_count; 138 | 139 | static String invalid_character; 140 | 141 | public: 142 | FlashDocument(): 143 | document_path(""), 144 | frame_size(1.0/24.0), 145 | last_eid(0){} 146 | 147 | static void _bind_methods(); 148 | 149 | template Ref element(FlashElement *parent = NULL); 150 | static String validate_token(String p_token); 151 | 152 | static Ref from_file(const String &p_path); 153 | Error load_file(const String &path); 154 | 155 | Vector2 get_atlas_size() const; 156 | Ref get_atlas() const { return atlas; } 157 | void set_atlas(Ref p_atlas) { atlas = p_atlas; } 158 | String get_document_path() const { return document_path; } 159 | Dictionary get_symbols() const { return symbols; } 160 | void set_symbols(Dictionary p_symbols) { symbols = p_symbols; } 161 | Dictionary get_bitmaps() const { return bitmaps; } 162 | void set_bitmaps(Dictionary p_bitmaps) { bitmaps = p_bitmaps; } 163 | Array get_timelines(); 164 | void set_timelines(Array p_timelines); 165 | int get_timelines_count() const { return timelines.size(); } 166 | Ref get_timeline(int idx) const { return timelines[idx]; } 167 | float get_duration(String timeline = String(), String label = String()); 168 | Dictionary get_variants() const; 169 | void cache_variants(); 170 | int get_variated_symbols_count() const { return variated_symbols_count; } 171 | 172 | FlashTimeline* get_timeline(String token); 173 | void parse_timeline(const String &path); 174 | Ref get_bitmap_rect(const String &bitmap_name); 175 | inline float get_frame_size() const { return frame_size; } 176 | Ref get_main_timeline(); 177 | 178 | virtual void setup(FlashDocument *p_document, FlashElement *p_parent); 179 | virtual Error parse(Ref parser); 180 | void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 181 | 182 | }; 183 | 184 | class FlashBitmapItem: public FlashElement { 185 | GDCLASS(FlashBitmapItem, FlashElement); 186 | String name; 187 | String bitmap_path; 188 | Ref texture; 189 | public: 190 | FlashBitmapItem(): 191 | name(""), 192 | bitmap_path("") {} 193 | 194 | static void _bind_methods(); 195 | 196 | String get_name() const { return name; }; 197 | String get_bitmap_path() const { return bitmap_path; }; 198 | Ref get_texture() const { return texture; }; 199 | void set_texture(Ref p_texture) { texture = p_texture; }; 200 | 201 | virtual Error parse(Ref xml); 202 | 203 | 204 | }; 205 | 206 | class FlashTimeline: public FlashElement { 207 | GDCLASS(FlashTimeline, FlashElement); 208 | friend FlashDocument; 209 | 210 | String token; 211 | String local_path; 212 | String clips_header; 213 | int duration; 214 | Dictionary clips; 215 | Dictionary events; 216 | Dictionary variants; 217 | List> layers; 218 | List> masks; 219 | int variation_idx; 220 | 221 | public: 222 | FlashTimeline(): 223 | 224 | token(""), 225 | duration(0), 226 | variation_idx(-1){} 227 | 228 | static void _bind_methods(); 229 | 230 | String get_token() const { return token; }; 231 | void set_token(String p_library_name) { token = p_library_name; } 232 | String get_local_path() const { return local_path; } 233 | void set_local_path(String p_local_path) { local_path = p_local_path; } 234 | String get_clips_header() const { return clips_header; } 235 | void set_clips_header(String p_clips_header) { clips_header = p_clips_header; } 236 | int get_duration() const { return duration; } 237 | void set_duration(int p_duration) { duration = p_duration; } 238 | Dictionary get_variants() const { return variants; } 239 | void set_variants(Dictionary p_variants) { variants = p_variants; } 240 | Dictionary get_clips() const { return clips; } 241 | void set_clips(Dictionary p_clips) { clips = p_clips; } 242 | Dictionary get_events() const { return events; } 243 | void set_events(Dictionary p_events) { events = p_events; } 244 | Array get_layers(); 245 | void set_layers(Array p_layers); 246 | int get_variation_idx() const { return variation_idx; } 247 | void set_variation_idx(int p_variation_idx) { variation_idx = p_variation_idx; } 248 | 249 | Ref get_layer(int idx); 250 | void add_label(const String &name, const String &label_type, float start, float duration); 251 | virtual void setup(FlashDocument *p_document, FlashElement *p_parent); 252 | virtual Error parse(Ref xml); 253 | void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 254 | }; 255 | 256 | class FlashLayer: public FlashElement { 257 | GDCLASS(FlashLayer, FlashElement); 258 | friend FlashDocument; 259 | friend FlashFrame; 260 | 261 | int index; 262 | String layer_name; 263 | String type; 264 | int duration; 265 | int mask_id; 266 | Color color; 267 | List> frames; 268 | 269 | public: 270 | FlashLayer(): 271 | index(0), 272 | layer_name(""), 273 | type(""), 274 | duration(0), 275 | mask_id(0), 276 | color(Color()){} 277 | 278 | static void _bind_methods(); 279 | 280 | int get_index() const { return index; } 281 | void set_index(int p_index) { index = p_index; } 282 | String get_layer_name() const { return layer_name; }; 283 | void set_layer_name(String p_name) { layer_name = p_name; } 284 | String get_type() const { return type; } 285 | void set_type(String p_type) { type = p_type; } 286 | int get_duration() const { return duration; } 287 | void set_duration(int p_duration) { duration = p_duration; } 288 | int get_mask_id() const { return mask_id; } 289 | void set_mask_id(int p_mask_id) { mask_id = p_mask_id; } 290 | Array get_frames(); 291 | void set_frames(Array p_frames); 292 | 293 | virtual void setup(FlashDocument *p_document, FlashElement *p_parent); 294 | virtual Error parse(Ref xml); 295 | void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 296 | 297 | }; 298 | 299 | class FlashDrawing: public FlashElement { 300 | GDCLASS(FlashDrawing, FlashElement); 301 | 302 | protected: 303 | Transform2D transform; 304 | 305 | public: 306 | FlashDrawing(): transform(Transform2D()){} 307 | static void _bind_methods(); 308 | Transform2D get_transform() const { return transform; } 309 | void set_transform(Transform2D p_transform) { transform = p_transform; } 310 | virtual void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 311 | }; 312 | 313 | class FlashFrame: public FlashElement { 314 | GDCLASS(FlashFrame, FlashElement); 315 | friend FlashDocument; 316 | friend FlashLayer; 317 | 318 | int index; 319 | int duration; 320 | String frame_name; 321 | String label_type; 322 | String keymode; 323 | String tween_type; 324 | FlashColorEffect color_effect; 325 | 326 | List> elements; 327 | List> tweens; 328 | 329 | public: 330 | FlashFrame(): 331 | index(0), 332 | duration(1), 333 | frame_name(""), 334 | label_type(""), 335 | keymode(""), 336 | tween_type("none"){} 337 | 338 | static void _bind_methods(); 339 | 340 | int get_index() const { return index; }; 341 | void set_index(int p_index) { index = p_index; } 342 | int get_duration() const { return duration; } 343 | void set_duration(int p_duration) { duration = p_duration; } 344 | String get_frame_name() const { return frame_name; } 345 | void set_frame_name(String p_name) { frame_name = p_name; } 346 | String get_label_type() const { return label_type; } 347 | void set_label_type(String p_label_type) { label_type = p_label_type; } 348 | String get_keymode() const { return keymode; } 349 | void set_keymode(String p_keymode) { keymode = p_keymode; } 350 | String get_tween_type() const { return tween_type; } 351 | void set_tween_type(String p_tween_type) { tween_type = p_tween_type; } 352 | PoolColorArray get_color_effect() const; 353 | void set_color_effect(PoolColorArray p_color_effect); 354 | Array get_elements(); 355 | void set_elements(Array p_elements); 356 | Array get_tweens(); 357 | void set_tweens(Array p_tweens); 358 | 359 | virtual void setup(FlashDocument *p_document, FlashElement *p_parent); 360 | virtual Error parse(Ref xml); 361 | void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 362 | 363 | 364 | }; 365 | 366 | class FlashInstance: public FlashDrawing { 367 | GDCLASS(FlashInstance, FlashDrawing); 368 | 369 | Vector2 center_point; 370 | Vector2 transformation_point; 371 | int first_frame; 372 | String loop; 373 | String timeline_token; 374 | String layer_name; 375 | 376 | FlashTimeline* timeline; 377 | 378 | 379 | public: 380 | FlashColorEffect color_effect; 381 | FlashInstance(): 382 | center_point(Vector2()), 383 | transformation_point(Vector2()), 384 | first_frame(0), 385 | loop("loop"), 386 | timeline_token(""), 387 | layer_name(""), 388 | timeline(nullptr), 389 | color_effect(FlashColorEffect()){} 390 | 391 | static void _bind_methods(); 392 | 393 | int get_first_frame() const { return first_frame; } 394 | void set_first_frame(int p_first_frame) { first_frame = p_first_frame; } 395 | String get_loop() const { return loop; } 396 | void set_loop(String p_loop) { loop = p_loop; } 397 | PoolColorArray get_color_effect() const; 398 | void set_color_effect(PoolColorArray p_color_effect); 399 | String get_timeline_token() const { return timeline_token; } 400 | void set_timeline_token(String p_name) { timeline_token = p_name; } 401 | String get_layer_name() const { return layer_name; } 402 | 403 | virtual void setup(FlashDocument *p_document, FlashElement *p_parent); 404 | FlashTimeline* get_timeline(); 405 | virtual Error parse(Ref xml); 406 | virtual void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 407 | }; 408 | 409 | class FlashShape: public FlashDrawing { 410 | GDCLASS(FlashShape, FlashDrawing); 411 | 412 | public: 413 | virtual Error parse(Ref xml); 414 | 415 | }; 416 | 417 | class FlashGroup: public FlashDrawing { 418 | GDCLASS(FlashGroup, FlashDrawing); 419 | 420 | List> members; 421 | 422 | public: 423 | static void _bind_methods(); 424 | Array get_members(); 425 | void set_members(Array p_members); 426 | 427 | List> all_members() const; 428 | virtual void setup(FlashDocument *p_document, FlashElement *p_parent); 429 | virtual Error parse(Ref xml); 430 | virtual void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 431 | }; 432 | 433 | 434 | class FlashBitmapInstance: public FlashDrawing { 435 | GDCLASS(FlashBitmapInstance, FlashDrawing); 436 | 437 | String library_item_name; 438 | 439 | Vector uvs; 440 | Ref texture; 441 | 442 | public: 443 | FlashBitmapInstance(): 444 | library_item_name(""){} 445 | 446 | static void _bind_methods(); 447 | 448 | Ref get_texture(); 449 | String get_library_item_name() const { return library_item_name; } 450 | void set_library_item_name(String p_library_item_name) { library_item_name = p_library_item_name; } 451 | 452 | Error parse(Ref xml); 453 | virtual void animation_process(FlashPlayer* node, float time, float delta, Transform2D tr=Transform2D(), FlashColorEffect effect=FlashColorEffect()); 454 | }; 455 | 456 | class FlashTween: public FlashElement { 457 | GDCLASS(FlashTween, FlashElement); 458 | public: 459 | enum Method { 460 | NONE, 461 | CLASSIC, 462 | IN_QUAD, OUT_QUAD, INOUT_QUAD, 463 | IN_CUBIC, OUT_CUBIC, INOUT_CUBIC, 464 | IN_QUART, OUT_QUART, INOUT_QUART, 465 | IN_QUINT, OUT_QUINT, INOUT_QUINT, 466 | IN_SINE, OUT_SINE, INOUT_SINE, 467 | IN_BACK, OUT_BACK, INOUT_BACK, 468 | IN_CIRC, OUT_CIRC, INOUT_CIRC, 469 | IN_BOUNCE, OUT_BOUNCE, INOUT_BOUNCE, 470 | IN_ELASTIC, OUT_ELASTIC, INOUT_ELASTIC, 471 | CUSTOM 472 | }; 473 | 474 | private: 475 | String target; 476 | PoolVector2Array points; 477 | Method method; 478 | float intensity; 479 | 480 | public: 481 | 482 | 483 | FlashTween(): 484 | target("all"), 485 | method(NONE), 486 | intensity(0){} 487 | 488 | static void _bind_methods(); 489 | String get_target() const { return target; } 490 | void set_target(String p_target) { target = p_target; } 491 | Method get_method() const { return method; } 492 | void set_method(Method p_method) { method = p_method; } 493 | float get_intensity() const { return intensity; } 494 | void set_intensity(float p_intesity) { intensity = p_intesity; } 495 | PoolVector2Array get_points() const { return points; } 496 | void set_points(PoolVector2Array p_points) { points = p_points; } 497 | 498 | Error parse(Ref xml); 499 | 500 | float interpolate(float time); 501 | }; 502 | 503 | VARIANT_ENUM_CAST(FlashTween::Method); 504 | 505 | class ResourceFormatLoaderFlashTexture: public ResourceFormatLoader { 506 | public: 507 | virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); 508 | virtual void get_recognized_extensions(List *p_extensions) const; 509 | virtual bool handles_type(const String &p_type) const; 510 | virtual String get_resource_type(const String &p_path) const; 511 | }; 512 | 513 | 514 | 515 | 516 | #endif -------------------------------------------------------------------------------- /register_types.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 24 | #include 25 | #include 26 | #include "register_types.h" 27 | #include "flash_player.h" 28 | #include "flash_resources.h" 29 | #include "animation_node_flash.h" 30 | 31 | #ifdef TOOLS_ENABLED 32 | #include "core/engine.h" 33 | #include "editor/editor_export.h" 34 | #include "editor/editor_node.h" 35 | #include "resource_importer_flash.h" 36 | 37 | class EditorExportFlash : public EditorExportPlugin { 38 | 39 | GDCLASS(EditorExportFlash, EditorExportPlugin); 40 | bool export_processed; 41 | 42 | public: 43 | virtual void _export_begin(const Set &p_features, bool p_debug, const String &p_path, int p_flags) { 44 | export_processed = false; 45 | } 46 | 47 | 48 | virtual void _export_file(const String &p_path, const String &p_type, const Set &p_features) { 49 | if (p_type != "FlashDocument") return; 50 | Ref config; 51 | config.instance(); 52 | Error err = config->load(p_path + ".import"); 53 | if (err != OK) return; 54 | 55 | List remaps; 56 | config->get_section_keys("remap", &remaps); 57 | 58 | Set remap_features; 59 | 60 | for (List::Element *F = remaps.front(); F; F = F->next()) { 61 | 62 | String remap = F->get(); 63 | String feature = remap.get_slice(".", 1); 64 | if (p_features.has(feature)) { 65 | remap_features.insert(feature); 66 | } 67 | } 68 | String imported_doc_path; 69 | for (List::Element *F = remaps.front(); F; F = F->next()) { 70 | String remap = F->get(); 71 | if (remap == "path") { 72 | String imported_doc_path = config->get_value("remap", remap); 73 | String texture_path = imported_doc_path.substr(0, imported_doc_path.length()-3) + "ftex"; 74 | add_file(texture_path, FileAccess::get_file_as_array(texture_path), false); 75 | } else if (remap.begins_with("path.")) { 76 | String feature = remap.get_slice(".", 1); 77 | if (remap_features.has(feature)) { 78 | String imported_doc_path = config->get_value("remap", remap); 79 | String texture_path = imported_doc_path.substr(0, imported_doc_path.length()-3) + "ftex"; 80 | add_file(texture_path, FileAccess::get_file_as_array(texture_path), false); 81 | } 82 | } 83 | } 84 | } 85 | }; 86 | 87 | static void _editor_init() { 88 | Ref flash_import; 89 | flash_import.instance(); 90 | ResourceFormatImporter::get_singleton()->add_importer(flash_import); 91 | 92 | Ref flash_export; 93 | flash_export.instance(); 94 | EditorExport::get_singleton()->add_export_plugin(flash_export); 95 | } 96 | #endif 97 | 98 | 99 | Ref resource_loader_flash_texture; 100 | 101 | void register_flash_types() { 102 | // core flash classes 103 | ClassDB::register_class(); 104 | #ifdef MODULE_FLASH_WITH_ANIMATION_NODES 105 | ClassDB::register_class(); 106 | ClassDB::register_class(); 107 | #endif 108 | 109 | // resources 110 | ClassDB::register_virtual_class(); 111 | ClassDB::register_class(); 112 | ClassDB::register_class(); 113 | ClassDB::register_class(); 114 | ClassDB::register_class(); 115 | ClassDB::register_class(); 116 | ClassDB::register_class(); 117 | ClassDB::register_class(); 118 | ClassDB::register_class(); 119 | ClassDB::register_class(); 120 | ClassDB::register_class(); 121 | ClassDB::register_class(); 122 | ClassDB::register_class(); 123 | 124 | 125 | // loader 126 | resource_loader_flash_texture.instance(); 127 | ResourceLoader::add_resource_format_loader(resource_loader_flash_texture); 128 | 129 | #ifdef TOOLS_ENABLED 130 | EditorNode::add_init_callback(_editor_init); 131 | #endif 132 | } 133 | 134 | void unregister_flash_types() { 135 | ResourceLoader::remove_resource_format_loader(resource_loader_flash_texture); 136 | resource_loader_flash_texture.unref(); 137 | } 138 | -------------------------------------------------------------------------------- /register_types.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | void register_flash_types(); 24 | void unregister_flash_types(); 25 | -------------------------------------------------------------------------------- /resource_importer_flash.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "resource_importer_flash.h" 32 | #include "flash_resources.h" 33 | 34 | const int ResourceImporterFlash::importer_version = 12; 35 | 36 | String ResourceImporterFlash::get_importer_name() const { 37 | return "flash"; 38 | } 39 | 40 | String ResourceImporterFlash::get_visible_name() const { 41 | return "Flash"; 42 | } 43 | 44 | void ResourceImporterFlash::get_recognized_extensions(List *p_extensions) const { 45 | p_extensions->push_back("zip"); 46 | p_extensions->push_back("zfl"); 47 | } 48 | 49 | String ResourceImporterFlash::get_save_extension() const { 50 | return "res"; 51 | } 52 | 53 | String ResourceImporterFlash::get_resource_type() const { 54 | return "FlashDocument"; 55 | } 56 | 57 | int ResourceImporterFlash::get_importer_version() const { 58 | return ResourceImporterFlash::importer_version; 59 | } 60 | 61 | void ResourceImporterFlash::get_import_options(List *r_options, int p_preset) const { 62 | 63 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/downscale", PROPERTY_HINT_ENUM, "Disabled,x2,x4"), 0)); 64 | r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), true)); 65 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless (PNG),Video RAM (S3TC/ETC/BPTC),Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); 66 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), 0)); 67 | r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), true)); 68 | r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), true)); 69 | r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/srgb", PROPERTY_HINT_ENUM, "Disable,Enable"), 0)); 70 | } 71 | 72 | bool ResourceImporterFlash::are_import_settings_valid(const String &p_path) const { 73 | 74 | //will become invalid if formats are missing to import 75 | Dictionary metadata = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path); 76 | 77 | if (!metadata.has("vram_texture")) { 78 | return false; 79 | } 80 | 81 | #ifndef GODOT_FEATURE_IMPORTER_VERSION 82 | int imported_with_version = metadata.get("importer_version", 0); 83 | if (imported_with_version != get_importer_version()) { 84 | return false; 85 | } 86 | #endif 87 | 88 | bool vram = metadata["vram_texture"]; 89 | if (!vram) { 90 | return true; //do not care about non vram 91 | } 92 | 93 | Vector formats_imported; 94 | if (metadata.has("imported_formats")) { 95 | formats_imported = metadata["imported_formats"]; 96 | } 97 | 98 | int index = 0; 99 | bool valid = true; 100 | while (compression_formats[index]) { 101 | String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]); 102 | bool test = ProjectSettings::get_singleton()->get(setting_path); 103 | if (test) { 104 | if (formats_imported.find(compression_formats[index]) == -1) { 105 | valid = false; 106 | break; 107 | } 108 | } 109 | index++; 110 | } 111 | 112 | return valid; 113 | } 114 | 115 | Error ResourceImporterFlash::import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *r_gen_files, Variant *r_metadata) { 116 | int compress_mode = p_options["compress/mode"]; 117 | int repeat = p_options["flags/repeat"]; 118 | bool filter = p_options["flags/filter"]; 119 | bool mipmaps = p_options["flags/mipmaps"]; 120 | int srgb = p_options["flags/srgb"]; 121 | int downscale = p_options["process/downscale"]; 122 | bool fix_alpha_border = p_options["process/fix_alpha_border"]; 123 | 124 | int tex_flags = 0; 125 | if (repeat > 0) 126 | tex_flags |= Texture::FLAG_REPEAT; 127 | if (repeat == 2) 128 | tex_flags |= Texture::FLAG_MIRRORED_REPEAT; 129 | if (filter) 130 | tex_flags |= Texture::FLAG_FILTER; 131 | if (mipmaps || compress_mode == COMPRESS_VIDEO_RAM) 132 | tex_flags |= Texture::FLAG_MIPMAPS; 133 | if (srgb == 1) 134 | tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR; 135 | 136 | // read zip and extract it to tmp dir 137 | //const Vector2 PADDING(1, 1); 138 | FileAccess *zip_source_file; 139 | zlib_filefunc_def io = zipio_create_io_from_file(&zip_source_file); 140 | zipFile zip_source = unzOpen2(p_source_file.utf8().get_data(), &io); 141 | if (zip_source == NULL) return FAILED; 142 | 143 | DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); 144 | String tmp_dir = p_save_path + ".tmp/"; 145 | da->make_dir_recursive(tmp_dir); 146 | 147 | if (unzGoToFirstFile(zip_source) != UNZ_OK) { 148 | return FAILED; 149 | } 150 | 151 | String document_path = ""; 152 | do { 153 | char char_filename[1024]; 154 | unz_file_info info; 155 | unzGetCurrentFileInfo(zip_source, &info, char_filename, sizeof(char_filename), NULL, 0, NULL, 0); 156 | String file_name = String::utf8(char_filename); 157 | if (file_name.ends_with("/")) continue; 158 | PoolByteArray data; 159 | data.resize(info.uncompressed_size); 160 | if (unzOpenCurrentFile(zip_source) != UNZ_OK) { 161 | ERR_FAIL_V_MSG(FAILED, "Could not open file within zip archive."); 162 | } 163 | unzReadCurrentFile(zip_source, data.write().ptr(), info.uncompressed_size); 164 | String file_path = tmp_dir + file_name; 165 | da->make_dir_recursive(file_path.get_base_dir()); 166 | FileAccess *file = FileAccess::open(file_path, FileAccess::WRITE); 167 | file->store_buffer(data.read().ptr(), data.size()); 168 | file->close(); 169 | unzCloseCurrentFile(zip_source); 170 | memdelete(file); 171 | if (document_path == String() && file_name.get_file() == "DOMDocument.xml"){ 172 | document_path = tmp_dir + file_name; 173 | } 174 | } while (unzGoToNextFile(zip_source) == UNZ_OK); 175 | if (document_path == "") { 176 | da->remove(tmp_dir); 177 | return FAILED; 178 | } 179 | 180 | 181 | // parse document 182 | Ref doc = FlashDocument::from_file(document_path); 183 | if (!doc.is_valid()) { 184 | da->remove(tmp_dir); 185 | return FAILED; 186 | } 187 | 188 | 189 | 190 | // make atlas 191 | // Array items_array = doc->get_bitmaps().values(); 192 | // Vector> items; 193 | // Vector sizes; 194 | // Vector positions; 195 | // Vector> images; 196 | // Point2i atlas_size; 197 | // for (int i=0; i item = items_array[i]; 199 | // items.push_back(item); 200 | // Ref img; img.instance(); 201 | // String img_path = doc->get_document_path() + "/" + item->get_bitmap_path(); 202 | // img->load(img_path); 203 | // if (img->get_format() != Image::FORMAT_RGBA8) { 204 | // img->convert(Image::FORMAT_RGBA8); 205 | // } 206 | // images.push_back(img); 207 | // sizes.push_back(img->get_size() + 2 * PADDING); 208 | // } 209 | // Geometry::make_atlas(sizes, positions, atlas_size); 210 | // Ref atlas; atlas.instance(); 211 | // atlas->create(atlas_size.x, atlas_size.y, false, Image::FORMAT_RGBA8); 212 | // if (atlas_size.x > 4096 || atlas_size.y > 4096) { 213 | // ERR_FAIL_V_MSG(FAILED, String("Generated atlas size ") + atlas_size + " bigger then maximum 4096x4096"); 214 | // } 215 | // atlas_size = Vector2(next_power_of_2(atlas_size.x), next_power_of_2(atlas_size.y)); 216 | 217 | // for (int i=0; iblit_rect(images[i], Rect2(Vector2(), sizes[i] - 2 * PADDING), positions[i] + PADDING); 219 | // } 220 | // String atlas_path = p_save_path + ".atlas.png"; 221 | // String atlas_imported_path = p_save_path + ".atlas"; 222 | // Error saved = atlas->save_png(atlas_path); 223 | // atlas.unref(); 224 | 225 | // Error imported = ResourceImporterTexture::import(atlas_path, atlas_imported_path, p_options, r_platform_variants, r_gen_files, r_metadata); 226 | // if (imported != OK) { 227 | // da->remove(tmp_dir); 228 | // return imported; 229 | // } 230 | 231 | String spritesheet_files_path = doc->get_document_path() + "/spritesheets.list"; 232 | Vector spritesheet_files = FileAccess::get_file_as_string(spritesheet_files_path).split("\n"); 233 | while (spritesheet_files.size() > 0 && spritesheet_files[spritesheet_files.size()-1] == String()) { 234 | spritesheet_files.remove(spritesheet_files.size()-1); 235 | } 236 | 237 | Vector> spritesheet_images; 238 | Dictionary spritesheets_layout; 239 | for (int i=0; iget_document_path() + "/" + spritesheet_files[i]; 241 | Variant json_variant; 242 | String json_err_msg; 243 | int json_err_line; 244 | String json_text = FileAccess::get_file_as_string(spriteheet_base_path + ".json"); 245 | JSON::parse(json_text, json_variant, json_err_msg, json_err_line); 246 | Dictionary json = json_variant; 247 | Dictionary frames = json["frames"]; 248 | for (int j=0; j img; 255 | img.instance(); 256 | img->load(spriteheet_path); 257 | if (img->get_format() != Image::FORMAT_RGBA8) { 258 | img->convert(Image::FORMAT_RGBA8); 259 | } 260 | if (fix_alpha_border) { 261 | img->fix_alpha_edges(); 262 | } 263 | for (int j=0; jshrink_x2(); 265 | if (fix_alpha_border) { 266 | img->fix_alpha_edges(); 267 | } 268 | } 269 | //img->optimize_channels(); 270 | spritesheet_images.push_back(img); 271 | } 272 | 273 | Array items = doc->get_bitmaps().values(); 274 | for (int i=0; i item = items[i]; 276 | Dictionary frame_info = spritesheets_layout.get(item->get_name().replace_first("gdexp/", ""), Dictionary()); 277 | if (frame_info.size() == 0) continue; 278 | 279 | Ref frame; 280 | frame.instance(); 281 | 282 | Dictionary frame_region = frame_info["frame"]; 283 | Rect2 region = Rect2( 284 | frame_region["x"], 285 | frame_region["y"], 286 | frame_region["w"], 287 | frame_region["h"] 288 | ); 289 | Vector2 original_size = region.size; 290 | for (int j=0; jset_region(region); 294 | frame->set_index(frame_info["texture_idx"]); 295 | frame->set_original_size(original_size); 296 | 297 | item->set_texture(frame); 298 | } 299 | 300 | String extension = get_save_extension(); 301 | Array formats_imported; 302 | if (compress_mode == COMPRESS_VIDEO_RAM) { 303 | //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). 304 | //Android, GLES 2.x 305 | 306 | bool ok_on_pc = false; 307 | 308 | 309 | if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc")) { 310 | _save_tex(p_save_path + ".s3tc.ftex", spritesheet_images, 311 | compress_mode, Image::COMPRESS_S3TC, mipmaps, tex_flags); 312 | doc->set_atlas(ResourceLoader::load(p_save_path + ".s3tc.ftex")); 313 | ResourceSaver::save(p_save_path + ".s3tc." + extension, doc); 314 | r_platform_variants->push_back("s3tc"); 315 | ok_on_pc = true; 316 | formats_imported.push_back("s3tc"); 317 | } 318 | 319 | if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { 320 | _save_tex(p_save_path + ".etc2.ftex", spritesheet_images, 321 | compress_mode, Image::COMPRESS_ETC2, mipmaps, tex_flags); 322 | doc->set_atlas(ResourceLoader::load(p_save_path + ".etc2.ftex")); 323 | ResourceSaver::save(p_save_path + ".etc2." + extension, doc); 324 | r_platform_variants->push_back("etc2"); 325 | formats_imported.push_back("etc2"); 326 | } 327 | 328 | if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { 329 | _save_tex(p_save_path + ".etc.ftex", spritesheet_images, 330 | compress_mode, Image::COMPRESS_ETC, mipmaps, tex_flags); 331 | doc->set_atlas(ResourceLoader::load(p_save_path + ".etc.ftex")); 332 | ResourceSaver::save(p_save_path + ".etc." + extension, doc); 333 | r_platform_variants->push_back("etc"); 334 | formats_imported.push_back("etc"); 335 | } 336 | 337 | if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { 338 | _save_tex(p_save_path + ".pvrtc.ftex", spritesheet_images, 339 | compress_mode, Image::COMPRESS_PVRTC4, mipmaps, tex_flags); 340 | doc->set_atlas(ResourceLoader::load(p_save_path + ".pvrtc.ftex")); 341 | ResourceSaver::save(p_save_path + ".pvrtc." + extension, doc); 342 | r_platform_variants->push_back("pvrtc"); 343 | formats_imported.push_back("pvrtc"); 344 | } 345 | 346 | if (!ok_on_pc) { 347 | //EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC."); 348 | } 349 | } else { 350 | //import normally 351 | _save_tex(p_save_path + ".ftex", spritesheet_images, 352 | compress_mode, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags); 353 | doc->set_atlas(ResourceLoader::load(p_save_path + ".ftex")); 354 | ResourceSaver::save(p_save_path + "." + extension, doc); 355 | } 356 | 357 | if (r_metadata) { 358 | Dictionary metadata; 359 | #ifndef GODOT_FEATURE_IMPORTER_VERSION 360 | metadata["importer_version"] = get_importer_version(); 361 | #endif 362 | metadata["vram_texture"] = compress_mode == COMPRESS_VIDEO_RAM; 363 | if (formats_imported.size()) { 364 | metadata["imported_formats"] = formats_imported; 365 | } 366 | *r_metadata = metadata; 367 | } 368 | 369 | return OK; 370 | 371 | 372 | 373 | 374 | // save document for each texture format 375 | // List formats; 376 | // if (r_platform_variants->size() > 0) { 377 | // for (List::Element *E = r_platform_variants->front(); E; E = E->next()) { 378 | // String format = "." + E->get() + ".ftex"; 379 | // if (formats.has(format)) continue; 380 | // formats.push_back(format); 381 | // } 382 | // } else { 383 | // formats.push_back(".ftex"); 384 | // } 385 | 386 | 387 | 388 | 389 | 390 | 391 | // for (List::Element *E = formats.front(); E; E = E->next()) { 392 | // String fmt = E->get(); 393 | // String stex_variant_path = atlas_imported_path + fmt.substr(0, fmt.length()-4) + "stex"; 394 | // String atlas_variant_path = atlas_imported_path + fmt; 395 | // da->rename(stex_variant_path, atlas_variant_path); 396 | // Ref atlas_texture = ResourceLoader::load(atlas_variant_path); 397 | // for (int i=0; i texture; texture.instance(); 399 | // texture->set_atlas(atlas_texture); 400 | // texture->set_region(Rect2(positions[i] + PADDING, sizes[i] - 2 * PADDING)); 401 | // Ref item = items[i]; 402 | // item->set_texture(texture); 403 | // } 404 | // String save_path = p_save_path + fmt.substr(0, fmt.length()-4) + get_save_extension(); 405 | // ResourceSaver::save(save_path, doc); 406 | // } 407 | 408 | // da->remove(tmp_dir); 409 | 410 | return OK; 411 | } 412 | 413 | Error ResourceImporterFlash::_save_tex( 414 | const String &p_path, 415 | const Vector> &p_spritesheets, 416 | int p_compress_mode, 417 | Image::CompressMode p_vram_compression, 418 | bool p_mipmaps, 419 | int p_texture_flags 420 | ) { 421 | Error error; 422 | 423 | Dictionary info; 424 | info["flags"] = p_texture_flags; 425 | if (p_spritesheets.size() == 0){ 426 | info["width"] = 0; 427 | info["height"] = 0; 428 | info["format"] = Image::FORMAT_RGBA8; 429 | } else { 430 | info["format"] = p_spritesheets[0]->get_format(); 431 | info["width"] = p_spritesheets[0]->get_width(); 432 | info["height"] = p_spritesheets[0]->get_height(); 433 | } 434 | 435 | Array images; 436 | for (int i = 0; i < p_spritesheets.size(); i++) { 437 | Ref image = p_spritesheets[i]->duplicate(); 438 | switch (p_compress_mode) { 439 | case COMPRESS_UNCOMPRESSED: 440 | case COMPRESS_LOSSLESS: { 441 | image->clear_mipmaps(); 442 | images.push_back(image); 443 | 444 | } break; 445 | case COMPRESS_VIDEO_RAM: { 446 | image->generate_mipmaps(false); 447 | Image::CompressSource csource = Image::COMPRESS_SOURCE_LAYERED; 448 | image->compress(p_vram_compression, csource, 0.95); 449 | images.push_back(image); 450 | } break; 451 | } 452 | info["format"] = image->get_format(); 453 | } 454 | info["images"] = images; 455 | PoolByteArray buff; 456 | Variant info_var = info; 457 | int len; 458 | encode_variant(info_var, NULL, len, true); 459 | buff.resize(len); 460 | { 461 | PoolByteArray::Write w = buff.write(); 462 | encode_variant(info_var, w.ptr(), len, true); 463 | } 464 | 465 | PoolVector compressed; 466 | compressed.resize(Compression::get_max_compressed_buffer_size(len, Compression::MODE_FASTLZ)); 467 | int compressed_size = Compression::compress(compressed.write().ptr(), buff.read().ptr(), len, Compression::MODE_FASTLZ); 468 | 469 | FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &error); 470 | ERR_FAIL_COND_V(error, error); 471 | { 472 | f->store_32(len); 473 | PoolByteArray::Read r = compressed.read(); 474 | f->store_buffer(r.ptr(), compressed_size); 475 | } 476 | memdelete(f); 477 | return OK; 478 | } 479 | 480 | const char *ResourceImporterFlash::compression_formats[] = { 481 | "s3tc", 482 | "etc", 483 | "etc2", 484 | "pvrtc", 485 | NULL 486 | }; 487 | String ResourceImporterFlash::get_import_settings_string() const { 488 | 489 | String s; 490 | 491 | int index = 0; 492 | while (compression_formats[index]) { 493 | String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]); 494 | bool test = ProjectSettings::get_singleton()->get(setting_path); 495 | if (test) { 496 | s += String(compression_formats[index]); 497 | } 498 | index++; 499 | } 500 | 501 | return s; 502 | } -------------------------------------------------------------------------------- /resource_importer_flash.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Yakov Borevich, Funexpected LLC 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #ifndef RESOURCE_IMPORTER_FLASH_H 24 | #define RESOURCE_IMPORTER_FLASH_H 25 | 26 | #include 27 | 28 | class ResourceImporterFlash: public ResourceImporter { 29 | GDCLASS(ResourceImporterFlash, ResourceImporter); 30 | 31 | static const int importer_version; 32 | static const char *compression_formats[]; 33 | 34 | protected: 35 | Error _save_tex( 36 | const String &p_path, 37 | const Vector> &p_spritesheets, 38 | int p_compress_mode, 39 | Image::CompressMode p_vram_compression, 40 | bool p_mipmaps, 41 | int p_texture_flags 42 | ); 43 | 44 | public: 45 | enum CompressMode { 46 | COMPRESS_LOSSLESS, 47 | COMPRESS_VIDEO_RAM, 48 | COMPRESS_UNCOMPRESSED 49 | }; 50 | 51 | virtual String get_importer_name() const; 52 | virtual String get_visible_name() const; 53 | virtual int get_preset_count() const { return 1; } 54 | virtual String get_preset_name(int p_idx) const { return "Default"; } 55 | virtual bool get_option_visibility(const String &p_option, const Map &p_options) const { return true; } 56 | virtual void get_recognized_extensions(List *p_extensions) const; 57 | virtual String get_save_extension() const; 58 | virtual void get_import_options(List *r_options, int p_preset) const; 59 | virtual bool are_import_settings_valid(const String &p_path) const; 60 | virtual String get_import_settings_string() const; 61 | 62 | virtual String get_resource_type() const; 63 | #ifdef GODOT_FEATURE_IMPORTER_VERSION 64 | virtual 65 | #endif 66 | int get_importer_version() const; 67 | 68 | virtual Error import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *r_gen_files = NULL, Variant *r_metadata = NULL); 69 | 70 | }; 71 | 72 | 73 | 74 | #endif --------------------------------------------------------------------------------