commit 9b20b6f7d955a6f1c46ed35322ac4e0e21da8806 Author: Relintai Date: Fri Jan 24 15:29:39 2020 +0100 Initial commit. Mostly bindings-related work. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a2e916 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.import +*.d +*.o +*.meta +*.obj +*.pyc +*.bc +*.os \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..190891a --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Péter Magyar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ea80e61 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Procedural Animations + +This is a Procedural Animations engine module for the GODOT Engine. + +clone this repository into your `/modules/`, and compile. + +``` +cd /modules + +git clone https://github.com/Relintai/procedural_animations procedural_animation + +``` diff --git a/SCsub b/SCsub new file mode 100644 index 0000000..aa23d14 --- /dev/null +++ b/SCsub @@ -0,0 +1,28 @@ +import os + +Import('env') + +module_env = env.Clone() + +if os.path.isdir('../mesh_data_resource'): + module_env.Append(CPPDEFINES=['MESH_DATA_RESOURCE_PRESENT']) + + +sources = [ + "register_types.cpp", + + "procedural_animation.cpp", + "procedural_animation_player.cpp", +] + +if ARGUMENTS.get('custom_modules_shared', 'no') == 'yes': + # Shared lib compilation + module_env.Append(CCFLAGS=['-fPIC']) + module_env['LIBS'] = [] + shared_lib = module_env.SharedLibrary(target='#bin/procedural_animations', source=sources) + shared_lib_shim = shared_lib[0].name.rsplit('.', 1)[0] + env.Append(LIBS=[shared_lib_shim]) + env.Append(LIBPATH=['#bin']) +else: + # Static compilation + module_env.add_source_files(env.modules_sources, sources) diff --git a/config.py b/config.py new file mode 100644 index 0000000..767480d --- /dev/null +++ b/config.py @@ -0,0 +1,19 @@ + + +def can_build(env, platform): + return True + + +def configure(env): + pass + + +def get_doc_classes(): + return [ + "ProceduralAnimation", + "ProceduralAnimationPlayer", + ] + +def get_doc_path(): + return "doc_classes" + diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..7f5c973 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,26 @@ +_build/ +env/ + +classes/* +!classes/index.rst + +__pycache__ +*.pyc +*~ +.directory +.vs/ +.vscode/ +*.mo + +*.swo +*.swp + +.DS_Store +[Tt]humbs.db +[Tt]humbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +__MACOSX +*.lnk +[Dd]esktop.ini diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..dcee282 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= -j auto +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 0000000..d7f65eb --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,273 @@ +/** + * Various tweaks to the Read the Docs theme to better conform with Godot's visual identity. + */ + + :root { + --navbar-background-color: #292c37; + --navbar-background-color-hover: #292c37; + --navbar-background-color-active: #212d51; + --navbar-current-background-color: #000; + --navbar-current-background-color-hover: #1c1f26; + --navbar-current-background-color-active: #131e3b; + --navbar-level-1-color: #fff; + --navbar-level-2-color: #f0f8ff; + --navbar-level-3-color: #ddcd8d; + --navbar-heading-color: #d7bd6f; + --link-color-active: #105078; + --code-literal-color: #d04c60; + --highlight-background-color: #f5ffe1; + --input-background-color: #fcfcfc; + --input-focus-border-color: #5f8cff; +} + +body, +h1, +h2, +h3, +h4, +h5, +h6, +input[type="text"], +input[type="button"], +input[type="reset"], +input[type="submit"], +textarea, +legend, +.btn, +.rst-content .toctree-wrapper p.caption, +.rst-versions { + /* Use a system font stack for better performance (no Web fonts required) */ + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +} + +h1, +h2, +h3, +h4, +h5, +h6, +legend, +.rst-content .toctree-wrapper p.caption { + /* Use a lighter font for headers (Medium instead of Bold) */ + font-weight: 500; +} + +p, +article ul, +article ol, +.wy-plain-list-disc, +.wy-plain-list-decimal, +.rst-content ol.arabic, +.rst-content .section ul, +.rst-content .toctree-wrapper ul, +.rst-content .section ol { + /* Increase the line height slightly to account for the different font */ + line-height: 25px; +} + +a:hover { + text-decoration: underline; +} + +a:active { + /* Add visual feedback when clicking on a link */ + color: var(--link-color-active); +} + +a.btn:hover { + text-decoration: none; +} + +/* Code display tweaks */ + +code, +.rst-content tt, +.rst-content code { + font-size: 14px; +} + +.rst-content tt.literal, +.rst-content code.literal { + color: var(--code-literal-color); +} + +.rst-content pre.literal-block, +.rst-content div[class^="highlight"] pre, +.rst-content .linenodiv pre { + /* Increase the font size and line height in code blocks */ + font-size: 14px; + line-height: 1.5; +} + +.highlight { + background-color: var(--highlight-background-color); +} + +/* Navigation bar logo and search */ + +.wy-side-nav-search { + background-color: var(--navbar-background-color); +} + +.wy-side-nav-search > a:hover, +.wy-side-nav-search .wy-dropdown > a:hover { + background-color: var(--navbar-background-color-hover); +} + +.wy-side-nav-search > a:active, +.wy-side-nav-search .wy-dropdown > a:active { + background-color: var(--navbar-background-color-active); +} + +.wy-side-nav-search input[type="text"] { + background-color: var(--input-background-color); + /* Avoid reflowing when toggling the focus state */ + border: 2px solid transparent; + box-shadow: none; + /* Make visual feedback instant */ + transition: none; + font-size: 14px; +} + +.wy-side-nav-search input[type="text"]:focus { + border: 2px solid var(--input-focus-border-color); +} + +/* Navigation bar */ + +.wy-nav-side { + background-color: var(--navbar-background-color); +} + +.wy-menu-vertical header, +.wy-menu-vertical p.caption { + color: var(--navbar-heading-color); + + /* Improves the appearance of uppercase text */ + letter-spacing: 0.75px; +} + +/* Mobile navigation */ + +.wy-nav-top, +.wy-nav-top a { + background-color: var(--navbar-background-color); + color: var(--navbar-level-1-color); +} + +/* Version branch label below the logo */ +.wy-side-nav-search > div.version { + color: var(--navbar-level-3-color); + opacity: 0.9; +} + +/* First level of navigation items */ + +.wy-menu-vertical { + /* Account for the increased `toctree-expand` button margins */ + width: 308px; +} + +.wy-menu-vertical a { + color: var(--navbar-level-1-color); +} + +.wy-menu-vertical a:hover { + background-color: var(--navbar-background-color-hover); + color: var(--navbar-level-1-color); +} + +.wy-menu-vertical a:active { + background-color: var(--navbar-background-color-active); +} + +.wy-menu-vertical li.toctree-l1.current > a { + border: none; +} + +.wy-side-nav-search, .wy-menu-vertical a, .wy-menu-vertical a span.toctree-expand, +.wy-menu-vertical li.toctree-l2 a span.toctree-expand { + color: var(--navbar-level-3-color); + opacity: 0.9; + margin-right: 8px; +} + +.wy-side-nav-search, .wy-menu-vertical a, .wy-menu-vertical a:hover span.toctree-expand, +.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand { + color: var(--navbar-level-2-color); + opacity: 1; +} + +.wy-side-nav-search, .wy-menu-vertical a, .wy-menu-vertical a:active span.toctree-expand, +.wy-menu-vertical li.toctree-l2 a:active span.toctree-expand { + color: var(--navbar-level-1-color); + opacity: 1; +} + +/* Second (and higher) levels of navigation items */ + +.wy-menu-vertical li.current a, +.wy-menu-vertical li.toctree-l2.current > a, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a, +.wy-menu-vertical li.toctree-l2.current li.toctree-l4 > a { + background-color: var(--navbar-current-background-color); + color: var(--navbar-level-2-color); +} + +.wy-menu-vertical li.current a:hover, +.wy-menu-vertical li.toctree-l2.current > a:hover, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a:hover, +.wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a:hover { + background-color: var(--navbar-current-background-color-hover); +} + +.wy-menu-vertical li.current a:active, +.wy-menu-vertical li.toctree-l2.current > a:active, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a:active, +.wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a:active { + background-color: var(--navbar-current-background-color-active); +} + +/* Version selector (only visible on Read the Docs) */ + +.rst-versions { + background-color: var(--navbar-current-background-color); +} + +.rst-versions a, +.rst-versions .rst-current-version, +.rst-versions .rst-current-version .fa, +.rst-versions .rst-other-versions dd a { + color: var(--navbar-level-1-color); +} + +.rst-versions .rst-other-versions small { + color: var(--navbar-level-3-color); +} + +.rst-versions .rst-other-versions dd a:hover { + text-decoration: underline; +} + +.rst-versions .rst-other-versions { + color: var(--navbar-heading-color); +} + +.rst-versions .rst-current-version { + background-color: var(--navbar-current-background-color); +} + +.rst-versions .rst-current-version:hover { + background-color: var(--navbar-current-background-color-hover); +} + +.rst-versions .rst-current-version:active { + background-color: var(--navbar-current-background-color-active); +} + +/* Hide the obnoxious automatic highlight in search results */ +.rst-content .highlighted { + background-color: transparent; + font-weight: inherit; + padding: 0; +} diff --git a/docs/_templates/breadcrumbs.html b/docs/_templates/breadcrumbs.html new file mode 100644 index 0000000..804ad69 --- /dev/null +++ b/docs/_templates/breadcrumbs.html @@ -0,0 +1,7 @@ +{%- extends "sphinx_rtd_theme/breadcrumbs.html" %} + +{% block breadcrumbs_aside %} +{% if not meta or meta.get('github_url') != 'hide' %} +{{ super() }} +{% endif %} +{% endblock %} diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html new file mode 100644 index 0000000..e0125e6 --- /dev/null +++ b/docs/_templates/layout.html @@ -0,0 +1,4 @@ +{% extends "!layout.html" %} +{% block linktags %} + {{ super() }} +{% endblock %} diff --git a/docs/classes/index.rst b/docs/classes/index.rst new file mode 100644 index 0000000..c6001e4 --- /dev/null +++ b/docs/classes/index.rst @@ -0,0 +1,9 @@ +Entity Spell System API +======================= + +.. toctree:: + :maxdepth: 1 + :name: toc-class-ref + :glob: + + class_* diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..a4bf305 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +# sys.path.insert(0, os.path.abspath('.')) + +needs_sphinx = '1.3' + + +# -- Project information ----------------------------------------------------- + +project = 'Entity Spell System for GODOT' +copyright = '2020, Péter Magyar (Relintai)' +author = 'Péter Magyar (Relintai)' + +version = 'latest' +release = 'latest' + +# Parse Sphinx tags passed from RTD via environment +env_tags = os.getenv('SPHINX_TAGS') +if env_tags != None: + for tag in env_tags.split(','): + print("Adding Sphinx tag: %s" % tag.strip()) + tags.add(tag.strip()) + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +sys.path.append(os.path.abspath('extensions')) +extensions = [ + 'gdscript', 'sphinx.ext.imgmath' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +source_suffix = '.rst' +source_encoding = 'utf-8-sig' + +# GDScript syntax highlighting +from gdscript import GDScriptLexer +from sphinx.highlighting import lexers +lexers['gdscript'] = GDScriptLexer() + +# Pygments (syntax highlighting) style to use +pygments_style = 'sphinx' +highlight_language = 'gdscript' + +# -- Options for HTML output ------------------------------------------------- + +# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +import sphinx_rtd_theme +html_theme = 'sphinx_rtd_theme' +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +if on_rtd: + using_rtd_theme = True + +# Theme options +html_theme_options = { + # 'typekit_id': 'hiw1hhg', + # 'analytics_id': '', + # 'sticky_navigation': True # Set to False to disable the sticky nav while scrolling. + 'logo_only': True, # if we have a html_logo below, this shows /only/ the logo with no title text + 'collapse_navigation': False, # Collapse navigation (False makes it tree-like) + # 'display_version': True, # Display the docs version + # 'navigation_depth': 4, # Depth of the headers shown in the navigation bar +} + +# VCS options: https://docs.readthedocs.io/en/latest/vcs.html#github +html_context = { + "display_github": True, # Integrate GitHub + "github_user": "Relintai", # Username + "github_repo": "entity_spell_system", # Repo name + "github_version": "master", # Version + "conf_py_path": "/docs/", # Path in the checkout to the docs root +} + +#html_logo = 'img/docs_logo.png' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# These paths are either relative to html_static_path +# or fully qualified paths (eg. https://...) +html_css_files = [ + 'css/custom.css', +] + +# Output file base name for HTML help builder +htmlhelp_basename = 'ESSdoc' + +# -- Options for reStructuredText parser ---------------------------------- + +# Enable directives that insert the contents of external files +file_insertion_enabled = False diff --git a/docs/create_class_docs.sh b/docs/create_class_docs.sh new file mode 100755 index 0000000..b821c77 --- /dev/null +++ b/docs/create_class_docs.sh @@ -0,0 +1,2 @@ + +python ../../../doc/tools/makerst.py ../doc_classes/ -o classes/ diff --git a/docs/extensions/gdscript.py b/docs/extensions/gdscript.py new file mode 100644 index 0000000..34b1c31 --- /dev/null +++ b/docs/extensions/gdscript.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.gdscript + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for GDScript. + + :copyright: Copyright 2xxx by The Godot Engine Community + :license: MIT. + + modified by Daniel J. Ramirez based on the original python.py pygment +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, include, bygroups, using, \ + default, words, combined, do_insertions +from pygments.util import get_bool_opt, shebang_matches +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Generic, Other, Error +from pygments import unistring as uni + +__all__ = ['GDScriptLexer'] + +line_re = re.compile('.*?\n') + + +class GDScriptLexer(RegexLexer): + """ + For `Godot source code `_ source code. + """ + + name = 'GDScript' + aliases = ['gdscript', 'gd'] + filenames = ['*.gd'] + mimetypes = ['text/x-gdscript', 'application/x-gdscript'] + + def innerstring_rules(ttype): + return [ + # the old style '%s' % (...) string formatting + (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?' + '[hlL]?[E-GXc-giorsux%]', String.Interpol), + # backslashes, quotes and formatting signs must be parsed one at a time + (r'[^\\\'"%\n]+', ttype), + (r'[\'"\\]', ttype), + # unhandled string formatting sign + (r'%', ttype), + # newlines are an error (use "nl" state) + ] + + tokens = { + 'root': [ + (r'\n', Text), + (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")', + bygroups(Text, String.Affix, String.Doc)), + (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')", + bygroups(Text, String.Affix, String.Doc)), + (r'[^\S\n]+', Text), + (r'#.*$', Comment.Single), + (r'[]{}:(),;[]', Punctuation), + (r'\\\n', Text), + (r'\\', Text), + (r'(in|and|or|not)\b', Operator.Word), + (r'!=|==|<<|>>|&&|\+=|-=|\*=|/=|%=|&=|\|=|\|\||[-~+/*%=<>&^.!|$]', Operator), + include('keywords'), + (r'(func)((?:\s|\\\s)+)', bygroups(Keyword, Text), 'funcname'), + (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Text), 'classname'), + include('builtins'), + ('([rR]|[uUbB][rR]|[rR][uUbB])(""")', + bygroups(String.Affix, String.Double), 'tdqs'), + ("([rR]|[uUbB][rR]|[rR][uUbB])(''')", + bygroups(String.Affix, String.Single), 'tsqs'), + ('([rR]|[uUbB][rR]|[rR][uUbB])(")', + bygroups(String.Affix, String.Double), 'dqs'), + ("([rR]|[uUbB][rR]|[rR][uUbB])(')", + bygroups(String.Affix, String.Single), 'sqs'), + ('([uUbB]?)(""")', bygroups(String.Affix, String.Double), + combined('stringescape', 'tdqs')), + ("([uUbB]?)(''')", bygroups(String.Affix, String.Single), + combined('stringescape', 'tsqs')), + ('([uUbB]?)(")', bygroups(String.Affix, String.Double), + combined('stringescape', 'dqs')), + ("([uUbB]?)(')", bygroups(String.Affix, String.Single), + combined('stringescape', 'sqs')), + include('name'), + include('numbers'), + ], + 'keywords': [ + (words(( + 'and', 'in', 'not', 'or', 'as', 'breakpoint', 'class', 'class_name', + 'extends', 'is', 'func', 'setget', 'signal', 'tool', 'const', + 'enum', 'export', 'onready', 'static', 'var', 'break', 'continue', + 'if', 'elif', 'else', 'for', 'pass', 'return', 'match', 'while', + 'remote', 'master', 'puppet', 'remotesync', 'mastersync', + 'puppetsync'), suffix=r'\b'), + Keyword), + ], + 'builtins': [ + (words(( + 'Color8', 'ColorN', 'abs', 'acos', 'asin', 'assert', 'atan', 'atan2', + 'bytes2var', 'ceil', 'char', 'clamp', 'convert', 'cos', 'cosh', + 'db2linear', 'decimals', 'dectime', 'deg2rad', 'dict2inst', + 'ease', 'exp', 'floor', 'fmod', 'fposmod', 'funcref', 'hash', + 'inst2dict', 'instance_from_id', 'is_inf', 'is_nan', 'lerp', + 'linear2db', 'load', 'log', 'max', 'min', 'nearest_po2', 'pow', + 'preload', 'print', 'print_stack', 'printerr', 'printraw', + 'prints', 'printt', 'rad2deg', 'rand_range', 'rand_seed', + 'randf', 'randi', 'randomize', 'range', 'round', 'seed', 'sign', + 'sin', 'sinh', 'sqrt', 'stepify', 'str', 'str2var', 'tan', + 'tan', 'tanh', 'type_exist', 'typeof', 'var2bytes', 'var2str', + 'weakref', 'yield'), + prefix=r'(?NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/procedural_animation.cpp b/procedural_animation.cpp new file mode 100644 index 0000000..8cacdec --- /dev/null +++ b/procedural_animation.cpp @@ -0,0 +1,300 @@ +#include "procedural_animation.h" + +StringName ProceduralAnimation::get_editor_category() const { + if (_editor_category >= 0 && _editor_category < _categories.size()) { + return _categories.get(_editor_category)->name; + } + + return "---"; +} +void ProceduralAnimation::set_editor_category(const StringName &value) { + for (int i = 0; i < _categories.size(); ++i) { + if (_categories.get(i)->name == value) { + _editor_category = i; + return; + } + } + + _editor_category = -1; + _editor_category_animation = -1; +} + +StringName ProceduralAnimation::get_editor_category_animation() const { + if (_editor_category >= 0 && _editor_category < _categories.size()) { + Category *category = _categories.get(_editor_category); + + if (_editor_category_animation >= 0 && _editor_category_animation < category->animations.size()) { + return category->animations.get(_editor_category_animation)->name; + } + } + + return "---"; +} +void ProceduralAnimation::set_editor_category_animation(const StringName &value) { + if (_editor_category >= 0 && _editor_category < _categories.size()) { + Category *category = _categories.get(_editor_category); + + for (int i = 0; i < category->animations.size(); ++i) { + if (category->animations.get(i)->name == value) { + _editor_category_animation = i; + return; + } + } + } + + _editor_category_animation = -1; +} + +String ProceduralAnimation::get_add_editor_category_name() const { + return _editor_add_category_name; +} +void ProceduralAnimation::set_add_editor_category_name(const String &value) { + _editor_add_category_name = value; +} + +bool ProceduralAnimation::get_add_editor_category() const { + return false; +} +void ProceduralAnimation::set_add_editor_category(const bool value) { + Category *cat = memnew(Category); + cat->name = _editor_add_category_name; + _categories.push_back(cat); + + _editor_category = _categories.size() - 1; +} + +String ProceduralAnimation::get_add_editor_category_animation_name() const { + return _add_editor_category_animation_name; +} +void ProceduralAnimation::set_add_editor_category_animation_name(const String &value) { + _add_editor_category_animation_name = value; +} + +bool ProceduralAnimation::get_add_editor_animation_category() const { + return false; +} +void ProceduralAnimation::set_add_editor_animation_category(const bool value) { + if (_editor_category >= 0 && _editor_category < _categories.size()) { + AnimationEntry *entry = memnew(AnimationEntry); + entry->name = _add_editor_category_animation_name; + _categories.get(_editor_category)->animations.push_back(entry); + + _editor_category_animation = _categories.get(_editor_category)->animations.size() - 1; + } +} + +ProceduralAnimation::ProceduralAnimation() { + _editor_category = -1; + _editor_category_animation = -1; +} + +ProceduralAnimation::~ProceduralAnimation() { + for (int i = 0; i < _categories.size(); ++i) { + Category *categ = _categories.get(i); + + for (int j = 0; j < categ->animations.size(); ++j) { + memdelete(categ->animations.get(j)); + } + + memdelete(categ); + } + + _categories.clear(); + + _animation.unref(); +} + +bool ProceduralAnimation::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + + if (name.begins_with("categories|")) { + + int category_index = name.get_slicec('|', 1).to_int(); + String what = name.get_slicec('|', 2); + + ERR_FAIL_INDEX_V(category_index, _categories.size(), false); + + Category *cat = _categories.get(category_index); + + if (what == "name") { + cat->name = p_value; + + return true; + } else if (what == "animation") { + int animation_index = name.get_slicec('|', 3).to_int(); + String anim_prop_name = name.get_slicec('|', 4); + + Category *category = _categories.get(category_index); + + ERR_FAIL_INDEX_V(category_index, _categories.get(category_index)->animations.size(), false); + + if (anim_prop_name == "name") { + category->animations.get(animation_index)->name = p_value; + + return true; + } else if (anim_prop_name == "index") { + category->animations.get(animation_index)->index = p_value; + + return true; + } else if (anim_prop_name == "keyframe_index") { + category->animations.get(animation_index)->keyframe_index = p_value; + + return true; + } else if (anim_prop_name == "next_animation") { + category->animations.get(animation_index)->next_animation = p_value; + + return true; + } else if (anim_prop_name == "in_curve") { + category->animations.get(animation_index)->in_curve = p_value; + + return true; + } else { + return false; + } + } + + return false; + } else { + return false; + } + + return true; +} + +bool ProceduralAnimation::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + + if (name.begins_with("categories|")) { + + int category_index = name.get_slicec('|', 1).to_int(); + String what = name.get_slicec('|', 2); + + ERR_FAIL_INDEX_V(category_index, _categories.size(), false); + + if (what == "name") { + r_ret = _categories.get(category_index)->name; + + return true; + } else if (what == "animation") { + int animation_index = name.get_slicec('|', 3).to_int(); + String anim_prop_name = name.get_slicec('|', 4); + + Category *category = _categories.get(category_index); + + ERR_FAIL_INDEX_V(animation_index, _categories.get(category_index)->animations.size(), false); + + if (anim_prop_name == "name") { + r_ret = category->name; + + return true; + } else if (anim_prop_name == "index") { + r_ret = category->animations.get(animation_index)->index; + + return true; + } else if (anim_prop_name == "keyframe_index") { + r_ret = category->animations.get(animation_index)->keyframe_index; + + return true; + } else if (anim_prop_name == "next_animation") { + r_ret = category->animations.get(animation_index)->next_animation; + + return true; + } else if (anim_prop_name == "in_curve") { + r_ret = category->animations.get(animation_index)->in_curve; + + return true; + } else { + return false; + } + } + + return false; + } else { + return false; + } + + return true; +} + +void ProceduralAnimation::_validate_property(PropertyInfo &property) const { + String name = property.name; + + if (name == "editor_category") { + String names("---"); + for (int i = 0; i < _categories.size(); ++i) { + names += ","; + + names += _categories.get(i)->name; + } + + property.hint_string = names; + } else if (name == "editor_category_animation") { + String names("---"); + + if (_editor_category >= 0 && _editor_category < _categories.size()) { + Category *category = _categories.get(_editor_category); + + for (int i = 0; i < category->animations.size(); ++i) { + names += ","; + + names += category->animations.get(i)->name; + } + } + + property.hint_string = names; + } +} + +void ProceduralAnimation::_get_property_list(List *p_list) const { + for (int i = 0; i < _categories.size(); ++i) { + int property_usange_editor = PROPERTY_USAGE_NOEDITOR; + + if (i == _editor_category) { + property_usange_editor = PROPERTY_USAGE_EDITOR; + } + + Category *categ = _categories.get(i); + + p_list->push_back(PropertyInfo(Variant::STRING, "categories|" + itos(i) + "|name", PROPERTY_HINT_NONE, "", property_usange_editor | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED)); + + for (int j = 0; j < categ->animations.size(); ++j) { + if (i == _editor_category && j == _editor_category_animation) { + property_usange_editor = PROPERTY_USAGE_EDITOR; + } else { + property_usange_editor = PROPERTY_USAGE_NOEDITOR; + } + + p_list->push_back(PropertyInfo(Variant::STRING, "categories|" + itos(i) + "|animation|" + itos(j) + "|name", PROPERTY_HINT_NONE, "", property_usange_editor | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED)); + p_list->push_back(PropertyInfo(Variant::INT, "categories|" + itos(i) + "|animation|" + itos(j) + "|index", PROPERTY_HINT_NONE, "", property_usange_editor | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, "categories|" + itos(i) + "|animation|" + itos(j) + "|keyframe_index", PROPERTY_HINT_NONE, "", property_usange_editor | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::STRING, "categories|" + itos(i) + "|animation|" + itos(j) + "|next_animation", PROPERTY_HINT_NONE, "", property_usange_editor | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "categories|" + itos(i) + "|animation|" + itos(j) + "|in_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve", property_usange_editor | PROPERTY_USAGE_INTERNAL)); + } + } +} + +void ProceduralAnimation::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_editor_category"), &ProceduralAnimation::get_editor_category); + ClassDB::bind_method(D_METHOD("set_editor_category", "value"), &ProceduralAnimation::set_editor_category); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_category", PROPERTY_HINT_ENUM, "---", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_editor_category", "get_editor_category"); + + ClassDB::bind_method(D_METHOD("get_editor_category_animation"), &ProceduralAnimation::get_editor_category_animation); + ClassDB::bind_method(D_METHOD("set_editor_category_animation", "value"), &ProceduralAnimation::set_editor_category_animation); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_category_animation", PROPERTY_HINT_ENUM, "---", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_editor_category_animation", "get_editor_category_animation"); + + ClassDB::bind_method(D_METHOD("get_add_editor_category_name"), &ProceduralAnimation::get_add_editor_category_name); + ClassDB::bind_method(D_METHOD("set_add_editor_category_name", "value"), &ProceduralAnimation::set_add_editor_category_name); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "add_editor_category_name"), "set_add_editor_category_name", "get_add_editor_category_name"); + + ClassDB::bind_method(D_METHOD("get_add_editor_category"), &ProceduralAnimation::get_add_editor_category); + ClassDB::bind_method(D_METHOD("set_add_editor_category", "value"), &ProceduralAnimation::set_add_editor_category); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_editor_category", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_add_editor_category", "get_add_editor_category"); + + ClassDB::bind_method(D_METHOD("get_add_editor_category_animation_name"), &ProceduralAnimation::get_add_editor_category_animation_name); + ClassDB::bind_method(D_METHOD("set_add_editor_category_animation_name", "value"), &ProceduralAnimation::set_add_editor_category_animation_name); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "add_editor_category_animation_name"), "set_add_editor_category_animation_name", "get_add_editor_category_animation_name"); + + ClassDB::bind_method(D_METHOD("get_add_editor_animation_category"), &ProceduralAnimation::get_add_editor_animation_category); + ClassDB::bind_method(D_METHOD("set_add_editor_animation_category", "value"), &ProceduralAnimation::set_add_editor_animation_category); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_editor_animation_category", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_add_editor_animation_category", "get_add_editor_animation_category"); +} \ No newline at end of file diff --git a/procedural_animation.h b/procedural_animation.h new file mode 100644 index 0000000..fa96ebf --- /dev/null +++ b/procedural_animation.h @@ -0,0 +1,75 @@ +#ifndef PROCEDURAL_ANIMATION_H +#define PROCEDURAL_ANIMATION_H + +#include "core/resource.h" + +#include "core/vector.h" +#include "scene/resources/animation.h" +#include "scene/resources/curve.h" + +#include "scene/resources/animation.h" + +class ProceduralAnimation : public Resource { + GDCLASS(ProceduralAnimation, Resource); + +public: + StringName get_editor_category() const; + void set_editor_category(const StringName &value); + + StringName get_editor_category_animation() const; + void set_editor_category_animation(const StringName &value); + + String get_add_editor_category_name() const; + void set_add_editor_category_name(const String &value); + + bool get_add_editor_category() const; + void set_add_editor_category(const bool value); + + String get_add_editor_category_animation_name() const; + void set_add_editor_category_animation_name(const String &value); + + bool get_add_editor_animation_category() const; + void set_add_editor_animation_category(const bool value); + + ProceduralAnimation(); + ~ProceduralAnimation(); + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _validate_property(PropertyInfo &property) const; + void _get_property_list(List *p_list) const; + static void _bind_methods(); + +protected: + struct AnimationEntry { + StringName name; + int index; + int keyframe_index; + StringName next_animation; + Ref in_curve; + + AnimationEntry() { + index = 0; + keyframe_index = 0; + } + }; + + struct Category { + StringName name; + Vector animations; + }; + +private: + int _editor_category; + int _editor_category_animation; + + String _editor_add_category_name; + String _add_editor_category_animation_name; + + Vector _categories; + + Ref _animation; +}; + +#endif \ No newline at end of file diff --git a/procedural_animation_player.cpp b/procedural_animation_player.cpp new file mode 100644 index 0000000..0db2ac3 --- /dev/null +++ b/procedural_animation_player.cpp @@ -0,0 +1,5 @@ +#include "procedural_animation_player.h" + +void ProceduralAnimationPlayer::play(const StringName &p_name, float p_custom_blend, float p_custom_scale, bool p_from_end) { + print_error("adada"); +} \ No newline at end of file diff --git a/procedural_animation_player.h b/procedural_animation_player.h new file mode 100644 index 0000000..17dcdaf --- /dev/null +++ b/procedural_animation_player.h @@ -0,0 +1,13 @@ +#ifndef PROCEDURAL_ANIMATION_PLAYER_H +#define PROCEDURAL_ANIMATION_PLAYER_H + +#include "scene/main/node.h" + +class ProceduralAnimationPlayer : public Node { + GDCLASS(ProceduralAnimationPlayer, Node); + +public: + void play(const StringName &p_name = StringName(), float p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false); +}; + +#endif \ No newline at end of file diff --git a/register_types.cpp b/register_types.cpp new file mode 100644 index 0000000..54c3065 --- /dev/null +++ b/register_types.cpp @@ -0,0 +1,12 @@ +#include "register_types.h" + +#include "procedural_animation.h" +#include "procedural_animation_player.h" + +void register_procedural_animations_types() { + ClassDB::register_class(); + ClassDB::register_class(); +} + +void unregister_procedural_animations_types() { +} diff --git a/register_types.h b/register_types.h new file mode 100644 index 0000000..c38cf34 --- /dev/null +++ b/register_types.h @@ -0,0 +1,3 @@ + +void register_procedural_animations_types(); +void unregister_procedural_animations_types();