From 207021590f4b05175ef56178f11affeb3d6caac1 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Sat, 1 Jul 2023 00:00:00 +0000 Subject: [PATCH 01/80] Add KHR_interactivity intro --- .github/workflows/CI.yml | 12 + .gitignore | 2 + .../2.0/Khronos/KHR_interactivity/Makefile | 78 ++++++ .../KHR_interactivity/Specification.adoc | 224 ++++++++++++++++++ 4 files changed, 316 insertions(+) create mode 100644 extensions/2.0/Khronos/KHR_interactivity/Makefile create mode 100644 extensions/2.0/Khronos/KHR_interactivity/Specification.adoc diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 43e6e0193b..ebbcd071ee 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -38,3 +38,15 @@ jobs: path: | specification/2.0/Specification.html specification/2.0/Specification.pdf + + - name: Build interactivity extension targets + run: | + cd extensions/2.0/Khronos/KHR_interactivity + make Specification.html + + - name: Archive generated files + uses: actions/upload-artifact@v2 + with: + name: interactivity-outputs + path: | + extensions/2.0/Khronos/KHR_interactivity/Specification.html diff --git a/.gitignore b/.gitignore index 28f450d9dd..cea75e8878 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ specification/2.0/PropertiesReference.adoc specification/2.0/JsonSchemaReference.adoc specification/2.0/Specification.html specification/2.0/Specification.pdf +extensions/2.0/Khronos/**/Specification.html +extensions/2.0/Khronos/**/Specification.pdf diff --git a/extensions/2.0/Khronos/KHR_interactivity/Makefile b/extensions/2.0/Khronos/KHR_interactivity/Makefile new file mode 100644 index 0000000000..e6a471d9b2 --- /dev/null +++ b/extensions/2.0/Khronos/KHR_interactivity/Makefile @@ -0,0 +1,78 @@ +# Copyright 2020 The Khronos Group Inc. +# SPDX-License-Identifier: Apache-2.0 + +# Output specification targets +SPEC = Specification +TARGETS = $(SPEC).html $(SPEC).pdf +all: $(TARGETS) + +# Generate the Properties Reference section of the spec from JSON schema. +# This uses 'npx' to minimally install the 'wetzel' tool, if needed. +# When using embedded schemas, SCHEMALINK should be a relative path. + +# Note this Makefile should remain locked to a particular version of +# wetzel, such that the history of changes to the documentation can +# accurately reflect changes to wetzel that impact the output here. +# Please manually upgrade the version as often as needed, without unlocking. + +WETZEL = npx wetzel@0.2.2 +SCHEMALINK = schema +EMBEDSCHEMA = JsonSchemaReference.adoc + +# Base name of the generated properties reference file +PROPREF = PropertiesReference.adoc +# Files generated by wetzel +GENERATED = $(PROPREF) $(EMBEDSCHEMA) +# Schema files to leave out of $(PROPREF) +IGNORESCHEMA = '["gltfchildofrootproperty.schema.json", "gltfid.schema.json", "gltfproperty.schema.json"]' +$(GENERATED): $(wildcard schema/*.json) + $(WETZEL) -n -a=cqo -m=a -p "$(SCHEMALINK)" -e "$(EMBEDSCHEMA)" \ + -i $(IGNORESCHEMA) -c "icon:check[]" -k "**MUST**"\ + schema/glTF.schema.json > $(PROPREF) + +# Spec targets for offline generation +# Requires an up-to-date asciidoctor, asciidoctor-pdf, and +# asciidoctor-mathematical be installed +# We recommend using the following image on dockerhub: +# docker pull khronosgroup/docker-images:asciidoctor-spec +ASCIIDOCTOR = asciidoctor $(ADOCOPTS) +ADOCOPTS = -d book +ADOCHTMLOPTS = -a stylesheet=../../../../specification/2.0/khronos.css -a sectanchors +SPECDEPS = $(SPEC).adoc #$(GENERATED) + +PATCHVERSION = 1 +SPECREVISION = 2.0.$(PATCHVERSION) + +# Spell out ISO 8601 format as not all date commands support --rfc-3339 +SPECDATE = $(shell echo `date -u "+%Y-%m-%d %TZ"`) + +# Generate AsciiDoc attributes for spec remark +# Could use `git log -1 --format="%cd"` to get branch commit date +# This used to be a dependency in the spec html/pdf targets, +# but that's likely to lead to merge conflicts. Just regenerate +# when pushing a new spec for review to the sandbox. +# The dependency on HEAD is per the suggestion in +# http://neugierig.org/software/blog/2014/11/binary-revisions.html +SPECREMARK = from git branch: $(shell echo `git symbolic-ref --short HEAD 2> /dev/null || echo Git branch not available`) \ + commit: $(shell echo `git log -1 --format="%H" 2> /dev/null || echo Git commit not available`) + +ATTRIBOPTS = -a revnumber="$(SPECREVISION)" \ + -a revdate="$(SPECDATE)" \ + -a revremark="$(SPECREMARK)" + +$(SPEC).html: $(SPECDEPS) + $(ASCIIDOCTOR) -b html5 $(ADOCHTMLOPTS) $(ATTRIBOPTS) $(SPEC).adoc -o $@ + +# :allow-url-read: is necessary for the embedded render.githubusers.com +# math images to be processed for the PDF target. See +# https://github.com/asciidoctor/asciidoctor-pdf/issues/369 +# asciidoctor-mathematical leaves intermediate images of equations behing. +# These are removed after creating the PDF. +STEMIMAGES = stem-*.png +$(SPEC).pdf: $(SPECDEPS) + $(ASCIIDOCTOR) -b pdf $(ATTRIBOPTS) -a allow-uri-read -r asciidoctor-pdf \ + -r asciidoctor-mathematical $(SPEC).adoc -o $@ + rm -f $(STEMIMAGES) + +clean: + -rm -f $(GENERATED) $(TARGETS) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc new file mode 100644 index 0000000000..d6527f955d --- /dev/null +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -0,0 +1,224 @@ +// Copyright 2013-2023 The Khronos Group Inc. +// +// SPDX-License-Identifier: CC-BY-4.0 + +// :regtitle: is explained in +// https://discuss.asciidoctor.org/How-to-add-markup-to-author-information-in-document-title-td6488.html += glTF{tmtitle} 2.0 Interactivity Extension Specification +:tmtitle: pass:q,r[^™^] +:regtitle: pass:q,r[^®^] +The Khronos{regtitle} 3D Formats Working Group +:data-uri: +:icons: font +:toc2: +:toclevels: 10 +:sectnumlevels: 10 +:max-width: 100% +:numbered: +:source-highlighter: coderay +:title-logo-image: image:../figures/glTF_RGB_June16.svg[Logo,pdfwidth=4in,align=right] +:docinfo: shared-head +:docinfodir: ../../../../specification/2.0 +:stem: + +// This causes cross references to chapters, sections, and tables to be +// rendered as "Section A.B" (for example) rather than rendering the reference +// as the text of the section title. It also enables cross references to +// [source] blocks as "Listing N", but only if the [source] block has a title. +:xrefstyle: short +:listing-caption: Listing + +ifndef::revdate[] +:toc-placement!: + +[NOTE] +.Note +==== +Khronos posts the AsciiDoc source of the glTF specification to enable community +feedback and remixing under CC-BY 4.0. Published versions of the Specification +are located in the https://www.khronos.org/registry/glTF[glTF Registry]. +==== +endif::[] + +// Table of contents is inserted here +toc::[] + +:leveloffset: 1 + +[[foreword]] += Foreword + +Copyright 2013-2023 The Khronos Group Inc. + +This specification is protected by copyright laws and contains material proprietary +to Khronos. Except as described by these terms, it or any components +may not be reproduced, republished, distributed, transmitted, displayed, broadcast, +or otherwise exploited in any manner without the express prior written permission +of Khronos. + +This specification has been created under the Khronos Intellectual Property Rights +Policy, which is Attachment A of the Khronos Group Membership Agreement available at +https://www.khronos.org/files/member_agreement.pdf. Khronos grants a conditional +copyright license to use and reproduce the unmodified specification for any purpose, +without fee or royalty, EXCEPT no licenses to any patent, trademark or other +intellectual property rights are granted under these terms. Parties desiring to +implement the specification and make use of Khronos trademarks in relation to that +implementation, and receive reciprocal patent license protection under the Khronos +IP Policy must become Adopters under the process defined by Khronos for this specification; +see https://www.khronos.org/conformance/adopters/file-format-adopter-program. + +Some parts of this Specification are non-normative through being explicitly identified as +purely informative, and do not define requirements necessary for compliance and so are +outside the Scope of this Specification. + +Where this Specification includes normative references to external documents, only the +specifically identified sections and functionality of those external documents are in +Scope. Requirements defined by external documents not created by Khronos may contain +contributions from non-members of Khronos not covered by the Khronos Intellectual +Property Rights Policy. + +Khronos makes no, and expressly disclaims any, representations or warranties, +express or implied, regarding this specification, including, without limitation: +merchantability, fitness for a particular purpose, non-infringement of any +intellectual property, correctness, accuracy, completeness, timeliness, and +reliability. Under no circumstances will Khronos, or any of its Promoters, +Contributors or Members, or their respective partners, officers, directors, +employees, agents or representatives be liable for any damages, whether direct, +indirect, special or consequential damages for lost revenues, lost profits, or +otherwise, arising from or in connection with these materials. + +Khronos® and Vulkan® are registered trademarks, and ANARI™, WebGL™, glTF™, NNEF™, OpenVX™, +SPIR™, SPIR‑V™, SYCL™, OpenVG™ and 3D Commerce™ are trademarks of The Khronos Group Inc. +OpenXR™ is a trademark owned by The Khronos Group Inc. and is registered as a trademark in +China, the European Union, Japan and the United Kingdom. OpenCL™ is a trademark of Apple Inc. +and OpenGL® is a registered trademark and the OpenGL ES™ and OpenGL SC™ logos are trademarks +of Hewlett Packard Enterprise used under license by Khronos. ASTC is a trademark of +ARM Holdings PLC. All other product names, trademarks, and/or company names are used solely +for identification and belong to their respective owners. + + +[[introduction]] += Introduction + +[[introduction-general]] +== General + +This document, referred to as the "`glTF Interactivity Extension Specification`" or just the "`Specification`" hereafter, describes the `KHR_interactivity` glTF extension. + +This extension aims to enhance glTF 2.0 by adding the ability to encode behavior and interactivity in 3D assets. + +[NOTE] +.Note +==== +This specification is for single user experiences only and does not deal with any of the complexity involved in multi-user networked experiences. +==== + +[[introduction-conventions]] +== Document Conventions + +The glTF Interactivity Extension Specification is intended for use by both implementers of the asset exporters or converters (e.g., digital content creation tools) and application developers seeking to import or load interactive glTF assets, forming a basis for interoperability between these parties. + +Specification text can address either party; typically, the intended audience can be inferred from context, though some sections are defined to address only one of these parties. + +Any requirements, prohibitions, recommendations, or options defined by <> are imposed only on the audience of that text. + +[[introduction-normative-terminology]] +=== Normative Terminology and References + +The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this document are to be interpreted as described in <>. + +These key words are highlighted in the specification for clarity. + +References to external documents are considered normative if the Specification uses any of the normative terms defined in this section to refer to them or their requirements, either as a whole or in part. + +[[introduction-informative-language]] +=== Informative Language + +Some language in the specification is purely informative, intended to give background or suggestions to implementers or developers. + +If an entire chapter or section contains only informative language, its title is suffixed with "`(Informative)`". If not designated as informative, all chapters, sections, and appendices in this document are normative. + +All Notes, Implementation notes, and Examples are purely informative. + +[[introduction-technical-terminology]] +=== Technical Terminology + +TBD + +[[introduction-normative-references]] +=== Normative References + +The following documents are referenced by normative sections of the specification: + +==== External Specifications + +[none] +* [[bcp14]] +Bradner, S., _Key words for use in RFCs to Indicate Requirement Levels_, BCP 14, RFC 2119, March 1997. Leiba, B., _Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words_, BCP 14, RFC 8174, May 2017. + + +[[motivation]] +== Motivation and Design Goals (Informative) + +glTF 2.0 assets are widely used in various industries, including automotive, e-commerce, and gaming. There is a growing demand for adding logic and behavior to glTF assets, particularly in the metaverse. This extension aims to fulfill this demand by providing a portable, easily implementable, safe, and visually accessible solution for adding behavior to glTF assets. The extension is inspired by visual scripting features of leading game engines and aims to deliver a minimum meaningful and extensible feature set. + +=== What Is a Behavior Graph? +A behavior graph is a series of interconnected nodes that represent behaviors and interactions in a 3D asset. It can respond to events and cause changes in the asset's appearance and behavior. + +=== What Problems Can They Solve? +Behavior graphs offer a flexible and multi-functional approach to encoding behavior, making them useful for various applications. For instance, they can be used to create smart assets with behavior and interactions, AR experiences with user interactions, and immersive game levels with dynamic assets and objectives. + +=== What Do They Not Solve? +Behavior graphs are not designed to handle UI presentation or arbitrary scripting. Creating a 3D UI using behavior graphs would be complex, not portable, and not accessible. Similarly, arbitrary scripting is challenging to make safe, portable across platforms, and has a vast surface area. + +=== Comparison with Trigger-Action Lists +Behavior graphs and trigger-action lists are the two common models for representing and executing behaviors in the digital world. Common 3D experience commerce tools use trigger-action lists, while behavior graphs are typically used by high-end game engines. In this section, we will explore the differences and similarities between these two models, and explain why glTF chose to adopt behavior graphs. + +Behavior graphs and trigger-action lists share common features, such as being safe and sandboxed, offering limited execution models controlled by the viewer, and both supporting the “trigger” and “action” node categories. However, there are also significant differences between the two models. Trigger-action lists lack “Queries”, “Logic”, and “Control Flow” nodes, meaning that sophisticated behavior based on queries, logic, or control flow branches is not possible. This lack of functionality greatly affects the ability to create complex behavior and control structures and rules out the implementation of advanced control flow structures in the future. + +On the other hand, behavior graphs are a superset of trigger-action lists, meaning that the former can support everything that trigger-action lists can, and more. Behavior graphs support “Queries”, “Logic” and “Control Flow” nodes, making them more expressive and capable of creating more sophisticated behaviors. This makes behavior graphs the preferred method of choice for high-end game engines, as it offers an identical safety model as trigger-action lists while being more expressive. + +=== Turing Completeness +The execution model and node choices for this extension mean that it is Turing-complete. This means that an implementation of this can execute any computation and it is also hard to predict if it will run forever (e.g. halt or not.) + +While this may present security implications, it is not a major hindrance and can be safely mitigated so that any implementation does not become susceptible to denial of services by badly behaving behavior graphs, whether intention or not. + +The main way to mitigate the risk of non-halting behavior graphs is to limit the amount of time given to them for execution, both in terms of individual time slice as well as overall execution time. + +=== Implementation Limitations +There will be limitations in engines and devices to such as: + +* Number of nodes in the graph +* Number of variables +* Number of custom events +* Number of concurrent pending events/async nodes +* Number of nodes executed per time slice +* Speed of graph execution + +These limitations are not defined in this specification. + + +[[concepts]] += Concepts + +[[concepts-general]] +== Graphs + +A behavior graph is a JSON object containing nodes. It **MAY** also contain custom variables and custom events. + +Behavior graphs are directed graphs with no directed cycles. + +[[nodes]] +== Nodes + +A node is a JSON object, which represents an executable item. A node is executed when its input flow socket is reached by or when one of its output value sockets is requested by the behavior graph engine. The node executes its logic and can then execute any number (including zero) of outgoing flow sockets. + +A node **MAY** have a set of parameters, each of which **MUST** be provided statically in the node object, resolved at runtime through subgraph executions, or have a default value. Nodes **MAY** be configurable through static properties that **MAY** affect the node's behavior and the number of its sockets. + +Node's sockets and configurations are defined by its _type_. Node types should follow `domain/operation` naming pattern. + +=== Sockets + +== Custom Events + +== Variables \ No newline at end of file From 09bd2cfa402a29f3d88ddc78c474368c835db685 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 2 Oct 2023 00:00:00 +0000 Subject: [PATCH 02/80] Add socket descriptions and math nodes --- .../KHR_interactivity/Specification.adoc | 523 +++++++++++++++++- 1 file changed, 516 insertions(+), 7 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index d6527f955d..cef5444c5c 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -157,6 +157,16 @@ The following documents are referenced by normative sections of the specificatio Bradner, S., _Key words for use in RFCs to Indicate Requirement Levels_, BCP 14, RFC 2119, March 1997. Leiba, B., _Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words_, BCP 14, RFC 8174, May 2017. +* [[ieee-754]] +ISO/IEC 60559 +_Floating-point arithmetic_ + + +* [[ecma-262]] +ECMA-262 +_ECMAScript® Language Specification_ + + [[motivation]] == Motivation and Design Goals (Informative) @@ -204,21 +214,520 @@ These limitations are not defined in this specification. [[concepts-general]] == Graphs -A behavior graph is a JSON object containing nodes. It **MAY** also contain custom variables and custom events. +A behavior graph is a JSON object containing _nodes_. It **MAY** also contain custom variables and custom events. Behavior graphs are directed graphs with no directed cycles. [[nodes]] -== Nodes +== Nodes and Sockets + +A _node_ is a JSON object, which represents an executable item. Each node is defined by its _type_ and a set of _sockets_. There are four kinds of sockets. + +_Output value sockets_ represent data initialized by the node or produced during its execution. For example, it could be results of math operations or parts of the node's internal state. Accessing these sockets either triggers computing the return value on the fly by executing the node or returns a value based on the node's internal state. Exact behavior depends on the node's type. + +_Input value sockets_ represent data accessed during the node's execution. For example, it could be arguments of math operations or execution parameters such as iteration count for loop nodes or duration for time-related nodes. These sockets **MUST** either be given a static value in the node object or connected to an output value socket of a different node. The node **MAY** access its input value sockets multiple times during the execution. The runtime **MUST** guarantee that all input value sockets have defined values when the node execution starts. + +_Output flow sockets_ represent "`function pointers`" that the node will call to advance the graph execution. For example, bodies and branches of flow control nodes are output flow sockets that drive further execution when certain condition are fulfilled. Output flow sockets **MAY** be unconnected; in such a case graph execution proceeds as if such sockets are no-ops. + +_Input flow sockets_ represent "`methods`" that could be called on the node. For example, flow control nodes (such as loops and conditions) usually have an `in` input flow socket that starts node's execution. Additional operations **MAY** also be defined such as `reset` for nodes having an internal state. -A node is a JSON object, which represents an executable item. A node is executed when its input flow socket is reached by or when one of its output value sockets is requested by the behavior graph engine. The node executes its logic and can then execute any number (including zero) of outgoing flow sockets. +Nodes **MAY** be configurable through static properties collectively called "`node's configuration`" that **MAY** affect the node's behavior and the number of its sockets, such as the number of cases for a switch-case control flow node. -A node **MAY** have a set of parameters, each of which **MUST** be provided statically in the node object, resolved at runtime through subgraph executions, or have a default value. Nodes **MAY** be configurable through static properties that **MAY** affect the node's behavior and the number of its sockets. +Input and output value sockets have associated data types, e.g., floats, integers, booleans, etc. -Node's sockets and configurations are defined by its _type_. Node types should follow `domain/operation` naming pattern. +Node's sockets and configurations are defined by its _type_. Node types follow `domain/operation` naming pattern. -=== Sockets +A node is executed when its input flow socket is reached by or when one of its output value sockets is requested by another node. Usually, the node executes its dependencies (if any), its own logic, and any number (including zero) of outgoing flow sockets. +[[types]] +== Value Types + +TBD + +[[events]] == Custom Events -== Variables \ No newline at end of file +TBD + +[[variables]] +== Variables + +TBD + +== Node Types + +=== Math Nodes + +In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3`, `float4`, or `float4x4` types. All value sockets of `floatN` types have the same type within a node. + +==== Constants + +===== E + +[cols="1h,1,2"] +|=== +| Type | `math/e` | Euler's number +| Output value sockets | `float value` | 2.718281828459045 +|=== + +===== Pi + +[cols="1h,1,2"] +|=== +| Type | `math/pi` | Ratio of a circle's circumference to its diameter +| Output value sockets | `float value` | 3.141592653589793 +|=== + +==== Arithmetic Nodes + +These all operate component-wise. The description is per component. + +If any input value is _NaN_, the output value is also _NaN_. + +===== Absolute Value + +[cols="1h,1,2"] +|=== +| Type | `math/abs` | Absolute value operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | latexmath:[\begin{cases}a, \text{if } a \ge 0.0 \\ -a, \text{if } a < 0.0 \end{cases}] +|=== + +===== Sign + +[cols="1h,1,2"] +|=== +| Type | `math/sign` | Sign operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | latexmath:[\begin{cases}-1.0, \text{if } a < 0.0 \\ a, \text{if } a = \pm0.0 \\ 1.0, \text{if } a > 0.0 \end{cases}] +|=== + +===== Truncate + +[cols="1h,1,2"] +|=== +| Type | `math/trunc` | Truncate operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | Integer value equal to the nearest integer to stem:[a] whose absolute value is not larger than the absolute value of stem:[a] +|=== + +If the argument is infinity, it is returned unchanged. + +===== Floor + +[cols="1h,1,2"] +|=== +| Type | `math/floor` | Floor operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | Value equal to the nearest integer that is less than or equal to stem:[a] +|=== + +If the argument is infinity, it is returned unchanged. + +===== Ceil + +[cols="1h,1,2"] +|=== +| Type | `math/ceil` | Ceil operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | Value equal to the nearest integer that is greater than or equal to stem:[a] +|=== + +If the argument is infinity, it is returned unchanged. + +===== Negation + +[cols="1h,1,2"] +|=== +| Type | `math/neg` | Negation operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | stem:[-a] +|=== + +===== Addition + +[cols="1h,1,2"] +|=== +| Type | `math/add` | Addition operation +.2+| Input value sockets +| `floatN a` | First addend +| `floatN b` | Second addend +| Output value sockets +| `floatN value` | Sum, stem:[a + b] +|=== + +===== Subtraction + +[cols="1h,1,2"] +|=== +| Type | `math/sub` | Subtraction operation +.2+| Input value sockets +| `floatN a` | Minuend +| `floatN b` | Subtrahend +| Output value sockets +| `floatN value` | Difference, stem:[a - b] +|=== + +===== Multiplication + +[cols="1h,1,2"] +|=== +| Type | `math/mul` | Multiplication operation +.2+| Input value sockets +| `floatN a` | First factor +| `floatN b` | Second factor +| Output value sockets +| `floatN value` | Product, stem:[a * b] +|=== + +===== Division + +[cols="1h,1,2"] +|=== +| Type | `math/div` | Division operation +.2+| Input value sockets +| `floatN a` | Dividend +| `floatN b` | Divisor +| Output value sockets +| `floatN value` | Quotient, stem:[a / b] +|=== + +===== Remainder + +[cols="1h,1,2"] +|=== +| Type | `math/rem` | Remainder operation +.2+| Input value sockets +| `floatN a` | Dividend +| `floatN b` | Divisor +| Output value sockets +| `floatN value` | latexmath:[a - (b \cdot trunc(\frac{a}{b}))] +|=== + +===== Minimum + +[cols="1h,1,2"] +|=== +| Type | `math/min` | Minimum operation +.2+| Input value sockets +| `floatN a` | First argument +| `floatN b` | Second argument +| Output value sockets +| `floatN value` | Smallest of the arguments +|=== + +===== Maximum + +[cols="1h,1,2"] +|=== +| Type | `math/max` | Maximum operation +.2+| Input value sockets +| `floatN a` | First argument +| `floatN b` | Second argument +| Output value sockets +| `floatN value` | Largest of the arguments +|=== + +===== Clamp + +[cols="1h,1,2"] +|=== +| Type | `math/clamp` | Clamp operation +.3+| Input value sockets +| `floatN a` | Value to clamp +| `floatN b` | Lower boundary +| `floatN c` | Upper boundary +| Output value sockets +| `floatN value` | latexmath:[min(max(a, b), c)] +|=== + +===== Saturate + +[cols="1h,1,2"] +|=== +| Type | `math/saturate` | Saturate operation +| Input value sockets +| `floatN a` | Value to saturate +| Output value sockets +| `floatN value` | latexmath:[clamp(a, 0, 1)] +|=== + +==== Angle and Trigonometry Nodes + +Node parameters specified as angle are assumed to be in units of radians. + +These all operate component-wise. The description is per component. + +If any input value is _NaN_, the output value is also _NaN_. + +===== Degrees-To-Radians + +[cols="1h,1,2"] +|=== +| Type | `math/rad` | Converts degrees to radians +| Input value sockets +| `floatN a` | Value in degrees +| Output value sockets +| `floatN value` | stem:[a * pi / 180.0] +|=== + +===== Radians-To-Degrees + +[cols="1h,1,2"] +|=== +| Type | `math/deg` | Converts radians to degrees +| Input value sockets +| `floatN a` | Value in radians +| Output value sockets +| `floatN value` | stem:[a * 180.0 / pi] +|=== + +===== Sine + +[cols="1h,1,2"] +|=== +| Type | `math/sin` | Sine function +| Input value sockets +| `floatN a` | Angle +| Output value sockets +| `floatN value` | latexmath:[\begin{cases}sin(a), \text{if } a \ne \pm\infty \\ \mathit{NaN}, \text{if } a = \pm\infty\end{cases}] +|=== + +===== Cosine + +[cols="1h,1,2"] +|=== +| Type | `math/cos` | Cosine function +| Input value sockets +| `floatN a` | Angle +| Output value sockets +| `floatN value` | latexmath:[\begin{cases}cos(a), \text{if } a \ne \pm\infty \\ \mathit{NaN}, \text{if } a = \pm\infty\end{cases}] +|=== + +===== Tangent + +[cols="1h,1,2"] +|=== +| Type | `math/tan` | Tangent function +| Input value sockets +| `floatN a` | Angle +| Output value sockets +| `floatN value` | latexmath:[\begin{cases}tan(a), \text{if } a \ne \pm\infty \\ \mathit{NaN}, \text{if } a = \pm\infty\end{cases}] +|=== + +===== Arcsine + +[cols="1h,1,2"] +|=== +| Type | `math/asin` | Arcsine function +| Input value sockets +| `floatN a` | Sine value +| Output value sockets +| `floatN value` | latexmath:[\begin{cases}arcsin(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\], \text{if } \|a\| \le 1 \\ \mathit{NaN}, \text{if } \|a\| > 1\end{cases}] +|=== + +===== Arccosine + +[cols="1h,1,2"] +|=== +| Type | `math/acos` | Arccosine function +| Input value sockets +| `floatN a` | Cosine value +| Output value sockets +| `floatN value` | latexmath:[\begin{cases}arccos(a) \in [0; \pi\], \text{if } \|a\| \le 1 \\ \mathit{NaN}, \text{if } \|a\| > 1\end{cases}] +|=== + +===== Arctangent + +[cols="1h,1,2"] +|=== +| Type | `math/atan` | Arctangent function +| Input value sockets +| `floatN a` | Tangent value +| Output value sockets +| `floatN value` | latexmath:[arctan(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\]] +|=== + +===== Arctangent 2 + +[cols="1h,1,2"] +|=== +| Type | `math/atan2` | Arctangent 2 function +.2+| Input value sockets +| `floatN a` | Y coordinate +| `floatN b` | X coordinate +| Output value sockets +| `floatN value` | Angle between the positive X-axis and the vector from the stem:[(0, 0)] origin to the stem:[(X, Y)] point on a 2D plane +|=== + +Zero and infinity argument values are handled according to <> or <> standards. + +==== Hyperbolic Functions + +These all operate component-wise. The description is per component. + +If any input value is _NaN_, the output value is also _NaN_. + +===== Hyperbolic Sine + +[cols="1h,1,2"] +|=== +| Type |`math/sinh`| Hyperbolic sine function +| Input value sockets +| `floatN a` | Hyperbolic angle value +|Output value sockets +| `floatN value` | latexmath:[\dfrac{e^a-e^{-a}}{2}] +|=== + +===== Hyperbolic Cosine + +[cols="1h,1,2"] +|=== +| Type |`math/cosh`| Hyperbolic cosine function +| Input value sockets +| `floatN a` | Hyperbolic angle value +|Output value sockets +| `floatN value` | latexmath:[\dfrac{e^a+e^{-a}}{2}] +|=== + +===== Hyperbolic Tangent + +[cols="1h,1,2"] +|=== +| Type |`math/tanh`| Hyperbolic tangent function +| Input value sockets +| `floatN a` | Hyperbolic angle value +|Output value sockets +| `floatN value` | latexmath:[\dfrac{e^a-e^{-a}}{e^a+e^{-a}}] +|=== + +===== Inverse Hyperbolic Sine + +[cols="1h,1,2"] +|=== +| Type |`math/asinh`| Inverse hyperbolic sine function +| Input value sockets +| `floatN a` | Hyperbolic sine value +|Output value sockets +| `floatN value` | latexmath:[ln(a+\sqrt{a^2+1})] +|=== + +===== Inverse Hyperbolic Cosine + +[cols="1h,1,2"] +|=== +| Type |`math/acosh`| Inverse hyperbolic cosine function +| Input value sockets +| `floatN a` | Hyperbolic cosine value +|Output value sockets +| `floatN value` | latexmath:[\begin{cases}ln(a+\sqrt{a^2-1}), \text{if } a \ge 1 \\ \mathit{NaN}, \text{if } a < 1\end{cases}] +|=== + +===== Inverse Hyperbolic Tangent + +[cols="1h,1,2"] +|=== +| Type |`math/atanh`| Inverse hyperbolic tangent function +| Input value sockets +| `floatN a` | Hyperbolic tangent value +|Output value sockets +| `floatN value` | latexmath:[\begin{cases}\dfrac{1}{2}ln\dfrac{1+a}{1-a}, \text{if } \|a\| \le 1 \\ \mathit{NaN}, \text{if } \|a\| > 1\end{cases}] +|=== + +==== Exponential Functions + +These all operate component-wise. The description is per component. + +If any input value is _NaN_, the output value is also _NaN_. + +===== Exponent + +[cols="1h,1,2"] +|=== +| Type | `math/exp` | Exponent function +| Input value sockets +| `floatN a` | Power value +|Output value sockets +| `floatN value` | stem:[e^a] +|=== + +===== Natural Logarithm + +[cols="1h,1,2"] +|=== +| Type | `math/log` | Natural logarithm function +| Input value sockets +| `floatN a` | Argument value +|Output value sockets +| `floatN value` | latexmath:[\begin{cases}ln(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +|=== + +===== Base-2 Logarithm + +[cols="1h,1,2"] +|=== +| Type | `math/log2` | Base-2 logarithm function +| Input value sockets +| `floatN a` | Argument +|Output value sockets +| `floatN value` | latexmath:[\begin{cases}log_2(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +|=== + +===== Base-10 Logarithm + +[cols="1h,1,2"] +|=== +| Type | `math/log10` | Base-10 logarithm function +| Input value sockets +| `floatN a` | Argument +|Output value sockets +| `floatN value` | latexmath:[\begin{cases}log_{10}(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +|=== + +===== Square Root + +[cols="1h,1,2"] +|=== +| Type | `math/sqrt` | Square root function +| Input value sockets +| `floatN a` | Radicand +|Output value sockets +| `floatN value` | latexmath:[\begin{cases}\sqrt{a}, \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +|=== + +===== Cube Root + +[cols="1h,1,2"] +|=== +| Type | `math/cbrt` | Cube root function +| Input value sockets +| `floatN a` | Radicand +|Output value sockets +| `floatN value` | latexmath:[\sqrt[3\]{a}] +|=== + +===== Power + +[cols="1h,1,2"] +|=== +| Type | `math/pow` | Power function +.2+| Input value sockets +| `floatN a` | Base +| `floatN b` | Exponent +| Output value sockets +| `floatN value` | stem:[a^b] +|=== + +Zero and infinity argument values are handled according to the <> standard. From 151f3add31f25d4475c62d6b3d176a59605a4489 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:00:00 +0000 Subject: [PATCH 03/80] Add vector and more arithmetic nodes; editorial changes --- .../KHR_interactivity/Specification.adoc | 135 +++++++++++++++--- 1 file changed, 118 insertions(+), 17 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index cef5444c5c..d2d20342e1 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -231,7 +231,7 @@ _Output flow sockets_ represent "`function pointers`" that the node will call to _Input flow sockets_ represent "`methods`" that could be called on the node. For example, flow control nodes (such as loops and conditions) usually have an `in` input flow socket that starts node's execution. Additional operations **MAY** also be defined such as `reset` for nodes having an internal state. -Nodes **MAY** be configurable through static properties collectively called "`node's configuration`" that **MAY** affect the node's behavior and the number of its sockets, such as the number of cases for a switch-case control flow node. +Nodes **MAY** be configurable through static properties collectively called "`node's configuration`" that **MAY** affect the node's behavior and the number of its sockets, such as the number of cases for a switch-case control flow node. Input and output value sockets have associated data types, e.g., floats, integers, booleans, etc. @@ -327,7 +327,7 @@ If the argument is infinity, it is returned unchanged. | Input value sockets | `floatN a` | Argument | Output value sockets -| `floatN value` | Value equal to the nearest integer that is less than or equal to stem:[a] +| `floatN value` | stem:[floor(a)], value equal to the nearest integer that is less than or equal to stem:[a] |=== If the argument is infinity, it is returned unchanged. @@ -340,11 +340,22 @@ If the argument is infinity, it is returned unchanged. | Input value sockets | `floatN a` | Argument | Output value sockets -| `floatN value` | Value equal to the nearest integer that is greater than or equal to stem:[a] +| `floatN value` | stem:[ceil(a)], value equal to the nearest integer that is greater than or equal to stem:[a] |=== If the argument is infinity, it is returned unchanged. +===== Fraction + +[cols="1h,1,2"] +|=== +| Type | `math/fract` | Fractional operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | stem:[a - floor(a)] +|=== + ===== Negation [cols="1h,1,2"] @@ -464,6 +475,19 @@ If the argument is infinity, it is returned unchanged. | `floatN value` | latexmath:[clamp(a, 0, 1)] |=== +===== Interpolate + +[cols="1h,1,2"] +|=== +| Type | `math/mix` | Linear interpolation operation +.3+| Input value sockets +| `floatN a` | Interpolated value at stem:[0.0] +| `floatN b` | Interpolated value at stem:[1.0] +| `floatN c` | Unclamped interpolation coefficient +| Output value sockets +| `floatN value` | stem:[(1.0 - c) * a + c * b] +|=== + ==== Angle and Trigonometry Nodes Node parameters specified as angle are assumed to be in units of radians. @@ -574,7 +598,7 @@ If any input value is _NaN_, the output value is also _NaN_. Zero and infinity argument values are handled according to <> or <> standards. -==== Hyperbolic Functions +==== Hyperbolic Nodes These all operate component-wise. The description is per component. @@ -587,7 +611,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type |`math/sinh`| Hyperbolic sine function | Input value sockets | `floatN a` | Hyperbolic angle value -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\dfrac{e^a-e^{-a}}{2}] |=== @@ -598,7 +622,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type |`math/cosh`| Hyperbolic cosine function | Input value sockets | `floatN a` | Hyperbolic angle value -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\dfrac{e^a+e^{-a}}{2}] |=== @@ -609,7 +633,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type |`math/tanh`| Hyperbolic tangent function | Input value sockets | `floatN a` | Hyperbolic angle value -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\dfrac{e^a-e^{-a}}{e^a+e^{-a}}] |=== @@ -620,7 +644,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type |`math/asinh`| Inverse hyperbolic sine function | Input value sockets | `floatN a` | Hyperbolic sine value -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[ln(a+\sqrt{a^2+1})] |=== @@ -631,7 +655,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type |`math/acosh`| Inverse hyperbolic cosine function | Input value sockets | `floatN a` | Hyperbolic cosine value -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\begin{cases}ln(a+\sqrt{a^2-1}), \text{if } a \ge 1 \\ \mathit{NaN}, \text{if } a < 1\end{cases}] |=== @@ -642,11 +666,11 @@ If any input value is _NaN_, the output value is also _NaN_. | Type |`math/atanh`| Inverse hyperbolic tangent function | Input value sockets | `floatN a` | Hyperbolic tangent value -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\begin{cases}\dfrac{1}{2}ln\dfrac{1+a}{1-a}, \text{if } \|a\| \le 1 \\ \mathit{NaN}, \text{if } \|a\| > 1\end{cases}] |=== -==== Exponential Functions +==== Exponential Nodes These all operate component-wise. The description is per component. @@ -659,7 +683,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type | `math/exp` | Exponent function | Input value sockets | `floatN a` | Power value -|Output value sockets +| Output value sockets | `floatN value` | stem:[e^a] |=== @@ -670,7 +694,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type | `math/log` | Natural logarithm function | Input value sockets | `floatN a` | Argument value -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\begin{cases}ln(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] |=== @@ -681,7 +705,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type | `math/log2` | Base-2 logarithm function | Input value sockets | `floatN a` | Argument -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\begin{cases}log_2(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] |=== @@ -692,7 +716,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type | `math/log10` | Base-10 logarithm function | Input value sockets | `floatN a` | Argument -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\begin{cases}log_{10}(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] |=== @@ -703,7 +727,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type | `math/sqrt` | Square root function | Input value sockets | `floatN a` | Radicand -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\begin{cases}\sqrt{a}, \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] |=== @@ -714,7 +738,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Type | `math/cbrt` | Cube root function | Input value sockets | `floatN a` | Radicand -|Output value sockets +| Output value sockets | `floatN value` | latexmath:[\sqrt[3\]{a}] |=== @@ -731,3 +755,80 @@ If any input value is _NaN_, the output value is also _NaN_. |=== Zero and infinity argument values are handled according to the <> standard. + +==== Vector Nodes + +If any input value is _NaN_, the output value is also _NaN_. + +===== Length + +[cols="1h,1,2"] +|=== +| Type | `math/length` | Vector length +| Input value sockets +| `float{2\|3\|4} a` | Vector +| Output value sockets +| `float value` | Length of stem:[a], e.g., stem:[sqrt(a_x^2 + a_y^2)] for `float2` +|=== + +===== Normalize + +[cols="1h,1,2"] +|=== +| Type | `math/normalize` | Vector normalization +| Input value sockets +| `float{2\|3\|4} a` | Vector +| Output value sockets +| `floatN value` | Vector in the same direction as stem:[a] but with a unit length, e.g., stem:[a/sqrt(a_x^2 + a_y^2)] for `float2` +|=== + +===== Dot Product + +[cols="1h,1,2"] +|=== +| Type | `math/dot` | Dot product +.2+| Input value sockets +| `float{2\|3\|4} a` | First vector +| `float{2\|3\|4} b` | Second vector of the same type as stem:[a] +| Output value sockets +| `float value` | Sum of per-component products of stem:[a] and stem:[b], e.g., stem:[a_x * b_x + a_y * b_y] for `float2` +|=== + +===== Cross Product + +[cols="1h,1,2"] +|=== +| Type | `math/cross` | Cross product +.2+| Input value sockets +| `float3 a` | Vector +| `float3 b` | Vector +| Output value sockets +| `float3 value` | Cross product of stem:[a] and stem:[b], i.e., stem:[(a_y * b_z - a_z * b_y, a_z * b_x - a_x * b_z, a_x * b_y - a_y * b_x)] +|=== + +===== Rotate 2D + +[cols="1h,1,2"] +|=== +| Type | `math/rotate` | 2D rotation +.2+| Input value sockets +| `float2 a` | Vector to rotate +| `float b` | Angle in radians +| Output value sockets +| `float2 value` | Vector stem:[a] rotated counter-clockwise by stem:[b] +|=== + +===== Rotate 3D + +[cols="1h,1,2"] +|=== +| Type | `math/rotate` | 3D rotation +.3+| Input value sockets +| `float3 a` | Vector to rotate +| `float3 b` | Vector representing an axis to rotate around +| `float c` | Angle in radians +| Output value sockets +| `float3 value` | Vector stem:[a] rotated around vector stem:[b] counter-clockwise by stem:[c] +|=== + +If the vector stem:[b] is not unit, rotation results may be undefined. From 47c12faa4907cd9cc7699e5396f22fede4d8335b Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 23 Oct 2023 00:00:00 +0000 Subject: [PATCH 04/80] Add comparison nodes; address feedback --- .../KHR_interactivity/Specification.adoc | 92 ++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index d2d20342e1..434ab10556 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -488,6 +488,94 @@ If the argument is infinity, it is returned unchanged. | `floatN value` | stem:[(1.0 - c) * a + c * b] |=== +==== Comparison Nodes + +If any input value is _NaN_, the output value is false. + +===== Equality + +[cols="1h,1,2"] +|=== +| Type | `math/eq` | Equality operation +.2+| Input value sockets +| `floatN a` | First argument +| `floatN b` | Second argument +| Output value sockets +| `bool value` | True if the input arguments are equal, per-component; false otherwise +|=== + +===== Less Than + +[cols="1h,1,2"] +|=== +| Type | `math/lt` | Less than operation +.2+| Input value sockets +| `float a` | First argument +| `float b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a < b]; false otherwise +|=== + +===== Less Than Or Equal To + +[cols="1h,1,2"] +|=== +| Type | `math/le` | Less than or equal to operation +.2+| Input value sockets +| `float a` | First argument +| `float b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a <= b]; false otherwise +|=== + +===== Greater Than + +[cols="1h,1,2"] +|=== +| Type | `math/gt` | Greater than operation +.2+| Input value sockets +| `float a` | First argument +| `float b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a > b]; false otherwise +|=== + +===== Greater Than Or Equal To + +[cols="1h,1,2"] +|=== +| Type | `math/ge` | Greater than or equal operation +.2+| Input value sockets +| `float a` | First argument +| `float b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a >= b]; false otherwise +|=== + +==== Special Floating-Point Nodes + +===== Is Not a Number + +[cols="1h,1,2"] +|=== +| Type | `math/isnan` | Not a Number check operation +| Input value sockets +| `float a` | Argument +| Output value sockets +| `bool value` | True if stem:[a] is _NaN_; false otherwise +|=== + +===== Is Infinity + +[cols="1h,1,2"] +|=== +| Type | `math/isinf` | Infinity check operation +| Input value sockets +| `float a` | Argument +| Output value sockets +| `bool value` | True if stem:[a] is positive or negative infinity; false otherwise +|=== + ==== Angle and Trigonometry Nodes Node parameters specified as angle are assumed to be in units of radians. @@ -810,7 +898,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/rotate` | 2D rotation +| Type | `math/rotate2d` | 2D rotation .2+| Input value sockets | `float2 a` | Vector to rotate | `float b` | Angle in radians @@ -822,7 +910,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/rotate` | 3D rotation +| Type | `math/rotate3d` | 3D rotation .3+| Input value sockets | `float3 a` | Vector to rotate | `float3 b` | Vector representing an axis to rotate around From 736e6869ab721e493bc6b2bc290e5b0becfff575 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 6 Nov 2023 00:00:00 +0000 Subject: [PATCH 05/80] Add special float constants --- .../Khronos/KHR_interactivity/Specification.adoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 434ab10556..a9db9fda9a 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -278,6 +278,22 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 | Output value sockets | `float value` | 3.141592653589793 |=== +===== Infinity + +[cols="1h,1,2"] +|=== +| Type | `math/inf` | Positive infinity +| Output value sockets | `float value` | _Infinity_ +|=== + +===== Not a Number + +[cols="1h,1,2"] +|=== +| Type | `math/nan` | Not a Number +| Output value sockets | `float value` | _NaN_ +|=== + ==== Arithmetic Nodes These all operate component-wise. The description is per component. From 667fdb1905998b8b8e610df5d202cf3d350c8132 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Tue, 14 Nov 2023 00:00:00 +0000 Subject: [PATCH 06/80] Add matrix and integer nodes; update formatting --- .../KHR_interactivity/Specification.adoc | 626 +++++++++++++++++- 1 file changed, 603 insertions(+), 23 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index a9db9fda9a..01fefba341 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -308,7 +308,11 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Argument | Output value sockets -| `floatN value` | latexmath:[\begin{cases}a, \text{if } a \ge 0.0 \\ -a, \text{if } a < 0.0 \end{cases}] +| `floatN value` | latexmath:[\begin{cases} + -a & \text{if } a \lt 0 \\ + 0 & \text{if } a = 0 \\ + a & \text{if } a \gt 0 + \end{cases}] |=== ===== Sign @@ -319,7 +323,11 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Argument | Output value sockets -| `floatN value` | latexmath:[\begin{cases}-1.0, \text{if } a < 0.0 \\ a, \text{if } a = \pm0.0 \\ 1.0, \text{if } a > 0.0 \end{cases}] +| `floatN value` | latexmath:[\begin{cases} + -1 & \text{if } a \lt 0 \\ + a & \text{if } a = \pm0 \\ + +1 & \text{if } a \gt 0 + \end{cases}] |=== ===== Truncate @@ -419,6 +427,12 @@ If the argument is infinity, it is returned unchanged. | `floatN value` | Product, stem:[a * b] |=== +[NOTE] +.Note +==== +For `float4x4` arguments, this operation performs per-component multiplication. +==== + ===== Division [cols="1h,1,2"] @@ -440,7 +454,11 @@ If the argument is infinity, it is returned unchanged. | `floatN a` | Dividend | `floatN b` | Divisor | Output value sockets -| `floatN value` | latexmath:[a - (b \cdot trunc(\frac{a}{b}))] +| `floatN value` | latexmath:[\begin{cases} + \mathit{NaN} & \text{if } a = \pm \infty \text{ or } b = \pm 0 \\ + a & \text{if } a \ne \pm \infty \text{ and } b = \pm \infty \\ + a - (b \cdot trunc(\frac{a}{b})) & \text{otherwise} + \end{cases}] |=== ===== Minimum @@ -474,12 +492,18 @@ If the argument is infinity, it is returned unchanged. | Type | `math/clamp` | Clamp operation .3+| Input value sockets | `floatN a` | Value to clamp -| `floatN b` | Lower boundary -| `floatN c` | Upper boundary +| `floatN b` | First boundary +| `floatN c` | Second boundary | Output value sockets -| `floatN value` | latexmath:[min(max(a, b), c)] +| `floatN value` | latexmath:[min(max(a, min(b, c)), max(b, c))] |=== +[NOTE] +.Note +==== +This operation correctly handles a case when stem:[b] is greater than stem:[c]. +==== + ===== Saturate [cols="1h,1,2"] @@ -488,7 +512,7 @@ If the argument is infinity, it is returned unchanged. | Input value sockets | `floatN a` | Value to saturate | Output value sockets -| `floatN value` | latexmath:[clamp(a, 0, 1)] +| `floatN value` | latexmath:[min(max(a, 0), 1)] |=== ===== Interpolate @@ -497,11 +521,11 @@ If the argument is infinity, it is returned unchanged. |=== | Type | `math/mix` | Linear interpolation operation .3+| Input value sockets -| `floatN a` | Interpolated value at stem:[0.0] -| `floatN b` | Interpolated value at stem:[1.0] +| `floatN a` | Interpolated value at stem:[0] +| `floatN b` | Interpolated value at stem:[1] | `floatN c` | Unclamped interpolation coefficient | Output value sockets -| `floatN value` | stem:[(1.0 - c) * a + c * b] +| `floatN value` | stem:[(1 - c) * a + c * b] |=== ==== Comparison Nodes @@ -608,7 +632,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Value in degrees | Output value sockets -| `floatN value` | stem:[a * pi / 180.0] +| `floatN value` | stem:[a * pi / 180] |=== ===== Radians-To-Degrees @@ -619,7 +643,7 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Value in radians | Output value sockets -| `floatN value` | stem:[a * 180.0 / pi] +| `floatN value` | stem:[a * 180 / pi] |=== ===== Sine @@ -630,7 +654,10 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Angle | Output value sockets -| `floatN value` | latexmath:[\begin{cases}sin(a), \text{if } a \ne \pm\infty \\ \mathit{NaN}, \text{if } a = \pm\infty\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + sin(a) & \text{if } a \ne \pm\infty \\ + \mathit{NaN} & \text{if } a = \pm\infty + \end{cases}] |=== ===== Cosine @@ -641,7 +668,10 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Angle | Output value sockets -| `floatN value` | latexmath:[\begin{cases}cos(a), \text{if } a \ne \pm\infty \\ \mathit{NaN}, \text{if } a = \pm\infty\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + cos(a) & \text{if } a \ne \pm\infty \\ + \mathit{NaN} & \text{if } a = \pm\infty + \end{cases}] |=== ===== Tangent @@ -652,7 +682,10 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Angle | Output value sockets -| `floatN value` | latexmath:[\begin{cases}tan(a), \text{if } a \ne \pm\infty \\ \mathit{NaN}, \text{if } a = \pm\infty\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + tan(a) & \text{if } a \ne \pm\infty \\ + \mathit{NaN} & \text{if } a = \pm\infty + \end{cases}] |=== ===== Arcsine @@ -663,7 +696,10 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Sine value | Output value sockets -| `floatN value` | latexmath:[\begin{cases}arcsin(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\], \text{if } \|a\| \le 1 \\ \mathit{NaN}, \text{if } \|a\| > 1\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + arcsin(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\] & \text{if } \|a\| \le 1 \\ + \mathit{NaN} & \text{if } \|a\| \gt 1 + \end{cases}] |=== ===== Arccosine @@ -674,7 +710,10 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Cosine value | Output value sockets -| `floatN value` | latexmath:[\begin{cases}arccos(a) \in [0; \pi\], \text{if } \|a\| \le 1 \\ \mathit{NaN}, \text{if } \|a\| > 1\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + arccos(a) \in [0; \pi\] & \text{if } \|a\| \le 1 \\ + \mathit{NaN} & \text{if } \|a\| \gt 1 + \end{cases}] |=== ===== Arctangent @@ -760,7 +799,10 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Hyperbolic cosine value | Output value sockets -| `floatN value` | latexmath:[\begin{cases}ln(a+\sqrt{a^2-1}), \text{if } a \ge 1 \\ \mathit{NaN}, \text{if } a < 1\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + ln(a+\sqrt{a^2-1}) & \text{if } a \ge 1 \\ + \mathit{NaN} & \text{if } a \lt 1 + \end{cases}] |=== ===== Inverse Hyperbolic Tangent @@ -771,7 +813,11 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Hyperbolic tangent value | Output value sockets -| `floatN value` | latexmath:[\begin{cases}\dfrac{1}{2}ln\dfrac{1+a}{1-a}, \text{if } \|a\| \le 1 \\ \mathit{NaN}, \text{if } \|a\| > 1\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + \dfrac{1}{2}ln\dfrac{1+a}{1-a} & \text{if } \|a\| \lt 1 \\ + \pm\infty & \text{if } a = \pm1 \\ + \mathit{NaN} & \text{if } \|a\| \gt 1 + \end{cases}] |=== ==== Exponential Nodes @@ -799,7 +845,11 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Argument value | Output value sockets -| `floatN value` | latexmath:[\begin{cases}ln(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + ln(a) & \text{if } a \gt 0 \\ + -\infty & \text{if } a = 0 \\ + \mathit{NaN} & \text{if } a \lt 0 + \end{cases}] |=== ===== Base-2 Logarithm @@ -810,7 +860,11 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Argument | Output value sockets -| `floatN value` | latexmath:[\begin{cases}log_2(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + log_2(a) & \text{if } a \gt 0 \\ + -\infty & \text{if } a = 0 \\ + \mathit{NaN} & \text{if } a \lt 0 + \end{cases}] |=== ===== Base-10 Logarithm @@ -821,7 +875,11 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Argument | Output value sockets -| `floatN value` | latexmath:[\begin{cases}log_{10}(a), \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + log_{10}(a) & \text{if } a \gt 0 \\ + -\infty & \text{if } a = 0 \\ + \mathit{NaN} & \text{if } a \lt 0 + \end{cases}] |=== ===== Square Root @@ -832,7 +890,10 @@ If any input value is _NaN_, the output value is also _NaN_. | Input value sockets | `floatN a` | Radicand | Output value sockets -| `floatN value` | latexmath:[\begin{cases}\sqrt{a}, \text{if } a \ge 0 \\ \mathit{NaN}, \text{if } a < 0\end{cases}] +| `floatN value` | latexmath:[\begin{cases} + \sqrt{a} & \text{if } a \ge 0 \\ + \mathit{NaN} & \text{if } a \lt 0 + \end{cases}] |=== ===== Cube Root @@ -936,3 +997,522 @@ If any input value is _NaN_, the output value is also _NaN_. |=== If the vector stem:[b] is not unit, rotation results may be undefined. + +===== Transform + +[cols="1h,1,2"] +|=== +| Type | `math/transform` | Vector transformation +.2+| Input value sockets +| `float4 a` | Vector to transform +| `float4x4 b` | Transformation matrix +| Output value sockets +| `float4 value` | Transformed vector +|=== + +==== Matrix Nodes + +===== Transpose + +[cols="1h,1,2"] +|=== +| Type | `math/transpose` | Transpose operation +| Input value sockets +| `float4x4 a` | Matrix to transpose +| Output value sockets +| `float4x4 value` | Matrix that is the transpose of stem:[a] +|=== + +===== Determinant + +[cols="1h,1,2"] +|=== +| Type | `math/determinant` | Dot product +| Input value sockets +| `float4x4 a` | Matrix +| Output value sockets +| `float value` | Determinant of stem:[a] +|=== + +===== Inverse + +[cols="1h,1,2"] +|=== +| Type | `math/inverse` | Inverse operation +| Input value sockets +| `float4x4 a` | Matrix to inverse +| Output value sockets +| `float4x4 value` | Matrix that is the inverse of stem:[a] +|=== + +===== Multiplication + +[cols="1h,1,2"] +|=== +| Type | `math/matmul` | Matrix multiplication operation +.2+| Input value sockets +| `float4x4 a` | First matrix +| `float4x4 b` | Second matrix +| Output value sockets +| `float4x4 value` | Matrix product +|=== + +==== Integer Arithmetic Nodes + +All inputs to these nodes are two's complement 32-bit signed integers. + +===== Absolute Value + +[cols="1h,1,2"] +|=== +| Type | `math/abs` | Absolute value operation +| Input value sockets +| `int a` | Argument +| Output value sockets +| `int value` | latexmath:[\begin{cases} + -a & \text{if } a \lt 0 \\ + a & \text{if } a \ge 0 + \end{cases}] +|=== + +As this node is defined in terms of the negation node (see below), the absolute value of `-2147483648` is `-2147483648`. + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +Math.abs(a)|0 +---- +==== + +===== Sign + +[cols="1h,1,2"] +|=== +| Type | `math/sign` | Sign operation +| Input value sockets +| `int a` | Argument +| Output value sockets +| `int value` | latexmath:[\begin{cases} + -1 & \text{if } a \lt 0 \\ + 0 & \text{if } a = 0 \\ + +1 & \text{if } a \gt 0 + \end{cases}] +|=== + +===== Negation + +[cols="1h,1,2"] +|=== +| Type | `math/neg` | Negation operation +| Input value sockets +| `int a` | Argument +| Output value sockets +| `int value` | stem:[-a] +|=== + +Negating `-2147483648` **MUST** return `-2147483648`. + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +(-a)|0 +---- +==== + +===== Addition + +[cols="1h,1,2"] +|=== +| Type | `math/add` | Addition operation +.2+| Input value sockets +| `int a` | First addend +| `int b` | Second addend +| Output value sockets +| `int value` | Sum, stem:[a + b] +|=== + +Arithmetic overflow **MUST** wrap around, for example: + +[source] +---- +2147483647 + 1 == -2147483648 +---- + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +(a + b)|0 +---- +==== + +===== Subtraction + +[cols="1h,1,2"] +|=== +| Type | `math/sub` | Subtraction operation +.2+| Input value sockets +| `int a` | Minuend +| `int b` | Subtrahend +| Output value sockets +| `int value` | Difference, stem:[a - b] +|=== + +Arithmetic overflow **MUST** wrap around, for example: + +[source] +---- +-2147483648 - 1 == 2147483647 +---- + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +(a - b)|0 +---- +==== + +===== Multiplication + +[cols="1h,1,2"] +|=== +| Type | `math/mul` | Multiplication operation +.2+| Input value sockets +| `int a` | First factor +| `int b` | Second factor +| Output value sockets +| `int value` | Product, stem:[a * b] +|=== + +Arithmetic overflow **MUST** wrap around, for example: + +[source] +---- + 2147483647 * 2147483647 == 1 + +-2147483648 * (-1) == -2147483648 +---- + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +Math.imul(a, b) +---- +==== + +===== Division + +[cols="1h,1,2"] +|=== +| Type | `math/div` | Division operation +.2+| Input value sockets +| `int a` | Dividend +| `int b` | Divisor +| Output value sockets +| `int value` | latexmath:[\begin{cases} + \frac{a}{b} & \text{if } b \ne 0 \\ + 0 & \text{if } b = 0 + \end{cases}] +|=== + +The quotient **MUST** be truncated towards zero. + +Arithmetic overflow is defined as follows: +[source] +---- +-2147483648 / (-1) == -2147483648 +---- + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +(a / b)|0 +---- +==== + +===== Remainder + +[cols="1h,1,2"] +|=== +| Type | `math/rem` | Remainder operation +.2+| Input value sockets +| `int a` | Dividend +| `int b` | Divisor +| Output value sockets +| `int value` | latexmath:[\begin{cases} + a - (b \cdot trunc(\frac{a}{b})) & \text{if } b \ne 0 \\ + 0 & \text{if } b = 0 + \end{cases}] +|=== + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +(a % b)|0 +---- +==== + +===== Minimum + +[cols="1h,1,2"] +|=== +| Type | `math/min` | Minimum operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `int value` | Smallest of the arguments +|=== + +===== Maximum + +[cols="1h,1,2"] +|=== +| Type | `math/max` | Maximum operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `int value` | Largest of the arguments +|=== + +===== Clamp + +[cols="1h,1,2"] +|=== +| Type | `math/clamp` | Clamp operation +.3+| Input value sockets +| `int a` | Value to clamp +| `int b` | First boundary +| `int c` | Second boundary +| Output value sockets +| `int value` | latexmath:[min(max(a, min(b, c)), max(b, c))] +|=== + +[NOTE] +.Note +==== +This operation correctly handles a case when stem:[b] is greater than stem:[c]. +==== + +==== Integer Comparison Nodes + +All inputs to these nodes are two's complement 32-bit signed integers. + +===== Equality + +[cols="1h,1,2"] +|=== +| Type | `math/eq` | Equality operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `bool value` | True if the input arguments are equal; false otherwise +|=== + +===== Less Than + +[cols="1h,1,2"] +|=== +| Type | `math/lt` | Less than operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a < b]; false otherwise +|=== + +===== Less Than Or Equal To + +[cols="1h,1,2"] +|=== +| Type | `math/le` | Less than or equal to operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a <= b]; false otherwise +|=== + +===== Greater Than + +[cols="1h,1,2"] +|=== +| Type | `math/gt` | Greater than operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a > b]; false otherwise +|=== + +===== Greater Than Or Equal To + +[cols="1h,1,2"] +|=== +| Type | `math/ge` | Greater than or equal operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `bool value` | True if stem:[a >= b]; false otherwise +|=== + +==== Integer Bitwise Nodes + +All inputs to these nodes are two's complement 32-bit signed integers. + +===== Bitwise NOT + +[cols="1h,1,2"] +|=== +| Type | `math/not` | Bitwise NOT operation +| Input value sockets +| `int a` | Argument +| Output value sockets +| `int value` | `~a` +|=== + +===== Bitwise AND + +[cols="1h,1,2"] +|=== +| Type | `math/and` | Bitwise AND operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `int value` | `a & b` +|=== + +===== Bitwise OR + +[cols="1h,1,2"] +|=== +| Type | `math/or` | Bitwise OR operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `int value` | `a \| b` +|=== + +===== Bitwise XOR + +[cols="1h,1,2"] +|=== +| Type | `math/xor` | Bitwise XOR operation +.2+| Input value sockets +| `int a` | First argument +| `int b` | Second argument +| Output value sockets +| `int value` | `a ^ b` +|=== + +===== Right Shift + +[cols="1h,1,2"] +|=== +| Type | `math/asr` | Right Shift +.2+| Input value sockets +| `int a` | Value to be shifted +| `int b` | Number of bits to shift by +| Output value sockets +| `int value` | `a >> b` +|=== + +Only the lowest 5 bits of stem:[b] are considered, i.e., its effective range is [0, 31]. The result **MUST** be truncated to 32 bits and interpreted as a two's complement signed integer. The most significant bit of stem:[a] **MUST** be propagated. + +===== Left Shift + +[cols="1h,1,2"] +|=== +| Type | `math/lsl` | Left Shift +.2+| Input value sockets +| `int a` | Value to be shifted +| `int b` | Number of bits to shift by +| Output value sockets +| `int value` | `a << b` +|=== + +Only the lowest 5 bits of stem:[b] are considered, i.e., its effective range is [0, 31]. The result **MUST** be truncated to 32 bits and interpreted as a two's complement signed integer. + +===== Count Leading Zeros + +[cols="1h,1,2"] +|=== +| Type | `math/clz` | Count leading zeros operation +| Input value sockets +| `int a` | Argument +| Output value sockets +| `int value` | Number of leading zero bits in stem:[a] +|=== + +If stem:[a] is 0, the operation returns 32; if stem:[a] is negative, the operation returns 0. + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +Math.clz32(a) +---- +==== + +===== Count Trailing Zeros + +[cols="1h,1,2"] +|=== +| Type | `math/ctz` | Count trailing zeros operation +| Input value sockets +| `int a` | Argument +| Output value sockets +| `int value` | Number of trailing zero bits in stem:[a] +|=== + +If stem:[a] is 0, the operation returns 32. + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +a ? (31 - Math.clz32(a & -a)) : 32; +---- +==== + +===== Count One Bits + +[cols="1h,1,2"] +|=== +| Type | `math/popcnt` | Count set bits operation +| Input value sockets +| `int a` | Argument +| Output value sockets +| `int value` | Number of set bits in stem:[a] +|=== + +If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation returns 32. From 4b7b4fea804ceabec22a17645ca6d71277aae150 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 22 Jan 2024 00:00:00 +0000 Subject: [PATCH 07/80] Add a note about math/tan return values --- .../2.0/Khronos/KHR_interactivity/Specification.adoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 01fefba341..57a9cc2512 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -688,6 +688,13 @@ If any input value is _NaN_, the output value is also _NaN_. \end{cases}] |=== +[NOTE] +.Note +==== +Since stem:[a] cannot exactly represent latexmath:[\pm\frac{\pi}{2}], this function does not return infinity. +The closest representable argument values would likely produce latexmath:[\pm16331239353195370]. +==== + ===== Arcsine [cols="1h,1,2"] From 866ea7f86bab716f2d0869c8285ecddcdbd054e4 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 26 Feb 2024 00:00:00 +0000 Subject: [PATCH 08/80] Add flow nodes --- .../KHR_interactivity/Specification.adoc | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 57a9cc2512..15ff4042c0 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1523,3 +1523,239 @@ a ? (31 - Math.clz32(a & -a)) : 32; |=== If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation returns 32. + +=== Control Flow Nodes + +==== Sync Nodes + +===== Sequence + +[cols="1h,1,2"] +|=== +| Type | `flow/sequence` | Sequentially activate all connected output flows +| Input flow sockets +| `in` | The entry flow into this node +| Output flow sockets +| `` | The `i`-th output flow, `i` is a non-negative integer decimal number less than or equal to 2147483647. +|=== + +This node has no internal state. + +When the `in` input flow is activated: + +1. Let a temporary variable `index` be -1. +2. Let a temporary variable `maxIndex` be the maximum output flow name value. +3. If `index` is less than `maxIndex`: +.. increment `index` by 1; +.. activate the output flow named `` if it exists and is connected; +.. goto step 3. + +===== Branch + +[cols="1h,1,2"] +|=== +| Type | `flow/branch` | Branch the execution flow based on a condition +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `bool condition` | Value selecting the branch taken +.2+| Output flow sockets +| `true` | The flow to be activated if the `condition` input value is true +| `false` | The flow to be activated if the `condition` input value is false +|=== + +This node has no internal state. + +The `condition` input value is evaluated each time the node is executed. + +===== Switch + +[cols="1h,1,2"] +|=== +| Type | `flow/switch` | Conditionally route the execution flow to one of the outputs +| Configuration +| `int[] cases` | The cases on which to perform the switch on; values **MUST** be unique integers +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `int selection` | The value on which the switch operates +.2+| Output flow sockets +| `` | The `i`-th output flow, `i` is an integer decimal number; it **MUST** be present in the `cases` configuration +| `default` | The output flow used when the `selection` input value is not present in the `cases` configuration +|=== + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate the `selection` input value. +2. If the `cases` configuration array does not contain the `selection` input value: +.. activate the `default` output flow if it is connected. +3. If the `cases` configuration array contains the `selection` input value: +.. activate the output flow named `` if it is connected. + +===== While Loop + +[cols="1h,1,2"] +|=== +| Type | `flow/while` | Repeatedly activate the output flow based on a condition +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `bool condition` | Loop condition +.2+| Output flow sockets +| `loopBody` | The flow to be activated while the `condition` input value is true +| `completed` | The flow to be activated once the `condition` input value is false +|=== + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate the `condition`. The `condition` **MUST NOT** statically evaluate to true, otherwise the node is invalid. +2. If the `condition` is true, +.. activate the `loopBody` output flow; +.. after completion of the `loopBody` output flow, goto step 1. +3. If the `condition` is false, +.. activate the `completed` output flow. + +===== For Loop + +[cols="1h,1,2"] +|=== +| Type | `flow/for` | Repeatedly activate the output flow based on an incrementing index value +| Configuration +| `int initialIndex` | The index value before the loop starts +| Input flow sockets +| `in` | The entry flow into this node +.2+| Input value sockets +| `int startIndex` | The start index of the loop +| `int endIndex` | The end index of the loop +.2+| Output flow sockets +| `loopBody` | The flow to be activated if the `index` value is less than the `endIndex` input value +| `completed` | The flow to be activated if the `index` value is greater than or equal to the `endIndex` input value +| Output value sockets +| `int index` | The current index value if the node has ever been activated, `initialIndex` otherwise +|=== + +The internal state of this node consists of one 32-bit signed integer value `index` initialized to `initialIndex`. + +When the `in` input flow is activated: + +1. Evaluate the `startIndex` input value. +2. Set `index` to `startIndex`. +3. Evaluate the `endIndex` input value. +4. If `index` is less than the `endIndex` input value, +.. activate the `loopBody` output flow; +.. after completion of the `loopBody` output flow, increment the `index` value by 1; +.. goto step 3. +5. If the `index` value is greater than or equal to the `endIndex` input value, +.. activate the `completed` output flow. + +===== Do N + +[cols="1h,1,2"] +|=== +| Type | `flow/doN` | Activate the output flow no more than N times +.2+| Input flow sockets +| `in` | The entry flow into this node +| `reset` | When this flow is activated, the `currentCount` value is reset to 0 +| Input value sockets +| `int n` | Maximum number of times the `out` output flow is activated +| Output flow sockets +| `out` | The flow to be activated if the `currentCount` value is less than the `n` input value +| Output value sockets +| `int currentCount` | The current execution count +|=== + +The internal state of this node consists of one 32-bit signed integer value `currentCount` initialized to 0. + +When the `reset` input flow is activated: + +1. Reset `currentCount` to 0. + +When the `in` input flow is activated: + +1. Evaluate the `n` input value. +2. If `currentCount` is less than `n`, +.. increment `currentCount` by 1; +.. activate the `out` output flow. + +===== Multi Gate + +[cols="1h,1,2"] +|=== +| Type | `flow/multiGate` | Route the execution flow to one of the outputs sequentially or randomly +.3+| Configuration +| `int outputFlows` | The number of output flows, **MUST** be greater than 0 +| `bool isRandom` | If set to true, output flows are executed in random order, picking a random not used output flow each time until all are done +| `bool loop` | If set to true, the outputs will repeat in a loop continuously after all are done +.2+| Input flow sockets +| `in` | The entry flow into this node +| `reset` | When this flow is activated, the `lastIndex` value is reset to -1 and all outputs are marked as not used +| Output flow sockets +| `` | The `i`-th output flow, `i` is a non-negative integer decimal number less than the `outputFlows` configuration value +| Output value sockets +| `int lastIndex` | The index of the last used output; `-1` if the node has not been activated +|=== + +The internal state of this node consists of one 32-bit signed integer value `lastIndex` initialized to -1 and a map of booleans with all values initialized to false representing used output flows. The size of the boolean map is equal to the number of connected output flows, i.e., unconnected output flows are ignored. + +When the `reset` input flow is activated: + +1. Reset the `lastIndex` value to -1. +2. Mark all connected output flows as not used in the boolean map. + +When the `in` input flow is activated: + +1. If the `isRandom` input value is false, +.. let `i` be the smallest connected not used output flow index according to the boolean map or -1 if all flows are marked as used. +2. If the `isRandom` input value is true, +.. let `i` be a random not used connected output flow index according to the boolean map or -1 if all flows are marked as used. +3. If `i` is greater than -1, +.. mark the `i`-th output as used in the boolean map; +.. set the `lastIndex` value to `i`; +.. activate the output flow `i`. +4. If `i` is equal to -1 and the `loop` input value is true, +.. mark all connected output flows as not used in the boolean map; +.. if the `isRandom` input value is false, +... set `i` to the smallest connected output flow index; +.. if the `isRandom` input value is true, +... set `i` be a random connected output flow index; +.. mark the `i`-th output flow as used in the boolean map; +.. set the `lastIndex` value to `i`; +.. activate the output flow `i`. + +===== Wait All + +[cols="1h,1,2"] +|=== +| Type | `flow/waitAll` | Activate the output flow when all input flows have been activated at least once. +| Configuration +| `int inputFlows` | The number of input flows, **MUST** be greater than 0 +.2+| Input flow sockets +| `` | The `i`-th input flow, `i` is a non-negative integer decimal number less than the `inputFlows` configuration value +| `reset` | When this flow is activated, all input flows are marked as unused +.2+| Output flow sockets +| `out` | The flow to be activated after every input flow activation except the last missing input +| `completed` | The flow to be activated when the last missing input flow is activated +| Output value sockets +| `int remainingInputs` | The number of not yet activated input flows +|=== + +The internal state of this node consists of one 32-bit signed integer value `remainingInputs` initialized to the number of connected input flows not including the `reset` input flow and a map of booleans with all values initialized to false representing used input flow sockets. The size of the boolean map is equal to the initial `remainingInputs` value. + +When the `reset` input flow is activated: + +1. Reset `remainingInputs` to the number of connected input flows not including the `reset` input flow. +2. Mark all connected input flows as not used in the boolean map. + +When any of the `` input flows is activated: + +1. If the ``-th input flow is not marked as used in the boolean map: +.. mark the ``-th input flow as used in the boolean map. +.. decrement the `remainingInputs` value by 1; +2. If the `remainingInputs` value is zero: +.. activate the `completed` output flow. +3. If the `remainingInputs` value is not zero: +.. activate the `out` output flow. From f69bfb1391f7f528a81b06c51dea698ad5577de6 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:00:00 +0000 Subject: [PATCH 09/80] Address feedback --- .../KHR_interactivity/Specification.adoc | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 15ff4042c0..081887776b 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1536,19 +1536,12 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu | Input flow sockets | `in` | The entry flow into this node | Output flow sockets -| `` | The `i`-th output flow, `i` is a non-negative integer decimal number less than or equal to 2147483647. +| `` | One or more output flows; their names are purely informative |=== This node has no internal state. -When the `in` input flow is activated: - -1. Let a temporary variable `index` be -1. -2. Let a temporary variable `maxIndex` be the maximum output flow name value. -3. If `index` is less than `maxIndex`: -.. increment `index` by 1; -.. activate the output flow named `` if it exists and is connected; -.. goto step 3. +When the `in` input flow is activated, all output flows are activated one by one in the order they are defined in JSON. ===== Branch @@ -1574,16 +1567,20 @@ The `condition` input value is evaluated each time the node is executed. |=== | Type | `flow/switch` | Conditionally route the execution flow to one of the outputs | Configuration -| `int[] cases` | The cases on which to perform the switch on; values **MUST** be unique integers +| `int[] cases` | The cases on which to perform the switch on; values **MUST** be unique 32-bit signed integers; at least one value **MUST** be present | Input flow sockets | `in` | The entry flow into this node | Input value sockets | `int selection` | The value on which the switch operates .2+| Output flow sockets -| `` | The `i`-th output flow, `i` is an integer decimal number; it **MUST** be present in the `cases` configuration -| `default` | The output flow used when the `selection` input value is not present in the `cases` configuration +| `` | The output flow, `case` is an integer decimal number present in the `cases` configuration array +| `default` | The output flow used when the `selection` input value is not present in the `cases` configuration array |=== +The node has one or more `` output flow sockets named as decimal integers equal to the elements of the `cases` configuration array. Encoded as JSON strings, these output flow socket names **MUST** contain only decimal integers (ASCII characters `0x30 ... 0x39`) and optionally a leading minus sign (ASCII character `0x2D`); other characters and leading zeros are not allowed. + +For example, if the `cases` configuration array is `[-1, 0, 1]`, the output socket names are exactly `"-1"`, `"0"`, and `"1"`. + This node has no internal state. When the `in` input flow is activated: @@ -1686,45 +1683,46 @@ When the `in` input flow is activated: [cols="1h,1,2"] |=== | Type | `flow/multiGate` | Route the execution flow to one of the outputs sequentially or randomly -.3+| Configuration -| `int outputFlows` | The number of output flows, **MUST** be greater than 0 +.2+| Configuration | `bool isRandom` | If set to true, output flows are executed in random order, picking a random not used output flow each time until all are done | `bool loop` | If set to true, the outputs will repeat in a loop continuously after all are done .2+| Input flow sockets | `in` | The entry flow into this node | `reset` | When this flow is activated, the `lastIndex` value is reset to -1 and all outputs are marked as not used | Output flow sockets -| `` | The `i`-th output flow, `i` is a non-negative integer decimal number less than the `outputFlows` configuration value +| `` | One or more output flows; their names are purely informative | Output value sockets | `int lastIndex` | The index of the last used output; `-1` if the node has not been activated |=== -The internal state of this node consists of one 32-bit signed integer value `lastIndex` initialized to -1 and a map of booleans with all values initialized to false representing used output flows. The size of the boolean map is equal to the number of connected output flows, i.e., unconnected output flows are ignored. +The internal state of this node consists of one 32-bit signed integer value `lastIndex` initialized to -1 and an array of booleans with all values initialized to false representing used output flows. The size of the boolean array is equal to the number of output flows. + +The output flows are ordered as they are defined in JSON. When the `reset` input flow is activated: 1. Reset the `lastIndex` value to -1. -2. Mark all connected output flows as not used in the boolean map. +2. Mark all output flows as not used in the boolean array. When the `in` input flow is activated: -1. If the `isRandom` input value is false, -.. let `i` be the smallest connected not used output flow index according to the boolean map or -1 if all flows are marked as used. -2. If the `isRandom` input value is true, -.. let `i` be a random not used connected output flow index according to the boolean map or -1 if all flows are marked as used. +1. If the `isRandom` configuration value is false, +.. let `i` be the smallest not used output flow index according to the boolean array or -1 if all output flows are marked as used. +2. If the `isRandom` configuration value is true, +.. let `i` be a random not used output flow index according to the boolean array or -1 if all output flows are marked as used. 3. If `i` is greater than -1, -.. mark the `i`-th output as used in the boolean map; +.. mark the output flow with index `i` as used in the boolean array; .. set the `lastIndex` value to `i`; -.. activate the output flow `i`. -4. If `i` is equal to -1 and the `loop` input value is true, -.. mark all connected output flows as not used in the boolean map; -.. if the `isRandom` input value is false, -... set `i` to the smallest connected output flow index; -.. if the `isRandom` input value is true, -... set `i` be a random connected output flow index; -.. mark the `i`-th output flow as used in the boolean map; +.. activate the output flow with index `i`. +4. If `i` is equal to -1 and the `loop` configuration value is true, +.. mark all output flows as not used in the boolean array; +.. if the `isRandom` configuration value is false, +... set `i` to 0; +.. if the `isRandom` configuration value is true, +... set `i` to a random output flow index; +.. mark the output flow with index `i` as used in the boolean array; .. set the `lastIndex` value to `i`; -.. activate the output flow `i`. +.. activate the output flow with index `i`. ===== Wait All @@ -1732,7 +1730,7 @@ When the `in` input flow is activated: |=== | Type | `flow/waitAll` | Activate the output flow when all input flows have been activated at least once. | Configuration -| `int inputFlows` | The number of input flows, **MUST** be greater than 0 +| `int inputFlows` | The number of input flows, **MUST** be greater than 0 and less than or equal to 2147483647 .2+| Input flow sockets | `` | The `i`-th input flow, `i` is a non-negative integer decimal number less than the `inputFlows` configuration value | `reset` | When this flow is activated, all input flows are marked as unused @@ -1743,6 +1741,10 @@ When the `in` input flow is activated: | `int remainingInputs` | The number of not yet activated input flows |=== +The node has one or more input flow sockets named as sequential non-negative decimal integers depending on the `inputFlows` configuration value. Encoded as JSON strings, these input flow socket names contain only decimal integers (ASCII characters `0x30 ... 0x39`); other characters and leading zeros are not allowed. + +For example, if `inputFlows` is 3, the input socket names are `"0"`, `"1"`, and `"2"` exactly. + The internal state of this node consists of one 32-bit signed integer value `remainingInputs` initialized to the number of connected input flows not including the `reset` input flow and a map of booleans with all values initialized to false representing used input flow sockets. The size of the boolean map is equal to the initial `remainingInputs` value. When the `reset` input flow is activated: From 29e8accb02bc75bc19bca9bc81cbac002d772b25 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:00:00 +0000 Subject: [PATCH 10/80] Add flow/throttle --- .../KHR_interactivity/Specification.adoc | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 081887776b..6ca8b3fcb1 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1761,3 +1761,44 @@ When any of the `` input flows is activated: .. activate the `completed` output flow. 3. If the `remainingInputs` value is not zero: .. activate the `out` output flow. + +===== Throttle + +[cols="1h,1,2"] +|=== +| Type | `flow/throttle` | Activate the output flow unless it has been activated less than a certain time ago +.2+| Input flow sockets +| `in` | The entry flow into this node +| `reset` | When this flow is activated, the output flow throttling state is reset +| Input value sockets +| `float duration` | The time, in seconds, to wait after an output flow activation before allowing subsequent output flow activations +.2+| Output flow sockets +| `out` | The flow to be activated if the output flow is not currently throttled +| `err` | The flow to be activated if the `duration` input value is negative, infinite, or NaN +| Output value sockets +| `float lastRemainingTime` | The remaining throttling time, in seconds, at the moment of the last valid activation of the input flow or NaN if the input flow has never been activated with a valid `duration` input value +|=== + +The internal state of this node consists of an uninitialized _timestamp_ value of an implementation-defined high-precision type and a floating-point `lastRemainingTime` value initialized to NaN. + +When the `reset` input flow is activated: + +1. Reset the `lastRemainingTime` value to NaN. + +When the `in` input flow is activated: + +1. Evaluate the `duration` input value. +2. If the `duration` input value is NaN, infinite, or negative, +.. activate the `err` output flow and skip the next steps. +3. If the `lastRemainingTime` value is not NaN: +.. Let `elapsed` be a non-negative difference, in seconds, between the _timestamp_ and the current time. +.. If the `duration` input value is less than or equal to the `elapsed` value, +... set the _timestamp_ value to the current time; +... set the `lastRemainingTime` value to zero; +... activate the `out` output flow. +.. If the `duration` input value is greater than the `elapsed` value, +... set the `lastRemainingTime` value to the positive difference, in seconds, between the `duration` and `elapsed` values. +4. If the `lastRemainingTime` value is NaN, +.. set the _timestamp_ value to the current time; +.. set the `lastRemainingTime` value to zero; +.. activate the `out` output flow. From 6ebb25f15e5cce60047f119b950196e8a8f1dbdd Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:00:00 +0000 Subject: [PATCH 11/80] Add flow/delay --- .../KHR_interactivity/Specification.adoc | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 6ca8b3fcb1..26a8b981de 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1779,7 +1779,7 @@ When any of the `` input flows is activated: | `float lastRemainingTime` | The remaining throttling time, in seconds, at the moment of the last valid activation of the input flow or NaN if the input flow has never been activated with a valid `duration` input value |=== -The internal state of this node consists of an uninitialized _timestamp_ value of an implementation-defined high-precision type and a floating-point `lastRemainingTime` value initialized to NaN. +The internal state of this node consists of an uninitialized _timestamp_ value of an implementation-defined high-precision time type and a floating-point `lastRemainingTime` value initialized to NaN. When the `reset` input flow is activated: @@ -1802,3 +1802,33 @@ When the `in` input flow is activated: .. set the _timestamp_ value to the current time; .. set the `lastRemainingTime` value to zero; .. activate the `out` output flow. + +==== Async Nodes + +===== Delay + +[cols="1h,1,2"] +|=== +| Type | `flow/delay` | Schedule the output flow activation after a certain delay +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `float duration` | The duration, in seconds, to delay +.3+| Output flow sockets +| `out` | The flow to be activated if the `duration` value is valid +| `err` | The flow to be activated if the `duration` value is invalid +| `completed` | The flow to be activated after the delay +|=== + +The internal state of this node consists of an _activationTime_ value of an implementation-defined high-precision time type initialized to epoch. + +When the `in` input flow is activated: + +1. Evaluate the `duration` input value. +2. If the `duration` input value is NaN, infinite, or negative, +.. activate the `err` output flow and skip the next steps. +3. Let _currentTime_ be an implementation-defined high-precision time value representing the current time. +4. If the _activationTime_ value is less than the _currentTime_ value, +.. set the _activationTime_ value to the sum of the _currentTime_ value and the `duration` input value converted to the same implementation-specific high-precision time type; +.. schedule the `completed` output flow activation at the _activationTime_ time. +5. Activate the `out` output flow. From c63a37fda2d75e224e8291eaf03f2d0732f2dad4 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:00:00 +0000 Subject: [PATCH 12/80] Address feedback --- .../Khronos/KHR_interactivity/Specification.adoc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 26a8b981de..48d91b6f73 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1685,7 +1685,7 @@ When the `in` input flow is activated: | Type | `flow/multiGate` | Route the execution flow to one of the outputs sequentially or randomly .2+| Configuration | `bool isRandom` | If set to true, output flows are executed in random order, picking a random not used output flow each time until all are done -| `bool loop` | If set to true, the outputs will repeat in a loop continuously after all are done +| `bool isLoop` | If set to true, the outputs will repeat in a loop continuously after all are done .2+| Input flow sockets | `in` | The entry flow into this node | `reset` | When this flow is activated, the `lastIndex` value is reset to -1 and all outputs are marked as not used @@ -1714,7 +1714,7 @@ When the `in` input flow is activated: .. mark the output flow with index `i` as used in the boolean array; .. set the `lastIndex` value to `i`; .. activate the output flow with index `i`. -4. If `i` is equal to -1 and the `loop` configuration value is true, +4. If `i` is equal to -1 and the `isLoop` configuration value is true, .. mark all output flows as not used in the boolean array; .. if the `isRandom` configuration value is false, ... set `i` to 0; @@ -1813,22 +1813,20 @@ When the `in` input flow is activated: | Input flow sockets | `in` | The entry flow into this node | Input value sockets -| `float duration` | The duration, in seconds, to delay +| `float duration` | The duration, in seconds, to delay the `completed` output flow activation .3+| Output flow sockets | `out` | The flow to be activated if the `duration` value is valid | `err` | The flow to be activated if the `duration` value is invalid | `completed` | The flow to be activated after the delay |=== -The internal state of this node consists of an _activationTime_ value of an implementation-defined high-precision time type initialized to epoch. +This node has no internal state. When the `in` input flow is activated: 1. Evaluate the `duration` input value. 2. If the `duration` input value is NaN, infinite, or negative, .. activate the `err` output flow and skip the next steps. -3. Let _currentTime_ be an implementation-defined high-precision time value representing the current time. -4. If the _activationTime_ value is less than the _currentTime_ value, -.. set the _activationTime_ value to the sum of the _currentTime_ value and the `duration` input value converted to the same implementation-specific high-precision time type; -.. schedule the `completed` output flow activation at the _activationTime_ time. +3. Let _activationTime_ be an implementation-defined high-precision time value equal to the sum of the current time value and the `duration` input value converted to the same time type. +4. Schedule the `completed` output flow activation at the _activationTime_ time. 5. Activate the `out` output flow. From 48851d2fed3a43dad3286011312df28e492eae8a Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:00:00 +0000 Subject: [PATCH 13/80] Mention flow/delay activations limit --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 48d91b6f73..f75f35de00 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1830,3 +1830,5 @@ When the `in` input flow is activated: 3. Let _activationTime_ be an implementation-defined high-precision time value equal to the sum of the current time value and the `duration` input value converted to the same time type. 4. Schedule the `completed` output flow activation at the _activationTime_ time. 5. Activate the `out` output flow. + +The graph execution **MAY** be terminated when the amount of pending activations exceeds an implementation-defined value. From 86bf6938fc5b4e51ebe77bc30686cd790436f298 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:00:00 +0000 Subject: [PATCH 14/80] Add pointer/get and pointer/set --- .../KHR_interactivity/Specification.adoc | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index f75f35de00..979b73ef88 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1832,3 +1832,83 @@ When the `in` input flow is activated: 5. Activate the `out` output flow. The graph execution **MAY** be terminated when the amount of pending activations exceeds an implementation-defined value. + +=== State Manipulation Nodes + +==== Object Model Access + +===== Pointer Get + +[cols="1h,1,2"] +|=== +| Type | `pointer/get` | Get an object model property value +| Configuration +| `string pointer` | The JSON pointer or JSON pointer template +| Input value sockets +| `int ` | The JSON pointer template path segment to be substituted at runtime +.2+| Output value sockets +| `T value` | The resolved property value +| `bool isValid` | True if the property value can be resolved, false otherwise +|=== + +This node gets a glTF Asset Object Model value using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. + +[NOTE] +.Examples +==== +The `pointer` configuration value `"/nodes/0/scale"` is valid; its output value socket is of `float3` type. + +The `pointer` configuration value `"/myProperty"` is invalid because this path is not defined. +==== + +The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. + +[NOTE] +.Example +==== +If the `pointer` configuration value is `"/nodes/{myId}/scale"`, the node has the `myId` input value socket, which value denotes the node index. +==== + +This node has no internal state. + +If the pointer or the pointer template with all its substitutions applied can be resolved, the `value` output value is the resolved property value and the `isValid` output value is true. + +If the pointer or the pointer template with all its substitutions applied cannot be resolved, the `value` output value is the default value for its type and the `isValid` output value is false. + +[NOTE] +.Note +==== +Unresolvable pointers include those with negative or out-of-bounds array indices and/or non-existent JSON objects. +==== + +===== Pointer Set + +[cols="1h,1,2"] +|=== +| Type | `pointer/set` | Set an object model property value +| Configuration +| `string pointer` | The JSON pointer or JSON pointer template +| Input flow sockets +| `in` | The entry flow into this node +.2+| Input value sockets +| `int ` | The JSON pointer template path segment to be substituted at runtime +| `T value` | The new property value +.2+| Output flow sockets +| `out` | The flow to be activated if the JSON pointer can be resolved +| `err` | The flow to be activated if the JSON pointer cannot be resolved +|=== + +This node sets a glTF Asset Object Model value using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. + +The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. If the pointer or the pointer template with all its substitutions applied can be resolved, +.. set the resolved property to the `value` input value; +.. activate the `out` output flow. +3. If the pointer or the pointer template with all its substitutions applied cannot be resolved, +.. activate the `err` output flow. From 71860b812a57ce38b9957fca45176c528ca9433a Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:00:00 +0000 Subject: [PATCH 15/80] Add variable/get and variable/set --- .../KHR_interactivity/Specification.adoc | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 979b73ef88..b860b0528d 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1835,6 +1835,48 @@ The graph execution **MAY** be terminated when the amount of pending activations === State Manipulation Nodes +==== Custom Variable Access + +===== Variable Get + +[cols="1h,1,2"] +|=== +| Type | `variable/get` | Get a custom variable value +| Configuration +| `int variable` | The custom variable index +| Output value sockets +| `T value` | The custom variable value +|=== + +This node gets a custom variable value using the variable index provided by the `variable` configuration value. The type `T` is determined by the referenced variable. The variable index **MUST** be non-negative and less than the total number of custom variables, otherwise the node is invalid. + +This node has no internal state. + +===== Variable Set + +[cols="1h,1,2"] +|=== +| Type | `variable/set` | Set a custom variable value +| Configuration +| `int variable` | The custom variable index +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `T value` | The new variable value +| Output flow sockets +| `out` | The flow to be activated after the value is set +|=== + +This node sets a custom variable value using the variable index provided by the `variable` configuration value and the `value` input value. The type `T` is determined by the referenced variable. The variable index **MUST** be non-negative and less than the total number of custom variables, otherwise the node is invalid. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate the `value` input value. +2. Set the custom variable with the `variable` configuration value index to the `value` input value. +3. Activate the `out` output flow. + ==== Object Model Access ===== Pointer Get From ffd99ef31a97166e939387f94a5dd4b458a5f94c Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 25 Mar 2024 00:00:00 +0000 Subject: [PATCH 16/80] Adjust flow/throttle error condition --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index b860b0528d..e574d79207 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1788,7 +1788,7 @@ When the `reset` input flow is activated: When the `in` input flow is activated: 1. Evaluate the `duration` input value. -2. If the `duration` input value is NaN, infinite, or negative, +2. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type used for the internal _timestamp_ value, .. activate the `err` output flow and skip the next steps. 3. If the `lastRemainingTime` value is not NaN: .. Let `elapsed` be a non-negative difference, in seconds, between the _timestamp_ and the current time. From 3260039fc8ff93b843fd5e2eb2d99d124d1f4338 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 25 Mar 2024 00:00:00 +0000 Subject: [PATCH 17/80] Update delay nodes --- .../KHR_interactivity/Specification.adoc | 64 +++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index e574d79207..8fb57d3f93 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1803,35 +1803,77 @@ When the `in` input flow is activated: .. set the `lastRemainingTime` value to zero; .. activate the `out` output flow. -==== Async Nodes +==== Delay Nodes -===== Delay +===== Set Delay [cols="1h,1,2"] |=== -| Type | `flow/delay` | Schedule the output flow activation after a certain delay -| Input flow sockets -| `in` | The entry flow into this node +| Type | `flow/setDelay` | Schedule the output flow activation after a certain delay +.2+| Input flow sockets +| `in` | The entry flow into this node +| `cancel` | When this flow is activated, all delayed activations scheduled by this node are cancelled | Input value sockets | `float duration` | The duration, in seconds, to delay the `completed` output flow activation +| Output value sockets +| `int lastDelayIndex` | The delay index assigned during the last successful node execution .3+| Output flow sockets | `out` | The flow to be activated if the `duration` value is valid | `err` | The flow to be activated if the `duration` value is invalid | `completed` | The flow to be activated after the delay |=== -This node has no internal state. +The internal state of this node consists of an integer `lastDelayIndex` value initialized to -1 and a dynamic array of activation indices scheduled by the node. This array is initially empty and its maximum size is implementation-specific. + +The internal state of an execution graph having one or more `flow/setDelay` nodes includes a dynamic array of activation indices scheduled from all such nodes. This array is initially empty and its maximum size is implementation-specific. When the `in` input flow is activated: 1. Evaluate the `duration` input value. -2. If the `duration` input value is NaN, infinite, or negative, +2. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type, .. activate the `err` output flow and skip the next steps. -3. Let _activationTime_ be an implementation-defined high-precision time value equal to the sum of the current time value and the `duration` input value converted to the same time type. -4. Schedule the `completed` output flow activation at the _activationTime_ time. -5. Activate the `out` output flow. +3. If scheduling a new activation exceeds any implementation-specific limit, +.. activate the `err` output flow and skip the next steps. +4. Let _activationTime_ be an implementation-defined high-precision time value equal to the sum of the current time value and the `duration` input value converted to the same time type. +5. If _activationTime_ is not valid according to implementation-specific validation rules, e.g., it exceeds an internal threshold value, +.. activate the `err` output flow and skip the next steps. +6. Set `lastDelayIndex` to a positive value representing the delayed flow activation being scheduled. This value **MUST** be unique across all previous activations of all `flow/setDelay` nodes of the graph. +7. Push the value of `lastDelayIndex` to the graph and node arrays of activation indices. +8. Schedule the following actions at the _activationTime_ time: +.. Removal of the activation index value from both arrays of activation indices. +.. Activation of the `completed` output flow. +9. Activate the `out` output flow. + +When the `cancel` input flow is activated: + +1. Set the `lastDelayIndex` value to -1. +2. For each activation index value in the node's array of activation indices: +.. Remove this activation index value from the node's and the graph's arrays of activation indices. +.. Cancel the corresponding scheduled activation. + +===== Cancel Delay + +[cols="1h,1,2"] +|=== +| Type | `flow/cancelDelay` | Cancel a previously scheduled output flow activation +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `int delayIndex` | The index value of the scheduled activation to be cancelled +| Output flow sockets +| `out` | The flow to be activated after executing this node +|=== + +This node has no internal state but its execution **MAY** affect internal states of other nodes and the graph. + +When the `in` input flow is activated: + +1. Evaluate the `delayIndex` input value. +2. Remove this activation index value from all arrays of activation indices if it exists. +3. Cancel the corresponding scheduled activation if it exists. +4. Activate the `out` output flow. -The graph execution **MAY** be terminated when the amount of pending activations exceeds an implementation-defined value. +Non-positive or not existing delay index values **MUST NOT** cause any runtime errors. === State Manipulation Nodes From 18e12ccef0b2eed7e80f2fb7db8550511cb9f391 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:00:00 +0000 Subject: [PATCH 18/80] Clarify delay limits --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 8fb57d3f93..bcd2cb47d3 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1827,6 +1827,8 @@ The internal state of this node consists of an integer `lastDelayIndex` value in The internal state of an execution graph having one or more `flow/setDelay` nodes includes a dynamic array of activation indices scheduled from all such nodes. This array is initially empty and its maximum size is implementation-specific. +Implementations **MUST** be aware of their effective limit on the maximum supported `duration` input value to avoid any implicit behavior changes, e.g., due to numeric overflows; exceeding such value **MUST** lead to the `err` output flow activation as described below. + When the `in` input flow is activated: 1. Evaluate the `duration` input value. From e73bc71c91a82b994b46b3c140e6b9ed0a6be33b Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 8 Apr 2024 00:00:00 +0000 Subject: [PATCH 19/80] Clarify invalid values for pointer/set --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index bcd2cb47d3..457177e25d 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1988,6 +1988,8 @@ This node sets a glTF Asset Object Model value using the provided JSON pointer. The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. +If the `value` input value is not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. + This node has no internal state. When the `in` input flow is activated: From 38e690fd69b02e7b1d5f74c4cd001c64192b9a39 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 8 Apr 2024 00:00:00 +0000 Subject: [PATCH 20/80] Add boolean arithmetic nodes --- .../KHR_interactivity/Specification.adoc | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 457177e25d..dcf1d1c8ce 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1524,6 +1524,55 @@ a ? (31 - Math.clz32(a & -a)) : 32; If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation returns 32. +==== Boolean Arithmetic Nodes + +===== Equality + +[cols="1h,1,2"] +|=== +| Type | `math/eq` | Equality operation +.2+| Input value sockets +| `bool a` | First argument +| `bool b` | Second argument +| Output value sockets +| `bool value` | True if and only if both stem:[a] and stem:[b] have the same value; false otherwise +|=== + +===== Boolean NOT + +[cols="1h,1,2"] +|=== +| Type | `math/not` | Boolean NOT operation +| Input value sockets +| `bool a` | Argument +| Output value sockets +| `bool value` | True if stem:[a] is false; false if stem:[a] is true +|=== + +===== Boolean AND + +[cols="1h,1,2"] +|=== +| Type | `math/and` | Boolean AND operation +.2+| Input value sockets +| `bool a` | First argument +| `bool b` | Second argument +| Output value sockets +| `bool value` | True if and only if both stem:[a] and stem:[b] are true; false otherwise +|=== + +===== Boolean OR + +[cols="1h,1,2"] +|=== +| Type | `math/or` | Boolean OR operation +.2+| Input value sockets +| `bool a` | First argument +| `bool b` | Second argument +| Output value sockets +| `bool value` | False if and only if both stem:[a] and stem:[b] are false; true otherwise +|=== + === Control Flow Nodes ==== Sync Nodes From 8fbc8667760ad2afa81f75bf644b738886c231ce Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 8 Apr 2024 00:00:00 +0000 Subject: [PATCH 21/80] Add type conversion nodes --- .../KHR_interactivity/Specification.adoc | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index dcf1d1c8ce..f970da937d 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1573,6 +1573,145 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu | `bool value` | False if and only if both stem:[a] and stem:[b] are false; true otherwise |=== +=== Type Conversion Nodes + +==== Boolean Conversion Nodes + +===== Boolean to Integer + +[cols="1h,1,2"] +|=== +| Type | `type/boolToInt` | Boolean to integer conversion +| Input value sockets +| `bool a` | Argument +| Output value sockets +| `int value` | stem:[1] if stem:[a] is true; stem:[0] otherwise +|=== + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +a|0 +---- +==== + +===== Boolean to Float + +[cols="1h,1,2"] +|=== +| Type | `type/boolToFloat` | Boolean to float conversion +| Input value sockets +| `bool a` | Argument +| Output value sockets +| `float value` | stem:[1] if stem:[a] is true; stem:[0] otherwise +|=== + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- ++a +---- +==== + +==== Integer Conversion Nodes + +===== Integer to Boolean + +[cols="1h,1,2"] +|=== +| Type | `type/intToBool` | Integer to boolean conversion +| Input value sockets +| `int a` | Argument +| Output value sockets +| `bool value` | True if stem:[a] is not equal to zero; false otherwise +|=== + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +!!a +---- +==== + +===== Integer to Float + +[cols="1h,1,2"] +|=== +| Type | `type/intToFloat` | Integer to float conversion +| Input value sockets +| `int a` | Argument +| Output value sockets +| `float value` | Floating-point value equal to stem:[a] +|=== + +Since floating-point values have double precision, this conversion **MUST** be lossless. + +This operation **MUST NOT** produce negative zero. + +[NOTE] +.Note +==== +This operation is no-op in ECMAScript. +==== + +==== Float Conversion Nodes + +===== Float to Boolean + +[cols="1h,1,2"] +|=== +| Type | `type/floatToBool` | Float to boolean conversion +| Input value sockets +| `float a` | Argument +| Output value sockets +| `bool value` | False if stem:[a] is NaN or equal to zero; true otherwise +|=== + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +!!a +---- +==== + +===== Float to Integer + +[cols="1h,1,2"] +|=== +| Type | `type/floatToInt` | Float to integer conversion +| Input value sockets +| `float a` | Argument +| Output value sockets +| `int value` | Integer value produced as described below +|=== + +1. If the stem:[a] input value is zero, infinite, or NaN, return zero and skip the next steps. +2. Let stem:[t] be stem:[a] with its fractional part removed by rounding towards zero. +3. Let stem:[k] be a value of the same sign as stem:[t] such that its absolute value is less than stem:[2^32] and stem:[k] is equal to stem:[t - q * 2^32] for some integer stem:[q]. +4. If stem:[k] is greater than or equal to stem:[2^31], return stem:[k - 2^32]; otherwise return stem:[k]. + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +a|0 +---- +==== + === Control Flow Nodes ==== Sync Nodes From b681b0a4963b8f7418057cd0d2904f6bddec40d8 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 8 Apr 2024 00:00:00 +0000 Subject: [PATCH 22/80] Add math/select node --- .../KHR_interactivity/Specification.adoc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index f970da937d..c8cede8c2f 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -592,7 +592,7 @@ If any input value is _NaN_, the output value is false. | `bool value` | True if stem:[a >= b]; false otherwise |=== -==== Special Floating-Point Nodes +==== Special Nodes ===== Is Not a Number @@ -616,6 +616,21 @@ If any input value is _NaN_, the output value is false. | `bool value` | True if stem:[a] is positive or negative infinity; false otherwise |=== +===== Select + +[cols="1h,1,2"] +|=== +| Type | `math/select` | Conditional selection operation +.3+| Input value sockets +| `bool condition` | Value selecting the value returned +| `T a` | Positive selection option +| `T b` | Negative selection option +| Output value sockets +| `T value` | stem:[a] if the the `condition` input value is true; stem:[b] otherwise +|=== + +The type `T` represents any type. It **MUST** be the same for the output value socket and the input value sockets stem:[a] and stem:[b], otherwise the node is invalid. + ==== Angle and Trigonometry Nodes Node parameters specified as angle are assumed to be in units of radians. From 9e000b926e3f8ecd4b6f5f96f63f1bc1aa916ee4 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 15 Apr 2024 00:00:00 +0000 Subject: [PATCH 23/80] Add boolean XOR --- .../2.0/Khronos/KHR_interactivity/Specification.adoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index c8cede8c2f..37961dee36 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1588,6 +1588,18 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu | `bool value` | False if and only if both stem:[a] and stem:[b] are false; true otherwise |=== +===== Boolean XOR + +[cols="1h,1,2"] +|=== +| Type | `math/xor` | Boolean XOR operation +.2+| Input value sockets +| `bool a` | First argument +| `bool b` | Second argument +| Output value sockets +| `bool value` | True if and only if stem:[a] is not equal to stem:[b]; false otherwise +|=== + === Type Conversion Nodes ==== Boolean Conversion Nodes From de89f6acff79748503bbdb0d99ad50c332c8a812 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 22 Apr 2024 00:00:00 +0400 Subject: [PATCH 24/80] Add vector and matrix combine/extract nodes --- .../KHR_interactivity/Specification.adoc | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 37961dee36..13d0982830 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1032,6 +1032,76 @@ If the vector stem:[b] is not unit, rotation results may be undefined. | `float4 value` | Transformed vector |=== +===== Combine + +[cols="1h,1,2"] +|=== +| Type | `math/combine2` | Combine two floats into a two-component vector +.2+| Input value sockets +| `float a` | First component +| `float b` | Second component +| Output value sockets +| `float2 value` | Vector +|=== + +[cols="1h,1,2"] +|=== +| Type | `math/combine3` | Combine three floats into a three-component vector +.3+| Input value sockets +| `float a` | First component +| `float b` | Second component +| `float c` | Third component +| Output value sockets +| `float3 value` | Vector +|=== + +[cols="1h,1,2"] +|=== +| Type | `math/combine4` | Combine four floats into a four-component vector +.4+| Input value sockets +| `float a` | First component +| `float b` | Second component +| `float c` | Third component +| `float d` | Fourth component +| Output value sockets +| `float4 value` | Vector +|=== + +===== Extract + +[cols="1h,1,2"] +|=== +| Type | `math/extract2` | Extract two floats from a two-component vector +| Input value sockets +| `float2 a` | Vector +.2+| Output value sockets +| `float 0` | First component +| `float 1` | Second component +|=== + +[cols="1h,1,2"] +|=== +| Type | `math/extract3` | Extract three floats from a three-component vector +| Input value sockets +| `float3 a` | Vector +.3+| Output value sockets +| `float 0` | First component +| `float 1` | Second component +| `float 2` | Third component +|=== + +[cols="1h,1,2"] +|=== +| Type | `math/extract4` | Extract four floats from a four-component vector +| Input value sockets +| `float4 a` | Vector +.4+| Output value sockets +| `float 0` | First component +| `float 1` | Second component +| `float 2` | Third component +| `float 3` | Fourth component +|=== + ==== Matrix Nodes ===== Transpose @@ -1079,6 +1149,58 @@ If the vector stem:[b] is not unit, rotation results may be undefined. | `float4x4 value` | Matrix product |=== +===== Combine + +[cols="1h,1,2"] +|=== +| Type | `math/combine4x4` | Combine 16 floats into a 4x4 matrix +.16+| Input value sockets +| `float a` | First row, first column element +| `float b` | Second row, first column element +| `float c` | Third row, first column element +| `float d` | Fourth row, first column element +| `float e` | First row, second column element +| `float f` | Second row, second column element +| `float g` | Third row, second column element +| `float h` | Fourth row, second column element +| `float i` | First row, third column element +| `float j` | Second row, third column element +| `float k` | Third row, third column element +| `float l` | Fourth row, third column element +| `float m` | First row, fourth column element +| `float n` | Second row, fourth column element +| `float o` | Third row, fourth column element +| `float p` | Fourth row, fourth column element +| Output value sockets +| `float4x4 value` | Matrix +|=== + +===== Extract + +[cols="1h,1,2"] +|=== +| Type | `math/extract4x4` | Extract 16 floats from a 4x4 matrix +| Input value sockets +| `float4x4 a` | Matrix +.16+| Output value sockets +| `float 0` | First row, first column element +| `float 1` | Second row, first column element +| `float 2` | Third row, first column element +| `float 3` | Fourth row, first column element +| `float 4` | First row, second column element +| `float 5` | Second row, second column element +| `float 6` | Third row, second column element +| `float 7` | Fourth row, second column element +| `float 8` | First row, third column element +| `float 9` | Second row, third column element +| `float 10` | Third row, third column element +| `float 11` | Fourth row, third column element +| `float 12` | First row, fourth column element +| `float 13` | Second row, fourth column element +| `float 14` | Third row, fourth column element +| `float 15` | Fourth row, fourth column element +|=== + ==== Integer Arithmetic Nodes All inputs to these nodes are two's complement 32-bit signed integers. From 6867635d2ce89f22720e7e43daecb35cba4544c5 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 13 May 2024 00:00:00 +0000 Subject: [PATCH 25/80] Add pointer/interpolate --- .../KHR_interactivity/Specification.adoc | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 13d0982830..2a327aebd2 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2333,7 +2333,85 @@ When the `in` input flow is activated: 1. Evaluate all input values. 2. If the pointer or the pointer template with all its substitutions applied can be resolved, +.. if the _pointer interpolation state dynamic array_ (defined below) contains an entry with the same resolved JSON Pointer value, remove it from the array; .. set the resolved property to the `value` input value; .. activate the `out` output flow. 3. If the pointer or the pointer template with all its substitutions applied cannot be resolved, .. activate the `err` output flow. + +===== Pointer Interpolate + +[cols="1h,1,2"] +|=== +| Type | `pointer/interpolate` | Interpolate an object model property value +| Configuration +| `string pointer` | The JSON pointer or JSON pointer template +| Input flow sockets +| `in` | The entry flow into this node +.5+| Input value sockets +| `int ` | The JSON pointer template path segment to be substituted at runtime +| `T value` | The target property value +| `float duration` | The time, in seconds, in which the property **SHOULD** reach the target value +| `float2 p1` | Control point P1 +| `float2 p2` | Control point P2 +.3+| Output flow sockets +| `out` | The flow to be activated if the JSON pointer can be resolved and the input values are valid +| `err` | The flow to be activated if the JSON pointer cannot be resolved or the input values are invalid +| `done` | The flow to be activated when the property reaches the target value +|=== + +This node interpolates and updates the specified glTF Asset Object Model property multiple times over the specified duration using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined or if it is integer or boolean, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. + +The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. + +If the `value` input value or any intermediate interpolated value are not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. + +This node has no internal state. + +When a node of this type is used in the behavior graph, the global graph state includes an implementation-defined _pointer interpolation state dynamic array_ each element of which contains the following data: + +- The resolved JSON Pointer to the Object Model property being interpolated +- Implementation-defined high precision timestamp value representing the interpolation start time +- Interpolation duration value converted to the implementation-defined high precision time type +- Object Model property value at the time of the successful node activation +- Information needed for cubic Bézier spline evaluation derived from the node's input values +- Target property value +- Implementation-specific pointer to the `done` output flow of the node that has added this entry + +This array is initially empty and its maximum size is implementation-specific. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. If the pointer or the pointer template with all its substitutions applied cannot be resolved, +.. activate the `err` output flow and skip the next steps. +3. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type used for the internal interpolation start time value, +.. activate the `err` output flow and skip the next steps. +4. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, +.. activate the `err` output flow and skip the next steps. +5. If starting a new pointer interpolation exceeds any implementation-specific limit, +.. activate the `err` output flow and skip the next steps. +6. If the _pointer interpolation state dynamic array_ contains an entry with the same resolved JSON Pointer value, +.. remove it from the array. +7. Using the implicitly-defined end points stem:[P_0 (0, 0)] and stem:[P_3 (1, 1)] together with the control points stem:[P_1] and stem:[P_2] provided via the input values construct a cubic Bézier easing function for the stem:[[0, 1]] input range. +8. Add a new entry to the _pointer interpolation state dynamic array_ filling it with the required information based on the evaluated input values. +9. Activate the `out` output flow. + +On each asset animation update, for each entry in the _pointer interpolation state dynamic array_: + +1. Compute the current input progress position _t_ as the time passed since the interpolation start divided by the interpolation's duration. +2. If _t_ is less than or equal to zero +.. skip the next steps. +3. If _t_ is NaN or greater than or equal to 1, +.. set the Object Model property to the target value; +.. remove the current entry from the _pointer interpolation state dynamic array_; +.. activate the `done` output flow linked to the current entry +.. skip the next steps. +4. Using the cubic Bézier spline information, compute the output progress position _q_ based on the _t_ value. This step implies that latexmath:[t \in [0; 1\]]. +5. Set the linked Object Model property to the new value computed as a linear interpolation between the original and the target property values using the output progress position _q_ as the interpolation coefficient. + +If the Object Model property is a quaternion, spherical linear interpolation expression **SHOULD** be used. + +Intermediate output progress values **MAY** be less than zero or greater than one. From 32fd17093c4f50e3cbf0277f2ad4dbc9cafd4be1 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 13 May 2024 00:00:00 +0000 Subject: [PATCH 26/80] Typo fixes --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 2a327aebd2..10226559d7 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2203,7 +2203,7 @@ When the `cancel` input flow is activated: | `out` | The flow to be activated after executing this node |=== -This node has no internal state but its execution **MAY** affect internal states of other nodes and the graph. +This node has no internal state but its execution **MAY** affect internal states of other nodes and the graph. When the `in` input flow is activated: @@ -2380,8 +2380,6 @@ When a node of this type is used in the behavior graph, the global graph state i This array is initially empty and its maximum size is implementation-specific. -This node has no internal state. - When the `in` input flow is activated: 1. Evaluate all input values. From 19630ba47dd3a33fe04be19001bc28b5df2483d9 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 13 May 2024 00:00:00 +0000 Subject: [PATCH 27/80] Adjust min/max/clamp node descriptions --- .../KHR_interactivity/Specification.adoc | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 10226559d7..2431b2fc09 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -473,6 +473,18 @@ For `float4x4` arguments, this operation performs per-component multiplication. | `floatN value` | Smallest of the arguments |=== +For the purposes of this node, negative zero is less than positive zero. + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +Math.min(a, b) +---- +==== + ===== Maximum [cols="1h,1,2"] @@ -485,6 +497,18 @@ For `float4x4` arguments, this operation performs per-component multiplication. | `floatN value` | Largest of the arguments |=== +For the purposes of this node, negative zero is less than positive zero. + +[NOTE] +.Note +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +Math.max(a, b) +---- +==== + ===== Clamp [cols="1h,1,2"] @@ -498,6 +522,8 @@ For `float4x4` arguments, this operation performs per-component multiplication. | `floatN value` | latexmath:[min(max(a, min(b, c)), max(b, c))] |=== +This node relies on `math/min` and `math/max` nodes defined above. + [NOTE] .Note ==== From a369436ac6d1024ca95c457841b4b0aea004527a Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 3 Jun 2024 00:00:00 +0000 Subject: [PATCH 28/80] Add animation control nodes --- .../KHR_interactivity/Specification.adoc | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 2431b2fc09..04c1f3206e 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2439,3 +2439,134 @@ On each asset animation update, for each entry in the _pointer interpolation sta If the Object Model property is a quaternion, spherical linear interpolation expression **SHOULD** be used. Intermediate output progress values **MAY** be less than zero or greater than one. + +==== Animation Control Nodes + +===== Animation Play + +[cols="1h,1,2"] +|=== +| Type | `animation/start` | Start playing an animation +| Input flow sockets +| `in` | The entry flow into this node +.4+| Input value sockets +| `int animation` | Animation index +| `float startTime` | Start time +| `float endTime` | End time +| `float speed` | Speed multiplier +.3+| Output flow sockets +| `out` | The flow to be activated if the input values are valid +| `err` | The flow to be activated if any of the input values is invalid +| `completed` | The flow to be activated after the animation ends +|=== + +This node starts playing an animation using the specified input values. + +For the purposes of the Animation Control Nodes the concept of glTF animations is extended to unambiguously map any _requested input timestamp_ stem:[r] to the _effective input timestamp_ stem:[t] present in the glTF animation data as follows. + +1. Let stem:[T] be the maximum value of all animation sampler input accessors of the animation. Then, the stored animation data defines the animated property values for all _effective input timestamps_ in the stem:[[0, T]] range. +2. Let stem:[r] be a scalar value on a timeline infinite in both directions, from negative infinity to positive infinity. +3. If stem:[T] is not equal to zero, let stem:[s] be the current iteration number computed as follows: +latexmath:[s=\begin{cases} + \left\lceil \dfrac{r-T}{T} \right\rceil & \text{if } r \gt 0 \\ + \left\lfloor \dfrac{r}{T} \right\rfloor & \text{if } r \le 0 \\ + \end{cases}] +4. Now for each _requested input timestamp_ stem:[r], the corresponding _effective input timestamp_ is +latexmath:[t=\begin{cases} + r - s * T & \text{if } T \ne 0 \\ + 0 & \text{if } T=0 \\ + \end{cases}] + +This node has no internal state. + +When a node of this type is used in the behavior graph, the global graph state includes an implementation-defined _animation state dynamic array_ each element of which contains the following data: + +- Animation index +- Start time value +- End time value +- Stop time value (see `animation/stopAt`) +- Speed value +- Implementation-specific creation timestamp value associated with the system time when this entry was added +- Implementation-specific _end completion_ pointer to the `completed` output flow of the node that has added this entry +- Implementation-specific _stop completion_ pointer to the `completed` output flow of the node that has scheduled its stopping (see `animation/stopAt`) + +This array is initially empty; its maximum size is implementation-specific. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. If the `animation` input value is negative or greater than or equal to the number of glTF animations in the asset, +.. activate the `err` output flow and skip the next steps. +3. If the `startTime` or `endTime` input values are NaN or if the `startTime` input value is infinite, +.. activate the `err` output flow and skip the next steps. +4. If the `speed` input value is NaN, infinite, or less than or equal to zero, +.. activate the `err` output flow and skip the next steps. +5. If starting a new animation exceeds any implementation-specific limit, +.. activate the `err` output flow and skip the next steps. +6. If the _animation state dynamic array_ contains an entry with the same animation index, +.. remove it from the array; the previously set `completed` flows **MUST NOT** be activated. +7. Add a new entry to the _animation state dynamic array_ filling it with the required information based on the evaluated input values. The stop time value **MUST** be set to the end time value and the stop completion pointer **MUST** be set to null. +8. Activate the `out` output flow. + +On each asset animation update, for each entry in the _animation state dynamic array_: + +1. TBD + +===== Animation Stop + +[cols="1h,1,2"] +|=== +| Type | `animation/stop` | Immediately stop a playing animation +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `int animation` | Animation index +.2+| Output flow sockets +| `out` | The flow to be activated if the animation index is valid +| `err` | The flow to be activated if the animation index is invalid +|=== + +This node stops a playing animation. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. If the `animation` input value is negative or greater than or equal to the number of glTF animations in the asset, +.. activate the `err` output flow and skip the next steps. +3. If the _animation state dynamic array_ exists and contains an entry with the same animation index, +.. remove it from the array and stop the playing animation. The animated properties **MUST** keep their current values and the previously associated `completed` flows **MUST NOT** be activated. +4. Activate the `out` output flow. + +===== Animation Stop At + +[cols="1h,1,2"] +|=== +| Type | `animation/stopAt` | Schedule stopping a playing animation +| Input flow sockets +| `in` | The entry flow into this node +.2+| Input value sockets +| `int animation` | Animation index +| `float stopTime` | Stop time +.3+| Output flow sockets +| `out` | The flow to be activated if the input values are valid +| `err` | The flow to be activated if any of the input values is invalid +| `completed` | The flow to be activated after the animation stops +|=== + +This node stops a playing animation. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. If the `animation` input value is negative or greater than or equal to the number of glTF animations in the asset, +.. activate the `err` output flow and skip the next steps. +3. If the `stopTime` input value is NaN, +.. activate the `err` output flow and skip the next steps. +4. If the _animation state dynamic array_ exists and does contain an entry with the same animation index, +.. update the entry's stop completion pointer to the `completed` output flow of this node; +.. update the entry's stop time to the `stopTime` input value. +5. Activate the `out` output flow. From 4685e06e548591e359d26b3f8d0e0b53d1b7d656 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 3 Jun 2024 00:00:00 +0000 Subject: [PATCH 29/80] Add lifecycle nodes --- .../KHR_interactivity/Specification.adoc | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 04c1f3206e..77895fa627 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2570,3 +2570,36 @@ When the `in` input flow is activated: .. update the entry's stop completion pointer to the `completed` output flow of this node; .. update the entry's stop time to the `stopTime` input value. 5. Activate the `out` output flow. + +=== Event Nodes + +==== Lifecycle Event Nodes + +===== On Start + +[cols="1h,1,2"] +|=== +| Type | `event/onStart` | Start event +| Output flow sockets +| `out` | The flow to be activated when the start event happens +|=== + +This node is activated when all glTF asset resources are loaded and ready for rendering and interactions. + +This node has no internal state. + +===== On Tick + +[cols="1h,1,2"] +|=== +| Type | `event/onTick` | Tick event +.2+| Output value sockets +| `float timeSinceStart` | Relative time in seconds since the graph execution start +| `float timeSinceLastTick` | Relative time in seconds since the last tick occurred +| Output flow sockets +| `out` | The flow to be activated when the tick event happens +|=== + +This node is activated when a tick occurs. There will be at most one tick per rendered frame, which **SHOULD** align with frame time, but there are no guarantees of time elapsed between ticks. + +The internal state of this node consists of two floating-point time values initialized to NaN. They **MUST** be set to their effective values before the `out` output flow is activated. From 09f1f1d15627e80d2b9b384383a65ff36f35808d Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 3 Jun 2024 00:00:00 +0000 Subject: [PATCH 30/80] Add custom event nodes --- .../KHR_interactivity/Specification.adoc | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 77895fa627..4b94f03a63 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2603,3 +2603,50 @@ This node has no internal state. This node is activated when a tick occurs. There will be at most one tick per rendered frame, which **SHOULD** align with frame time, but there are no guarantees of time elapsed between ticks. The internal state of this node consists of two floating-point time values initialized to NaN. They **MUST** be set to their effective values before the `out` output flow is activated. + +==== Custom Event Nodes + +===== Receive + +[cols="1h,1,2"] +|=== +| Type | `event/receive` | Receive a custom event +| Configuration +| `int event` | The custom event index +| Output value sockets +| `` | Output values defined by the custom event +|=== + +This node is activated when a custom event specified by the `event` configuration value occurs. The types, names, and semantics of the output value sockets are defined by the custom event index. + +The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid. + +This node has no internal state. + +===== Send + +[cols="1h,1,2"] +|=== +| Type | `event/send` | Send a custom event +| Configuration +| `int event` | The custom event index +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `` | Input values defined by the custom event +.2+| Output flow sockets +| `out` | The flow to be activated after sending the event +| `err` | The flow to be activated if any of the input values is invalid +|=== + +This node sends a custom event specified by the `event` configuration value. The types, names, and validation rules of the input value sockets are defined by the custom event index. + +The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. If any of them is invalid, activate the `err` output flow and skip the next step. +3. Activate the `out` output flow. From 22fececa79b3cebb6e8c97357d8f5316731e865e Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 10 Jun 2024 00:00:00 +0000 Subject: [PATCH 31/80] Add more intro sections --- .../KHR_interactivity/Specification.adoc | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 4b94f03a63..6d12810981 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -240,19 +240,60 @@ Node's sockets and configurations are defined by its _type_. Node types follow ` A node is executed when its input flow socket is reached by or when one of its output value sockets is requested by another node. Usually, the node executes its dependencies (if any), its own logic, and any number (including zero) of outgoing flow sockets. [[types]] -== Value Types +== Types -TBD +All value sockets and configurations are strictly typed. + +Implementations of this extension **MUST** support the following type signatures. + +[[value-types]] +=== Value Types + +The following types are supported for value sockets. + +bool:: +a boolean value + +float:: +a double precision <> floating-point scalar value + +float2:: +a two-component vector of *float* values + +float3:: +a three-component vector of *float* values + +float4:: +a four-component vector of *float* values + +float4x4:: +a 4x4 matrix of *float* values + +int:: +a two's complement 32-bit signed integer scalar value + +[[configuration-types]] +=== Configuration Types + +Node configurations **MAY** use all of the value types and these two additional types. + +int[]:: +an array of *int* values + +string:: +a UTF-8 string value [[events]] == Custom Events -TBD +A behavior graph **MAY** define custom events for interacting with external execution environments and/or creating asynchronous loops. Although semantics of custom events is application-specific, their declarations **MUST** include value socket type information to ensure graph's type safety. [[variables]] -== Variables +== Custom Variables -TBD +A behavior graph **MAY** define custom variables. A variable **MAY** be declared simultaneously with its initial value, otherwise the variable **MUST** be initialized to the type-specific default. + +Custom variables **MUST** retain their values until the graph execution is terminated. == Node Types From 63f5d3d75188f3785be4ed72638a1bfb67d5df7b Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Jun 2024 00:00:00 +0000 Subject: [PATCH 32/80] Add JSON syntax --- .../KHR_interactivity/Specification.adoc | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 6d12810981..22f04edf1d 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2691,3 +2691,224 @@ When the `in` input flow is activated: 1. Evaluate all input values. 2. If any of them is invalid, activate the `err` output flow and skip the next step. 3. Activate the `out` output flow. + += JSON Syntax + +== General + +A `KHR_interactivity` extension object is added to the root-level `extensions` property. It contains four arrays corresponding to four interactivity concepts: `types`, `events`, `variables`, and `nodes`. As with the core glTF spec, if a JSON array is empty, it **MUST** be omitted from the asset. + +```json +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": ["KHR_interactivity"], + "extensions": { + "KHR_interactivity": { + "types": [ + // + ], + "events": [ + // + ], + "variables": [ + // + ], + "nodes": [ + // + ] + } + } +} +``` + +== Types + +The `types` array defines mappings between graph-local type indices and the recognized type signatures. + +The following example defines type `0` as *float2*, type `1` as *int*, and type `2` as *float*: + +```json +"types": [ + { + "signature": "float2" + }, + { + "signature": "int" + }, + { + "signature": "float" + } +] +``` + +The signature value **MUST** be one of the value types defined in this extension specification or `"custom"`. In the latter case, the type semantic **MUST** be provided by an additional extension. + +Non-custom signature **MUST NOT** appear more than once. + +== Events + +The `events` array defines external identifiers and value socket types for custom events. + +The following example defines a custom "`checkout`" event with an external identifier and one value socket: + +```json +"events": [ + { + "id": "checkout", + "values": [ + { + "id": "variant", + "type": 1 + } + ] + } +] +``` + +The event ID value is an application-specific event identifier recognized by the execution environment. If the `id` property is undefined, the event is considered internal to the graph. + +The `values` array defines IDs and type indices of the sockets associated with the event. If the array is undefined, the event has no associated value sockets. + +== Variables + +The `variables` array defines custom variables with their types and optional initialization values. + +The following example defines a custom variable with its initial value: + +```json +"variables": [ + { + "type": 0, + "value": [0.5, 0.5] + } +] +``` + +The `type` value defines the index of the variable type. + +The `value` array, if present, defines the initial variable value. The following table defines array size and default values for all value types defined in this extension. + +[cols="1,1,2", options="header"] +|=== +| Type | Array size | Default value +| `bool` | 1 | Boolean false +| `float` | 1 | Floating-point zero +| `float2` | 2 | Two floating-point zeros +| `float3` | 3 | Three floating-point zeros +| `float4` | 4 | Four floating-point zeros +| `float4x4` | 16 | Sixteen floating-point zeros +| `int` | 1 | Integer zero +|=== + +If the variable type is custom, the `value` property is defined by the extension defining the custom type. + +== Nodes + +The `nodes` array defines the behavior graph. + +Each element of the `nodes` array represents a node instance, i.e., it specifies node's type, configuration, sources of input value sockets, and pointers of the output flow sockets. + +Input value sockets **MAY** have inline constant values; in this case, the value socket type **MUST** be defined. + +Inline values and configurations use JSON arrays similarly to the initial variable values. + +The following example instantiates a `math/add` node that has both its input value sockets filled with inline integer values. + +```json +"nodes": [ + { + "type": "math/add", + "values": [ + { + "id": "a", + "value": [1], + "type": 1 + }, + { + "id": "b", + "value": [2], + "type": 1 + } + ] + } +] +``` + +The following example instantiates three nodes. The `math/sub` node has both its input value sockets connected to output value sockets of two other nodes: `math/pi` and `math/e`. + +```json +"nodes": [ + { + "type": "math/pi" + }, + { + "type": "math/e" + }, + { + "type": "math/sub", + "values": [ + { + "id": "a", + "node": 0, + "socket": "value" + }, + { + "id": "b", + "node": 1, + "socket": "value" + } + ] + } +] +``` + +The following example instantiates two nodes. The `variable/set` node sets a custom variable with index `0` when the start event happens. + +```json +"variables": [ + { + "type": 1 + } +], +"nodes": [ + { + "type": "variable/set", + "configuration": [ + { + "id": "variable", + "value": [0] + } + ], + "values": [ + { + "id": "value", + "value": [1], + "type": 1 + } + ] + }, + { + "type": "event/onStart", + "flows": [ + { + "id": "out", + "node": 0, + "socket": "in" + } + ] + } +] +``` + +The `type` property is required; it defines semantics and validation of the `configuration`, `values`, and `flows` arrays. + +The same `id` value **MUST NOT** be used more than once within each of the `configuration`, `values`, and `flows` arrays. + +If the node type has configuration, the `configuration` array **MUST** provide all configuration parameters as inline values. + +If the node type has input value sockets, the `values` array **MUST** connect all input value sockets to other nodes or fill them with inline values. Additionally, for each element of the `values` array: + +- `value` and `node` properties **MUST NOT** be defined at the same time; +- if `value` is defined, `type` **MUST** also be defined. From 4177adfbc3f543136e2c7d4ea210202d11d46550 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:00:00 +0000 Subject: [PATCH 33/80] Initialize floating-point variables to NaN --- .../2.0/Khronos/KHR_interactivity/Specification.adoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 22f04edf1d..4bc29a5fb8 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2794,11 +2794,11 @@ The `value` array, if present, defines the initial variable value. The following |=== | Type | Array size | Default value | `bool` | 1 | Boolean false -| `float` | 1 | Floating-point zero -| `float2` | 2 | Two floating-point zeros -| `float3` | 3 | Three floating-point zeros -| `float4` | 4 | Four floating-point zeros -| `float4x4` | 16 | Sixteen floating-point zeros +| `float` | 1 | Floating-point NaN +| `float2` | 2 | Two floating-point NaNs +| `float3` | 3 | Three floating-point NaNs +| `float4` | 4 | Four floating-point NaN +| `float4x4` | 16 | Sixteen floating-point NaNs | `int` | 1 | Integer zero |=== From 21e1b4c0b9de6c53424262c60797ed5793e0ec16 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:00:00 +0000 Subject: [PATCH 34/80] Update custom event nodes --- .../KHR_interactivity/Specification.adoc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 4bc29a5fb8..f4deb00478 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2656,13 +2656,17 @@ The internal state of this node consists of two floating-point time values initi | `int event` | The custom event index | Output value sockets | `` | Output values defined by the custom event +| Output flow sockets +| `out` | The flow to be activated when the custom event happens |=== This node is activated when a custom event specified by the `event` configuration value occurs. The types, names, and semantics of the output value sockets are defined by the custom event index. The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid. -This node has no internal state. +The internal state of this node consists of all output value sockets initialized to type-default values, i.e., NaN for floating-point types, zero for integers, and false for bools. + +The output value sockets **MUST** be updated before activating the `out` output flow. ===== Send @@ -2675,12 +2679,17 @@ This node has no internal state. | `in` | The entry flow into this node | Input value sockets | `` | Input values defined by the custom event -.2+| Output flow sockets +| Output flow sockets | `out` | The flow to be activated after sending the event -| `err` | The flow to be activated if any of the input values is invalid |=== -This node sends a custom event specified by the `event` configuration value. The types, names, and validation rules of the input value sockets are defined by the custom event index. +This node sends a custom event specified by the `event` configuration value. The types and names of the input value sockets are defined by the custom event index. + +[NOTE] +.Note +==== +If the graph needs to know whether the event has been received and/or processed by an external environment, the latter could send another event in response. +==== The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid. @@ -2689,7 +2698,7 @@ This node has no internal state. When the `in` input flow is activated: 1. Evaluate all input values. -2. If any of them is invalid, activate the `err` output flow and skip the next step. +2. Send the custom event. 3. Activate the `out` output flow. = JSON Syntax From 83aa69ccd6aa837e44506a56f4c69c70db22c34b Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:00:00 +0000 Subject: [PATCH 35/80] Align async output flow socket names --- .../KHR_interactivity/Specification.adoc | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index f4deb00478..5e0cfa7259 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2218,13 +2218,13 @@ When the `in` input flow is activated: | `in` | The entry flow into this node | `cancel` | When this flow is activated, all delayed activations scheduled by this node are cancelled | Input value sockets -| `float duration` | The duration, in seconds, to delay the `completed` output flow activation +| `float duration` | The duration, in seconds, to delay the `done` output flow activation | Output value sockets | `int lastDelayIndex` | The delay index assigned during the last successful node execution .3+| Output flow sockets | `out` | The flow to be activated if the `duration` value is valid | `err` | The flow to be activated if the `duration` value is invalid -| `completed` | The flow to be activated after the delay +| `done` | The flow to be activated after the delay |=== The internal state of this node consists of an integer `lastDelayIndex` value initialized to -1 and a dynamic array of activation indices scheduled by the node. This array is initially empty and its maximum size is implementation-specific. @@ -2247,7 +2247,7 @@ When the `in` input flow is activated: 7. Push the value of `lastDelayIndex` to the graph and node arrays of activation indices. 8. Schedule the following actions at the _activationTime_ time: .. Removal of the activation index value from both arrays of activation indices. -.. Activation of the `completed` output flow. +.. Activation of the `done` output flow. 9. Activate the `out` output flow. When the `cancel` input flow is activated: @@ -2498,7 +2498,7 @@ Intermediate output progress values **MAY** be less than zero or greater than on .3+| Output flow sockets | `out` | The flow to be activated if the input values are valid | `err` | The flow to be activated if any of the input values is invalid -| `completed` | The flow to be activated after the animation ends +| `done` | The flow to be activated after the animation ends |=== This node starts playing an animation using the specified input values. @@ -2528,8 +2528,8 @@ When a node of this type is used in the behavior graph, the global graph state i - Stop time value (see `animation/stopAt`) - Speed value - Implementation-specific creation timestamp value associated with the system time when this entry was added -- Implementation-specific _end completion_ pointer to the `completed` output flow of the node that has added this entry -- Implementation-specific _stop completion_ pointer to the `completed` output flow of the node that has scheduled its stopping (see `animation/stopAt`) +- Implementation-specific _end completion_ pointer to the `done` output flow of the node that has added this entry +- Implementation-specific _stop completion_ pointer to the `done` output flow of the node that has scheduled its stopping (see `animation/stopAt`) This array is initially empty; its maximum size is implementation-specific. @@ -2545,7 +2545,7 @@ When the `in` input flow is activated: 5. If starting a new animation exceeds any implementation-specific limit, .. activate the `err` output flow and skip the next steps. 6. If the _animation state dynamic array_ contains an entry with the same animation index, -.. remove it from the array; the previously set `completed` flows **MUST NOT** be activated. +.. remove it from the array; the previously set `done` flows **MUST NOT** be activated. 7. Add a new entry to the _animation state dynamic array_ filling it with the required information based on the evaluated input values. The stop time value **MUST** be set to the end time value and the stop completion pointer **MUST** be set to null. 8. Activate the `out` output flow. @@ -2577,7 +2577,7 @@ When the `in` input flow is activated: 2. If the `animation` input value is negative or greater than or equal to the number of glTF animations in the asset, .. activate the `err` output flow and skip the next steps. 3. If the _animation state dynamic array_ exists and contains an entry with the same animation index, -.. remove it from the array and stop the playing animation. The animated properties **MUST** keep their current values and the previously associated `completed` flows **MUST NOT** be activated. +.. remove it from the array and stop the playing animation. The animated properties **MUST** keep their current values and the previously associated `done` flows **MUST NOT** be activated. 4. Activate the `out` output flow. ===== Animation Stop At @@ -2593,7 +2593,7 @@ When the `in` input flow is activated: .3+| Output flow sockets | `out` | The flow to be activated if the input values are valid | `err` | The flow to be activated if any of the input values is invalid -| `completed` | The flow to be activated after the animation stops +| `done` | The flow to be activated after the animation stops |=== This node stops a playing animation. @@ -2608,7 +2608,7 @@ When the `in` input flow is activated: 3. If the `stopTime` input value is NaN, .. activate the `err` output flow and skip the next steps. 4. If the _animation state dynamic array_ exists and does contain an entry with the same animation index, -.. update the entry's stop completion pointer to the `completed` output flow of this node; +.. update the entry's stop completion pointer to the `done` output flow of this node; .. update the entry's stop time to the `stopTime` input value. 5. Activate the `out` output flow. From d873db60ddb663036d4ff724d3f45d8508e9cc47 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:00:00 +0000 Subject: [PATCH 36/80] Clarify flow/multiGate randomness --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 5e0cfa7259..7ca07e3c0a 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2128,6 +2128,8 @@ When the `in` input flow is activated: .. set the `lastIndex` value to `i`; .. activate the output flow with index `i`. +When the `isRandom` and `isLoop` configuration values are true, the output flow activation order **SHOULD** be randomized on each loop iteration. + ===== Wait All [cols="1h,1,2"] From 86409f3685ff125e8d83ab00255ecc175b663914 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:00:00 +0000 Subject: [PATCH 37/80] Fix typo --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 7ca07e3c0a..6c3135d302 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -191,7 +191,7 @@ On the other hand, behavior graphs are a superset of trigger-action lists, meani === Turing Completeness The execution model and node choices for this extension mean that it is Turing-complete. This means that an implementation of this can execute any computation and it is also hard to predict if it will run forever (e.g. halt or not.) -While this may present security implications, it is not a major hindrance and can be safely mitigated so that any implementation does not become susceptible to denial of services by badly behaving behavior graphs, whether intention or not. +While this may present security implications, it is not a major hindrance and can be safely mitigated so that any implementation does not become susceptible to denial of services by badly behaving behavior graphs, whether intentional or not. The main way to mitigate the risk of non-halting behavior graphs is to limit the amount of time given to them for execution, both in terms of individual time slice as well as overall execution time. From 4834fcd00c05230947b8c682833a2aa6222f027f Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:00:00 +0000 Subject: [PATCH 38/80] Clarify input value sockets for pointer nodes --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 6c3135d302..9bb96ec36d 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2337,7 +2337,7 @@ When the `in` input flow is activated: | Configuration | `string pointer` | The JSON pointer or JSON pointer template | Input value sockets -| `int ` | The JSON pointer template path segment to be substituted at runtime +| `int ` | Zero or more JSON pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) .2+| Output value sockets | `T value` | The resolved property value | `bool isValid` | True if the property value can be resolved, false otherwise @@ -2383,7 +2383,7 @@ Unresolvable pointers include those with negative or out-of-bounds array indices | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets -| `int ` | The JSON pointer template path segment to be substituted at runtime +| `int ` | Zero or more JSON pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) | `T value` | The new property value .2+| Output flow sockets | `out` | The flow to be activated if the JSON pointer can be resolved @@ -2418,7 +2418,7 @@ When the `in` input flow is activated: | Input flow sockets | `in` | The entry flow into this node .5+| Input value sockets -| `int ` | The JSON pointer template path segment to be substituted at runtime +| `int ` | Zero or more JSON pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) | `T value` | The target property value | `float duration` | The time, in seconds, in which the property **SHOULD** reach the target value | `float2 p1` | Control point P1 From e33a3bc8a33455469a4c7128aa2197fea54d64de Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:00:00 +0000 Subject: [PATCH 39/80] Clarify configuration value types --- .../2.0/Khronos/KHR_interactivity/Specification.adoc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 9bb96ec36d..8929159422 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2821,7 +2821,15 @@ The `nodes` array defines the behavior graph. Each element of the `nodes` array represents a node instance, i.e., it specifies node's type, configuration, sources of input value sockets, and pointers of the output flow sockets. -Input value sockets **MAY** have inline constant values; in this case, the value socket type **MUST** be defined. +Input value sockets **MAY** have inline constant values; in this case, the value socket type **MUST** be explicitly defined. + +Configuration values are always implicitly typed based on the node's type. + +[NOTE] +.Rationale +==== +Some nodes have configuration values of array and/or string types that cannot be expressed with the explicit types defined in this Specification. +==== Inline values and configurations use JSON arrays similarly to the initial variable values. From fbcf6968e19ad6a29024f31b5659fe78df9b5a4c Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:00:00 +0000 Subject: [PATCH 40/80] Clarify pointer nodes for not mutable pointers --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 8929159422..5a7643f4ae 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2390,7 +2390,7 @@ Unresolvable pointers include those with negative or out-of-bounds array indices | `err` | The flow to be activated if the JSON pointer cannot be resolved |=== -This node sets a glTF Asset Object Model value using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. +This node sets a glTF Asset Object Model value using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. @@ -2429,7 +2429,7 @@ When the `in` input flow is activated: | `done` | The flow to be activated when the property reaches the target value |=== -This node interpolates and updates the specified glTF Asset Object Model property multiple times over the specified duration using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined or if it is integer or boolean, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. +This node interpolates and updates the specified glTF Asset Object Model property multiple times over the specified duration using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined, if it is integer or boolean, or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. From 11339a0b5e1b14a7072fee342fcdeafa33f40ed6 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:00:00 +0000 Subject: [PATCH 41/80] Add active camera pointers --- .../KHR_interactivity/Specification.adoc | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 5a7643f4ae..f7a8151c0d 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2703,6 +2703,29 @@ When the `in` input flow is activated: 2. Send the custom event. 3. Activate the `out` output flow. +== Extending glTF Object Model + +This Specification defines additional glTF Object Model pointers for use with `pointer/*` nodes. + +=== Active Camera Information + +In some viewers, such as, but not limited to, augmented reality viewers and virtual reality viewers, the viewer implementation gives the user direct control over a virtual camera. This virtual camera **MAY** be controlled by user head movements, by movements of the user's phone with their hands, or by mouse, keyboard or touch input on a laptop, or by other means. It is useful for interactivity to be able to react to the position of this virtual camera. + +This Specification defines the “active camera” as the camera transformation that ought to be reacted to by interactivity. When there is only one camera being displayed to the user the implementation **SHOULD** use this camera as the “active camera”. When there are multiple cameras being controlled by the user, the implementation **MAY** select one such camera or construct a synthetic camera to use as the “active camera” (for example the midpoint of two stereoscopic camera positions). When zero cameras are being controlled by the user but views from one or more cameras are being displayed to the user, the implementation **SHOULD** select one of the cameras that is being displayed as the “active camera”. + +The `position` read-only property represents the “active camera” position in the global space. The `rotation` read-only property represents the “active camera” rotation quaternion; the identity quaternion corresponds to the camera orientation defined in the glTF 2.0 Specification. + +An implementation **MAY** provide no “active camera” data, for example for privacy reasons or if no cameras are being displayed to the user. If the “active camera” position is unavailable, the `position` property **MUST** be set to all NaNs; if the “active camera” rotation is unavailable, the `rotation` property **MUST** be set to all NaNs. + +The following pointers represent the read-only properties defined in this section. + +[options="header",cols="50%,15%"] +|=== +| Pointer | Type +| `/activeCamera/rotation` | `float4` +| `/activeCamera/position` | `float3` +|=== + = JSON Syntax == General From 220ca407a2ce1f8463855803778edf73a885b7e9 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 29 Jul 2024 00:00:00 +0000 Subject: [PATCH 42/80] Clarify pointer nodes validity --- .../KHR_interactivity/Specification.adoc | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index f7a8151c0d..ae0a5f1bbe 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2335,34 +2335,38 @@ When the `in` input flow is activated: |=== | Type | `pointer/get` | Get an object model property value | Configuration -| `string pointer` | The JSON pointer or JSON pointer template +| `string pointer` | The JSON Pointer or JSON Pointer template | Input value sockets -| `int ` | Zero or more JSON pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) +| `int ` | Zero or more JSON Pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) .2+| Output value sockets | `T value` | The resolved property value | `bool isValid` | True if the property value can be resolved, false otherwise |=== -This node gets a glTF Asset Object Model value using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. +This node gets a glTF Asset Object Model value using the provided JSON Pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the pointer string has invalid syntax or if the type of the referenced property cannot be statically determined, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. [NOTE] .Examples ==== -The `pointer` configuration value `"/nodes/0/scale"` is valid; its output value socket is of `float3` type. +The `pointer` configuration value `"/nodes/1024/scale"` is valid even if the `nodes` array does not have enough elements; the type `T` is `float3`; the output values are set as described below. -The `pointer` configuration value `"/myProperty"` is invalid because this path is not defined. +The `pointer` configuration value `"/nodes/-1/scale"` is invalid because `-1` is not a valid array element reference according to the JSON Pointer standard. + +The `pointer` configuration value `"/myProperty"` is invalid because this path is not defined in the glTF Asset Object Model. ==== -The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. +The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime by converting each `` input value to a base-10 string representation. All input values **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. [NOTE] .Example ==== -If the `pointer` configuration value is `"/nodes/{myId}/scale"`, the node has the `myId` input value socket, which value denotes the node index. +If the `pointer` configuration value is `"/nodes/{myId}/scale"`, the behavior graph node has the `myId` input value socket, which value denotes the glTF node index. ==== This node has no internal state. +If any of the input values is negative, the `value` output value is the default value for its type and the `isValid` output value is false. + If the pointer or the pointer template with all its substitutions applied can be resolved, the `value` output value is the resolved property value and the `isValid` output value is true. If the pointer or the pointer template with all its substitutions applied cannot be resolved, the `value` output value is the default value for its type and the `isValid` output value is false. @@ -2370,7 +2374,7 @@ If the pointer or the pointer template with all its substitutions applied cannot [NOTE] .Note ==== -Unresolvable pointers include those with negative or out-of-bounds array indices and/or non-existent JSON objects. +When the `isValid` output value is false, it means that the pointer string could be resolved in principle, i.e., it represents a known glTF property and its type can be determined, but the property does not exist in the current asset. For example, an array index is out of bounds or an optional JSON object, e.g., an extension, does not exist at that location. Refer to the glTF Asset Object Model Specification for the JSON Pointer resolution rules. ==== ===== Pointer Set @@ -2379,20 +2383,20 @@ Unresolvable pointers include those with negative or out-of-bounds array indices |=== | Type | `pointer/set` | Set an object model property value | Configuration -| `string pointer` | The JSON pointer or JSON pointer template +| `string pointer` | The JSON Pointer or JSON Pointer template | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets -| `int ` | Zero or more JSON pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) +| `int ` | Zero or more JSON Pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) | `T value` | The new property value .2+| Output flow sockets -| `out` | The flow to be activated if the JSON pointer can be resolved -| `err` | The flow to be activated if the JSON pointer cannot be resolved +| `out` | The flow to be activated if the JSON Pointer can be resolved +| `err` | The flow to be activated if the JSON Pointer cannot be resolved |=== -This node sets a glTF Asset Object Model value using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. +This node sets a glTF Asset Object Model value using the provided JSON Pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the pointer string has invalid syntax, if the type of the referenced property cannot be statically determined, or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. -The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. +The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime by converting each `` input value to a base-10 string representation. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. If the `value` input value is not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. @@ -2401,11 +2405,13 @@ This node has no internal state. When the `in` input flow is activated: 1. Evaluate all input values. -2. If the pointer or the pointer template with all its substitutions applied can be resolved, +2. If any of the pointer segment input values is negative, +.. activate the `err` output flow and skip the next steps. +3. If the pointer or the pointer template with all its substitutions applied can be resolved, .. if the _pointer interpolation state dynamic array_ (defined below) contains an entry with the same resolved JSON Pointer value, remove it from the array; .. set the resolved property to the `value` input value; .. activate the `out` output flow. -3. If the pointer or the pointer template with all its substitutions applied cannot be resolved, +4. If the pointer or the pointer template with all its substitutions applied cannot be resolved, .. activate the `err` output flow. ===== Pointer Interpolate @@ -2414,24 +2420,24 @@ When the `in` input flow is activated: |=== | Type | `pointer/interpolate` | Interpolate an object model property value | Configuration -| `string pointer` | The JSON pointer or JSON pointer template +| `string pointer` | The JSON Pointer or JSON Pointer template | Input flow sockets | `in` | The entry flow into this node .5+| Input value sockets -| `int ` | Zero or more JSON pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) +| `int ` | Zero or more JSON Pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) | `T value` | The target property value | `float duration` | The time, in seconds, in which the property **SHOULD** reach the target value | `float2 p1` | Control point P1 | `float2 p2` | Control point P2 .3+| Output flow sockets -| `out` | The flow to be activated if the JSON pointer can be resolved and the input values are valid -| `err` | The flow to be activated if the JSON pointer cannot be resolved or the input values are invalid +| `out` | The flow to be activated if the JSON Pointer can be resolved and the input values are valid +| `err` | The flow to be activated if the JSON Pointer cannot be resolved or the input values are invalid | `done` | The flow to be activated when the property reaches the target value |=== -This node interpolates and updates the specified glTF Asset Object Model property multiple times over the specified duration using the provided JSON pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the type of the pointer cannot be statically determined, if it is integer or boolean, or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. +This node interpolates and updates the specified glTF Asset Object Model property multiple times over the specified duration using the provided JSON Pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the pointer string has invalid syntax, if the type of the referenced property cannot be statically determined, if it is integer or boolean, or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. -The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime using the input values. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. +The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime by converting each `` input value to a base-10 string representation. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. If the `value` input value or any intermediate interpolated value are not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. @@ -2452,19 +2458,21 @@ This array is initially empty and its maximum size is implementation-specific. When the `in` input flow is activated: 1. Evaluate all input values. -2. If the pointer or the pointer template with all its substitutions applied cannot be resolved, +2. If any of the pointer segment input values is negative, .. activate the `err` output flow and skip the next steps. -3. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type used for the internal interpolation start time value, +3. If the pointer or the pointer template with all its substitutions applied cannot be resolved, +.. activate the `err` output flow and skip the next steps. +4. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type used for the internal interpolation start time value, .. activate the `err` output flow and skip the next steps. -4. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, +5. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, .. activate the `err` output flow and skip the next steps. -5. If starting a new pointer interpolation exceeds any implementation-specific limit, +6. If starting a new pointer interpolation exceeds any implementation-specific limit, .. activate the `err` output flow and skip the next steps. -6. If the _pointer interpolation state dynamic array_ contains an entry with the same resolved JSON Pointer value, +7. If the _pointer interpolation state dynamic array_ contains an entry with the same resolved JSON Pointer value, .. remove it from the array. -7. Using the implicitly-defined end points stem:[P_0 (0, 0)] and stem:[P_3 (1, 1)] together with the control points stem:[P_1] and stem:[P_2] provided via the input values construct a cubic Bézier easing function for the stem:[[0, 1]] input range. -8. Add a new entry to the _pointer interpolation state dynamic array_ filling it with the required information based on the evaluated input values. -9. Activate the `out` output flow. +8. Using the implicitly-defined end points stem:[P_0 (0, 0)] and stem:[P_3 (1, 1)] together with the control points stem:[P_1] and stem:[P_2] provided via the input values construct a cubic Bézier easing function for the stem:[[0, 1]] input range. +9. Add a new entry to the _pointer interpolation state dynamic array_ filling it with the required information based on the evaluated input values. +10. Activate the `out` output flow. On each asset animation update, for each entry in the _pointer interpolation state dynamic array_: From c77e924e18f1911a55aa06d203c3817d71817ff1 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 13 Jan 2025 00:00:00 +0000 Subject: [PATCH 43/80] Public specification update --- .../KHR_interactivity/Specification.adoc | 2330 +++++++++++++---- 1 file changed, 1781 insertions(+), 549 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index ae0a5f1bbe..13d99e65a3 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1,4 +1,4 @@ -// Copyright 2013-2023 The Khronos Group Inc. +// Copyright 2024 The Khronos Group Inc. // // SPDX-License-Identifier: CC-BY-4.0 @@ -48,7 +48,7 @@ toc::[] [[foreword]] = Foreword -Copyright 2013-2023 The Khronos Group Inc. +Copyright 2024 The Khronos Group Inc. This specification is protected by copyright laws and contains material proprietary to Khronos. Except as described by these terms, it or any components @@ -107,11 +107,7 @@ This document, referred to as the "`glTF Interactivity Extension Specification`" This extension aims to enhance glTF 2.0 by adding the ability to encode behavior and interactivity in 3D assets. -[NOTE] -.Note -==== -This specification is for single user experiences only and does not deal with any of the complexity involved in multi-user networked experiences. -==== +This extension is for single user experiences only and does not deal with any of the complexity involved in multi-user networked experiences. [[introduction-conventions]] == Document Conventions @@ -154,13 +150,22 @@ The following documents are referenced by normative sections of the specificatio [none] * [[bcp14]] -Bradner, S., _Key words for use in RFCs to Indicate Requirement Levels_, BCP 14, RFC 2119, March 1997. Leiba, B., _Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words_, BCP 14, RFC 8174, May 2017. +Bradner, S., _Key words for use in RFCs to Indicate Requirement Levels_, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997. Leiba, B., _Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words_, BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017. +* [[rfc6901]] +Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed., _JavaScript Object Notation (JSON) Pointer_, RFC 6901, DOI 10.17487/RFC6901, April 2013, + + * [[ieee-754]] ISO/IEC 60559 _Floating-point arithmetic_ ++ +[TIP] +==== +Also known as IEEE 754-2019, https://standards.ieee.org/ieee/754/6210/ +==== * [[ecma-262]] ECMA-262 @@ -189,24 +194,12 @@ Behavior graphs and trigger-action lists share common features, such as being sa On the other hand, behavior graphs are a superset of trigger-action lists, meaning that the former can support everything that trigger-action lists can, and more. Behavior graphs support “Queries”, “Logic” and “Control Flow” nodes, making them more expressive and capable of creating more sophisticated behaviors. This makes behavior graphs the preferred method of choice for high-end game engines, as it offers an identical safety model as trigger-action lists while being more expressive. === Turing Completeness -The execution model and node choices for this extension mean that it is Turing-complete. This means that an implementation of this can execute any computation and it is also hard to predict if it will run forever (e.g. halt or not.) +The execution model and node choices for this extension mean that it is Turing-complete. This means that an implementation of this can execute any computation and it is also hard to predict if it will run forever, e.g., halt or not. While this may present security implications, it is not a major hindrance and can be safely mitigated so that any implementation does not become susceptible to denial of services by badly behaving behavior graphs, whether intentional or not. The main way to mitigate the risk of non-halting behavior graphs is to limit the amount of time given to them for execution, both in terms of individual time slice as well as overall execution time. -=== Implementation Limitations -There will be limitations in engines and devices to such as: - -* Number of nodes in the graph -* Number of variables -* Number of custom events -* Number of concurrent pending events/async nodes -* Number of nodes executed per time slice -* Speed of graph execution - -These limitations are not defined in this specification. - [[concepts]] = Concepts @@ -218,38 +211,91 @@ A behavior graph is a JSON object containing _nodes_. It **MAY** also contain cu Behavior graphs are directed graphs with no directed cycles. +When a glTF asset contains a behavior graph, all glTF animations are assumed to be controlled by the graph so they **MUST NOT** play automatically. + [[nodes]] -== Nodes and Sockets +== Nodes -A _node_ is a JSON object, which represents an executable item. Each node is defined by its _type_ and a set of _sockets_. There are four kinds of sockets. +A _node_ is a JSON object, which represents an executable item. Each node is defined by its _declaration_, which includes an _operation_ and a (possibly empty) set of _value sockets_. Node operations follow `domain/operation` naming pattern. Depending on the operation, a node **MAY** have input and/or output _flow sockets_; they **MAY** be affected by the node's _configuration_. -_Output value sockets_ represent data initialized by the node or produced during its execution. For example, it could be results of math operations or parts of the node's internal state. Accessing these sockets either triggers computing the return value on the fly by executing the node or returns a value based on the node's internal state. Exact behavior depends on the node's type. +[[nodes-operation]] +=== Operation -_Input value sockets_ represent data accessed during the node's execution. For example, it could be arguments of math operations or execution parameters such as iteration count for loop nodes or duration for time-related nodes. These sockets **MUST** either be given a static value in the node object or connected to an output value socket of a different node. The node **MAY** access its input value sockets multiple times during the execution. The runtime **MUST** guarantee that all input value sockets have defined values when the node execution starts. +A node's _operation_ defines a specific set of steps performed by the execution environment when the node is executed. -_Output flow sockets_ represent "`function pointers`" that the node will call to advance the graph execution. For example, bodies and branches of flow control nodes are output flow sockets that drive further execution when certain condition are fulfilled. Output flow sockets **MAY** be unconnected; in such a case graph execution proceeds as if such sockets are no-ops. +A node is executed when one of its input flow sockets is activated, when one of its output value sockets is accessed by another node, when an operation-specific event occurs. A node **MAY** repeatedly activate its own input flow sockets during the execution. + +Usually, the node execution includes evaluating its input value sockets (if any), processing its own logic, and activating any number (including zero) of output flow sockets. + +[[nodes-sockets]] +=== Sockets + +There are four kinds of sockets. -_Input flow sockets_ represent "`methods`" that could be called on the node. For example, flow control nodes (such as loops and conditions) usually have an `in` input flow socket that starts node's execution. Additional operations **MAY** also be defined such as `reset` for nodes having an internal state. +_Output value sockets_ represent data initialized by the node or produced during its execution. For example, it could be results of math operations or parts of the node's internal state. Accessing these sockets either triggers computing the return value on the fly by executing the node or returns a value based on the node's internal state. Exact behavior depends on the node's operation. As a general rule, output value sockets **MUST** retain their values until a node having any number of flow sockets is executed. -Nodes **MAY** be configurable through static properties collectively called "`node's configuration`" that **MAY** affect the node's behavior and the number of its sockets, such as the number of cases for a switch-case control flow node. +[NOTE] +.Implementation Note +==== +At the current state of the Specification, the retention of output value socket values is observable only with the `math/random` node. +==== + +_Input value sockets_ represent data accessed during the node's execution. For example, it could be arguments of math operations or execution parameters such as iteration count for loop nodes or duration for time-related nodes. Each of these sockets **MUST** either be given an inline constant value in the node object or connected to an output value socket of a different node. The node **MAY** access its input value sockets multiple times during the execution. The runtime **MUST** guarantee that all input value sockets have defined values when the node execution starts. + +_Output flow sockets_ represent "`function pointers`" that the node will call to advance the graph execution. For example, bodies and branches of flow control nodes are output flow sockets that drive further execution when certain condition are fulfilled. Output flow sockets **MAY** be unconnected; in such a case graph execution proceeds as if such sockets are no-ops. + +_Input flow sockets_ represent "`methods`" that could be called on the node. For example, flow control nodes (such as loops and branches) usually have an `in` input flow socket that starts node's execution. Additional input flow sockets **MAY** exist such as `reset` for nodes having an internal state. Input and output value sockets have associated data types, e.g., floats, integers, booleans, etc. -Node's sockets and configurations are defined by its _type_. Node types follow `domain/operation` naming pattern. +Socket ids exist in four separate scopes corresponding to the four socket kinds. + +[NOTE] +.Example +==== +For example, a `flow/sequence` node can have an output flow socket with id `"in"` despite having an input flow socket with the same id. +==== -A node is executed when its input flow socket is reached by or when one of its output value sockets is requested by another node. Usually, the node executes its dependencies (if any), its own logic, and any number (including zero) of outgoing flow sockets. +[[socket-order]] +==== Socket Order -[[types]] -== Types +Although sockets are inherently unordered withing a node (because JSON properties are unordered), some operations such as `flow/sequence` or `flow/multiGate` need a specific socket order to guarantee predictable behavior. In such cases, the sockets are implicitly sorted by their ids in ascending order. -All value sockets and configurations are strictly typed. +For any given ids `a` and `b`, the following procedure **MUST** be used to determine if `a` is less than `b`. -Implementations of this extension **MUST** support the following type signatures. +1. Let _unitsA_ and _unitsB_ be the sequences of UTF-16 code units corresponding to the socket ids `a` and `b` respectively and _lengthA_ and _lengthB_ be the lengths of these sequences. +2. Let _minLength_ be the minimum of _lengthA_ and _lengthB_. +3. For each integer _i_ such that 0 ≤ _i_ < _minLength_, in ascending order, do +.. if _unitsA[i]_ < _unitsB[i]_ return true; +.. if _unitsA[i]_ > _unitsB[i]_ return false. +4. If _lengthA_ < _lengthB_ return true. +5. Return false. + +[TIP] +.Implementation Tip +==== +This is implementable in ECMAScript as follows, assuming that `flows` is a JSON object representing output flow sockets: +[source,js] +---- +const sortedSocketIds = Object.keys(flows).sort(); +---- +==== + +[CAUTION] +==== +This process enforces lexicographic order solely based on UTF-16 code units. In particular, the following two caveats apply: + +- A socket id `10` is _less_ than a socket id `9`. This could be avoided by padding socket ids to the same number of characters, i.e., using `09` instead of `9` in this case. + +- The sorting algorithm does not account for characters that use more than one code unit in UTF-16 encoding. For example, the "`North East Sans-Serif Arrow`" character has a code point of `0x1F855` encoded as two surrogate code units `[0xD83E, 0xDC55]` so it is _less_ than the "`Replacement Character`" character that has a code point of `0xFFFD` encoded directly as a single code unit. +==== + +[[socket-types]] +==== Value Socket Types -[[value-types]] -=== Value Types +All value sockets are strictly typed. -The following types are supported for value sockets. +Implementations of this extension **MUST** support the following type signatures. bool:: a boolean value @@ -266,27 +312,69 @@ a three-component vector of *float* values float4:: a four-component vector of *float* values +float2x2:: +a 2x2 matrix of *float* values + +float3x3:: +a 3x3 matrix of *float* values + float4x4:: a 4x4 matrix of *float* values int:: a two's complement 32-bit signed integer scalar value -[[configuration-types]] -=== Configuration Types +[[nodes-configuration]] +=== Configuration + +Nodes **MAY** be configurable through inline properties collectively called _configuration_ that **MAY** affect the node's behavior and the number of its sockets, such as the set of cases for the `flow/switch` node. + +If a node specification does not include any configuration, the node is not configurable and any configuration properties defined for it in the behavior graph **MUST** be ignored. + +Unless specified otherwise, all nodes that include configuration have a _default_ configuration. The default configuration **MUST** be used when the behavior graph does not provide any configuration or when the provided configuration is invalid. If a node does not have a default configuration (like `variable/*` nodes) and the behavior graph does not provide a valid configuration, the whole graph is invalid and **MUST** be rejected. -Node configurations **MAY** use all of the value types and these two additional types. +For a configuration to be valid, all configuration properties defined by the node specification **MUST** be provided in the behavior graph with valid types and values. If any of the configuration properties defined by the node specification is omitted or has invalid type or invalid value, the whole configuration is invalid and the node behavior **MUST** fall back to the default configuration if the latter is supported. Configuration properties present in the behavior graph but not defined by the node specification **MUST** be ignored. + +Implementations **SHOULD** generate appropriate warnings as deemed possible when: + +* a non-configurable node has a configuration in the behavior graph; +* a provided configuration contains unknown properties; +* a provided configuration is invalid. + +[[nodes-configuration-types]] +==== Configuration Types + +Configuration properties use a separate type system unrelated to the value socket types. + +bool:: +a boolean value + +int:: +a two's complement 32-bit signed integer scalar value int[]:: an array of *int* values string:: -a UTF-8 string value +a string value + +[[nodes-noop]] +=== Unsupported Operations + +If the execution environment does not support the operation, e.g., when the operation is defined by an unsupported or disabled extension for the Interactivity Specification, the operation is implicitly replaced with a "`no-op`" operation defined as follows: + +- activating the node's input flow sockets is ignored; +- the node's output flow sockets are never activated; +- the node's output value sockets have constant <> values. [[events]] == Custom Events -A behavior graph **MAY** define custom events for interacting with external execution environments and/or creating asynchronous loops. Although semantics of custom events is application-specific, their declarations **MUST** include value socket type information to ensure graph's type safety. +A behavior graph **MAY** define custom events for interacting with external execution environments and/or creating asynchronous loops. + +A custom event definition includes its value sockets with types and optional initial values as well as an optional unique string identifier for linking the event with the external environment. + +Semantics of custom events are application-specific. [[variables]] == Custom Variables @@ -295,11 +383,59 @@ A behavior graph **MAY** define custom variables. A variable **MAY** be declared Custom variables **MUST** retain their values until the graph execution is terminated. -== Node Types +[[variables-types]] +=== Custom Variable Types + +Custom variables use the same type system as the value sockets. The following table defines type-default values. + +[cols="1,2", options="header"] +|=== +| Type | Default value +| `bool` | Boolean false +| `float` | Floating-point NaN +| `float2` | Two floating-point NaNs +| `float3` | Three floating-point NaNs +| `float4` | Four floating-point NaNs +| `float2x2` | Four floating-point NaNs +| `float3x3` | Nine floating-point NaNs +| `float4x4` | Sixteen floating-point NaNs +| `int` | Integer zero +|=== + +[[limits]] +== Implementation-Specific Limits + +=== Static Limits + +Implementations **MAY** restrict the size and complexity of behavior graphs by imposing certain limits on the following statically-known properties: + +* The number of types +* The number of variables +* The number of custom events and the number of value sockets within a custom event +* The number of operation declarations +* The number of input and output value sockets in operation declarations +* The number of nodes +* The number of graph-defined output flow sockets in operations like `flow/sequence`, `flow/switch`, or `flow/multiGate` + +The graph **MUST** be rejected if it exceeds implementation-defined max values for these properties. + +=== Dynamic Limits + +Implementations **MAY** restrict the runtime capabilities of behavior graphs by imposing certain limits on the following features that require dynamic allocation of memory and/or processing power: + +* Numbers of simultaneous delays, animations, and interpolations; exceeding these limits results in runtime errors that can be gracefully handled by the graph itself, see `err` output flows of the corresponding nodes. + +* Number of events processed within a single rendered frame; exceeding this limit **MAY** result in an implementation-specific behavior such as reducing the frame rate or rescheduling the extra events. + +These limits are exposed to behavior graphs via additional glTF Object Model pointers. + += Functional Specification + +== Nodes === Math Nodes -In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3`, `float4`, or `float4x4` types. All value sockets of `floatN` types have the same type within a node. +In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, or `float4x4` types. All value sockets of `floatN` types have the same type within a node. ==== Constants @@ -307,7 +443,7 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 [cols="1h,1,2"] |=== -| Type | `math/e` | Euler's number +| Operation | `math/e` | Euler's number | Output value sockets | `float value` | 2.718281828459045 |=== @@ -315,7 +451,7 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 [cols="1h,1,2"] |=== -| Type | `math/pi` | Ratio of a circle's circumference to its diameter +| Operation | `math/pi` | Ratio of a circle's circumference to its diameter | Output value sockets | `float value` | 3.141592653589793 |=== @@ -323,7 +459,7 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 [cols="1h,1,2"] |=== -| Type | `math/inf` | Positive infinity +| Operation | `math/inf` | Positive infinity | Output value sockets | `float value` | _Infinity_ |=== @@ -331,7 +467,7 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 [cols="1h,1,2"] |=== -| Type | `math/nan` | Not a Number +| Operation | `math/nan` | Not a Number | Output value sockets | `float value` | _NaN_ |=== @@ -339,19 +475,19 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 These all operate component-wise. The description is per component. -If any input value is _NaN_, the output value is also _NaN_. +If any input value component is _NaN_, the corresponding output value component is also _NaN_. ===== Absolute Value [cols="1h,1,2"] |=== -| Type | `math/abs` | Absolute value operation +| Operation | `math/abs` | Absolute value operation | Input value sockets | `floatN a` | Argument | Output value sockets | `floatN value` | latexmath:[\begin{cases} -a & \text{if } a \lt 0 \\ - 0 & \text{if } a = 0 \\ + +0 & \text{if } a = \pm0 \\ a & \text{if } a \gt 0 \end{cases}] |=== @@ -360,7 +496,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/sign` | Sign operation +| Operation | `math/sign` | Sign operation | Input value sockets | `floatN a` | Argument | Output value sockets @@ -375,7 +511,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/trunc` | Truncate operation +| Operation | `math/trunc` | Truncate operation | Input value sockets | `floatN a` | Argument | Output value sockets @@ -388,7 +524,7 @@ If the argument is infinity, it is returned unchanged. [cols="1h,1,2"] |=== -| Type | `math/floor` | Floor operation +| Operation | `math/floor` | Floor operation | Input value sockets | `floatN a` | Argument | Output value sockets @@ -401,7 +537,7 @@ If the argument is infinity, it is returned unchanged. [cols="1h,1,2"] |=== -| Type | `math/ceil` | Ceil operation +| Operation | `math/ceil` | Ceil operation | Input value sockets | `floatN a` | Argument | Output value sockets @@ -410,11 +546,36 @@ If the argument is infinity, it is returned unchanged. If the argument is infinity, it is returned unchanged. +===== Round + +[cols="1h,1,2"] +|=== +| Operation | `math/round` | Round operation +| Input value sockets +| `floatN a` | Argument +| Output value sockets +| `floatN value` | Value equal to the integer nearest to stem:[a] +|=== + +Half-way cases **MUST** be rounded away from zero. Negative values greater than `-0.5` **MUST** be rounded to negative zero. + +If the argument is infinity, it is returned unchanged. + +[TIP] +.Implementation Tip +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +a < 0 ? -Math.round(-a) : Math.round(a) +---- +==== + ===== Fraction [cols="1h,1,2"] |=== -| Type | `math/fract` | Fractional operation +| Operation | `math/fract` | Fractional operation | Input value sockets | `floatN a` | Argument | Output value sockets @@ -425,7 +586,7 @@ If the argument is infinity, it is returned unchanged. [cols="1h,1,2"] |=== -| Type | `math/neg` | Negation operation +| Operation | `math/neg` | Negation operation | Input value sockets | `floatN a` | Argument | Output value sockets @@ -436,7 +597,7 @@ If the argument is infinity, it is returned unchanged. [cols="1h,1,2"] |=== -| Type | `math/add` | Addition operation +| Operation | `math/add` | Addition operation .2+| Input value sockets | `floatN a` | First addend | `floatN b` | Second addend @@ -448,7 +609,7 @@ If the argument is infinity, it is returned unchanged. [cols="1h,1,2"] |=== -| Type | `math/sub` | Subtraction operation +| Operation | `math/sub` | Subtraction operation .2+| Input value sockets | `floatN a` | Minuend | `floatN b` | Subtrahend @@ -460,7 +621,7 @@ If the argument is infinity, it is returned unchanged. [cols="1h,1,2"] |=== -| Type | `math/mul` | Multiplication operation +| Operation | `math/mul` | Multiplication operation .2+| Input value sockets | `floatN a` | First factor | `floatN b` | Second factor @@ -468,17 +629,19 @@ If the argument is infinity, it is returned unchanged. | `floatN value` | Product, stem:[a * b] |=== +For matrix arguments, this operation performs per-component multiplication. + [NOTE] -.Note +.Authoring Note ==== -For `float4x4` arguments, this operation performs per-component multiplication. +See `math/matmul` for matrix multiplication. ==== ===== Division [cols="1h,1,2"] |=== -| Type | `math/div` | Division operation +| Operation | `math/div` | Division operation .2+| Input value sockets | `floatN a` | Dividend | `floatN b` | Divisor @@ -490,7 +653,7 @@ For `float4x4` arguments, this operation performs per-component multiplication. [cols="1h,1,2"] |=== -| Type | `math/rem` | Remainder operation +| Operation | `math/rem` | Remainder operation .2+| Input value sockets | `floatN a` | Dividend | `floatN b` | Divisor @@ -502,11 +665,21 @@ For `float4x4` arguments, this operation performs per-component multiplication. \end{cases}] |=== +[TIP] +.Implementation Tip +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +a % b +---- +==== + ===== Minimum [cols="1h,1,2"] |=== -| Type | `math/min` | Minimum operation +| Operation | `math/min` | Minimum operation .2+| Input value sockets | `floatN a` | First argument | `floatN b` | Second argument @@ -516,8 +689,8 @@ For `float4x4` arguments, this operation performs per-component multiplication. For the purposes of this node, negative zero is less than positive zero. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] @@ -530,7 +703,7 @@ Math.min(a, b) [cols="1h,1,2"] |=== -| Type | `math/max` | Maximum operation +| Operation | `math/max` | Maximum operation .2+| Input value sockets | `floatN a` | First argument | `floatN b` | Second argument @@ -540,8 +713,8 @@ Math.min(a, b) For the purposes of this node, negative zero is less than positive zero. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] @@ -554,7 +727,7 @@ Math.max(a, b) [cols="1h,1,2"] |=== -| Type | `math/clamp` | Clamp operation +| Operation | `math/clamp` | Clamp operation .3+| Input value sockets | `floatN a` | Value to clamp | `floatN b` | First boundary @@ -566,7 +739,7 @@ Math.max(a, b) This node relies on `math/min` and `math/max` nodes defined above. [NOTE] -.Note +.Authoring Note ==== This operation correctly handles a case when stem:[b] is greater than stem:[c]. ==== @@ -575,7 +748,7 @@ This operation correctly handles a case when stem:[b] is greater than stem:[c]. [cols="1h,1,2"] |=== -| Type | `math/saturate` | Saturate operation +| Operation | `math/saturate` | Saturate operation | Input value sockets | `floatN a` | Value to saturate | Output value sockets @@ -586,7 +759,7 @@ This operation correctly handles a case when stem:[b] is greater than stem:[c]. [cols="1h,1,2"] |=== -| Type | `math/mix` | Linear interpolation operation +| Operation | `math/mix` | Linear interpolation operation .3+| Input value sockets | `floatN a` | Interpolated value at stem:[0] | `floatN b` | Interpolated value at stem:[1] @@ -603,7 +776,7 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/eq` | Equality operation +| Operation | `math/eq` | Equality operation .2+| Input value sockets | `floatN a` | First argument | `floatN b` | Second argument @@ -615,7 +788,7 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/lt` | Less than operation +| Operation | `math/lt` | Less than operation .2+| Input value sockets | `float a` | First argument | `float b` | Second argument @@ -627,7 +800,7 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/le` | Less than or equal to operation +| Operation | `math/le` | Less than or equal to operation .2+| Input value sockets | `float a` | First argument | `float b` | Second argument @@ -639,7 +812,7 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/gt` | Greater than operation +| Operation | `math/gt` | Greater than operation .2+| Input value sockets | `float a` | First argument | `float b` | Second argument @@ -651,7 +824,7 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/ge` | Greater than or equal operation +| Operation | `math/ge` | Greater than or equal operation .2+| Input value sockets | `float a` | First argument | `float b` | Second argument @@ -665,7 +838,7 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/isnan` | Not a Number check operation +| Operation | `math/isnan` | Not a Number check operation | Input value sockets | `float a` | Argument | Output value sockets @@ -676,7 +849,7 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/isinf` | Infinity check operation +| Operation | `math/isinf` | Infinity check operation | Input value sockets | `float a` | Argument | Output value sockets @@ -687,16 +860,45 @@ If any input value is _NaN_, the output value is false. [cols="1h,1,2"] |=== -| Type | `math/select` | Conditional selection operation +| Operation | `math/select` | Conditional selection operation .3+| Input value sockets | `bool condition` | Value selecting the value returned | `T a` | Positive selection option | `T b` | Negative selection option | Output value sockets -| `T value` | stem:[a] if the the `condition` input value is true; stem:[b] otherwise +| `T value` | stem:[a] if the `condition` input value is true; stem:[b] otherwise |=== -The type `T` represents any type. It **MUST** be the same for the output value socket and the input value sockets stem:[a] and stem:[b], otherwise the node is invalid. +The type `T` represents any supported type including custom types. It **MUST** be the same for the output value socket and the input value sockets stem:[a] and stem:[b]. + +===== Random + +[cols="1h,1,2"] +|=== +| Operation | `math/random` | Random value generation operation +| Output value sockets | `float value` | A pseudo-random number greater than or equal to zero and less than one +|=== + +[WARNING] +==== +This node is not intended for any workflows that require cryptographically secure random numbers. +==== + +The value of the output value socket `value` **MUST** be initialized to a random number on the first access. Any two accesses of the output value socket `value` **MUST** return the same value if there were no flow socket activations (of other nodes) between them. + +[NOTE] +.Example +==== +This means that, e.g., a `math/eq` node with both its input value sockets connected to the same `math/random` node always returns true. +==== + +The value of the output value socket `value` **SHOULD** be updated when accessed as a result of a new flow socket activation, including self-activations. + +[NOTE] +.Implementation Note +==== +At the current state of the Specification, only `flow/while` and `flow/for` nodes use self-activation of their input flow sockets. +==== ==== Angle and Trigonometry Nodes @@ -704,13 +906,13 @@ Node parameters specified as angle are assumed to be in units of radians. These all operate component-wise. The description is per component. -If any input value is _NaN_, the output value is also _NaN_. +If any input value component is _NaN_, the corresponding output value component is also _NaN_. ===== Degrees-To-Radians [cols="1h,1,2"] |=== -| Type | `math/rad` | Converts degrees to radians +| Operation | `math/rad` | Converts degrees to radians | Input value sockets | `floatN a` | Value in degrees | Output value sockets @@ -721,7 +923,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/deg` | Converts radians to degrees +| Operation | `math/deg` | Converts radians to degrees | Input value sockets | `floatN a` | Value in radians | Output value sockets @@ -732,7 +934,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/sin` | Sine function +| Operation | `math/sin` | Sine function | Input value sockets | `floatN a` | Angle | Output value sockets @@ -746,7 +948,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/cos` | Cosine function +| Operation | `math/cos` | Cosine function | Input value sockets | `floatN a` | Angle | Output value sockets @@ -760,7 +962,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/tan` | Tangent function +| Operation | `math/tan` | Tangent function | Input value sockets | `floatN a` | Angle | Output value sockets @@ -771,7 +973,7 @@ If any input value is _NaN_, the output value is also _NaN_. |=== [NOTE] -.Note +.Authoring Note ==== Since stem:[a] cannot exactly represent latexmath:[\pm\frac{\pi}{2}], this function does not return infinity. The closest representable argument values would likely produce latexmath:[\pm16331239353195370]. @@ -781,7 +983,7 @@ The closest representable argument values would likely produce latexmath:[\pm163 [cols="1h,1,2"] |=== -| Type | `math/asin` | Arcsine function +| Operation | `math/asin` | Arcsine function | Input value sockets | `floatN a` | Sine value | Output value sockets @@ -795,7 +997,7 @@ The closest representable argument values would likely produce latexmath:[\pm163 [cols="1h,1,2"] |=== -| Type | `math/acos` | Arccosine function +| Operation | `math/acos` | Arccosine function | Input value sockets | `floatN a` | Cosine value | Output value sockets @@ -809,7 +1011,7 @@ The closest representable argument values would likely produce latexmath:[\pm163 [cols="1h,1,2"] |=== -| Type | `math/atan` | Arctangent function +| Operation | `math/atan` | Arctangent function | Input value sockets | `floatN a` | Tangent value | Output value sockets @@ -820,7 +1022,7 @@ The closest representable argument values would likely produce latexmath:[\pm163 [cols="1h,1,2"] |=== -| Type | `math/atan2` | Arctangent 2 function +| Operation | `math/atan2` | Arctangent 2 function .2+| Input value sockets | `floatN a` | Y coordinate | `floatN b` | X coordinate @@ -828,19 +1030,19 @@ The closest representable argument values would likely produce latexmath:[\pm163 | `floatN value` | Angle between the positive X-axis and the vector from the stem:[(0, 0)] origin to the stem:[(X, Y)] point on a 2D plane |=== -Zero and infinity argument values are handled according to <> or <> standards. +Zero and infinity argument values **MUST** be handled according to the <> standard. ==== Hyperbolic Nodes These all operate component-wise. The description is per component. -If any input value is _NaN_, the output value is also _NaN_. +If any input value component is _NaN_, the corresponding output value component is also _NaN_. ===== Hyperbolic Sine [cols="1h,1,2"] |=== -| Type |`math/sinh`| Hyperbolic sine function +| Operation |`math/sinh`| Hyperbolic sine function | Input value sockets | `floatN a` | Hyperbolic angle value | Output value sockets @@ -851,7 +1053,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type |`math/cosh`| Hyperbolic cosine function +| Operation |`math/cosh`| Hyperbolic cosine function | Input value sockets | `floatN a` | Hyperbolic angle value | Output value sockets @@ -862,7 +1064,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type |`math/tanh`| Hyperbolic tangent function +| Operation |`math/tanh`| Hyperbolic tangent function | Input value sockets | `floatN a` | Hyperbolic angle value | Output value sockets @@ -873,7 +1075,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type |`math/asinh`| Inverse hyperbolic sine function +| Operation |`math/asinh`| Inverse hyperbolic sine function | Input value sockets | `floatN a` | Hyperbolic sine value | Output value sockets @@ -884,7 +1086,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type |`math/acosh`| Inverse hyperbolic cosine function +| Operation |`math/acosh`| Inverse hyperbolic cosine function | Input value sockets | `floatN a` | Hyperbolic cosine value | Output value sockets @@ -898,7 +1100,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type |`math/atanh`| Inverse hyperbolic tangent function +| Operation |`math/atanh`| Inverse hyperbolic tangent function | Input value sockets | `floatN a` | Hyperbolic tangent value | Output value sockets @@ -913,13 +1115,13 @@ If any input value is _NaN_, the output value is also _NaN_. These all operate component-wise. The description is per component. -If any input value is _NaN_, the output value is also _NaN_. +If any input value component is _NaN_, the corresponding output value component is also _NaN_. ===== Exponent [cols="1h,1,2"] |=== -| Type | `math/exp` | Exponent function +| Operation | `math/exp` | Exponent function | Input value sockets | `floatN a` | Power value | Output value sockets @@ -930,7 +1132,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/log` | Natural logarithm function +| Operation | `math/log` | Natural logarithm function | Input value sockets | `floatN a` | Argument value | Output value sockets @@ -945,7 +1147,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/log2` | Base-2 logarithm function +| Operation | `math/log2` | Base-2 logarithm function | Input value sockets | `floatN a` | Argument | Output value sockets @@ -960,7 +1162,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/log10` | Base-10 logarithm function +| Operation | `math/log10` | Base-10 logarithm function | Input value sockets | `floatN a` | Argument | Output value sockets @@ -975,7 +1177,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/sqrt` | Square root function +| Operation | `math/sqrt` | Square root function | Input value sockets | `floatN a` | Radicand | Output value sockets @@ -989,7 +1191,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/cbrt` | Cube root function +| Operation | `math/cbrt` | Cube root function | Input value sockets | `floatN a` | Radicand | Output value sockets @@ -1000,7 +1202,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/pow` | Power function +| Operation | `math/pow` | Power function .2+| Input value sockets | `floatN a` | Base | `floatN b` | Exponent @@ -1008,39 +1210,51 @@ If any input value is _NaN_, the output value is also _NaN_. | `floatN value` | stem:[a^b] |=== -Zero and infinity argument values are handled according to the <> standard. +Zero and infinity argument values **MUST** be handled according to the <> standard. ==== Vector Nodes -If any input value is _NaN_, the output value is also _NaN_. +If any input value component is _NaN_, the output value is _NaN_ or a vector of NaNs. ===== Length [cols="1h,1,2"] |=== -| Type | `math/length` | Vector length +| Operation | `math/length` | Vector length | Input value sockets | `float{2\|3\|4} a` | Vector | Output value sockets | `float value` | Length of stem:[a], e.g., stem:[sqrt(a_x^2 + a_y^2)] for `float2` |=== +[TIP] +.Implementation Tip +==== +This is implementable in ECMAScript via the following expression: +[source,js] +---- +Math.hypot(...a) +---- +==== + ===== Normalize [cols="1h,1,2"] |=== -| Type | `math/normalize` | Vector normalization +| Operation | `math/normalize` | Vector normalization | Input value sockets | `float{2\|3\|4} a` | Vector | Output value sockets -| `floatN value` | Vector in the same direction as stem:[a] but with a unit length, e.g., stem:[a/sqrt(a_x^2 + a_y^2)] for `float2` +| `floatN value` | Vector in the same direction as stem:[a] but with a unit length, e.g., latexmath:[\dfrac{\vec{a}}{\sqrt{a_x^2 + a_y^2}}] for `float2` |=== +Normalizing a zero-length vector **MUST** return a NaN vector. + ===== Dot Product [cols="1h,1,2"] |=== -| Type | `math/dot` | Dot product +| Operation | `math/dot` | Dot product .2+| Input value sockets | `float{2\|3\|4} a` | First vector | `float{2\|3\|4} b` | Second vector of the same type as stem:[a] @@ -1048,11 +1262,13 @@ If any input value is _NaN_, the output value is also _NaN_. | `float value` | Sum of per-component products of stem:[a] and stem:[b], e.g., stem:[a_x * b_x + a_y * b_y] for `float2` |=== +Both input value sockets **MUST** have the same type. + ===== Cross Product [cols="1h,1,2"] |=== -| Type | `math/cross` | Cross product +| Operation | `math/cross` | Cross product .2+| Input value sockets | `float3 a` | Vector | `float3 b` | Vector @@ -1064,7 +1280,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/rotate2d` | 2D rotation +| Operation | `math/rotate2d` | 2D rotation .2+| Input value sockets | `float2 a` | Vector to rotate | `float b` | Angle in radians @@ -1076,7 +1292,7 @@ If any input value is _NaN_, the output value is also _NaN_. [cols="1h,1,2"] |=== -| Type | `math/rotate3d` | 3D rotation +| Operation | `math/rotate3d` | 3D rotation .3+| Input value sockets | `float3 a` | Vector to rotate | `float3 b` | Vector representing an axis to rotate around @@ -1085,142 +1301,170 @@ If any input value is _NaN_, the output value is also _NaN_. | `float3 value` | Vector stem:[a] rotated around vector stem:[b] counter-clockwise by stem:[c] |=== -If the vector stem:[b] is not unit, rotation results may be undefined. +If the vector stem:[b] is not unit, rotation results **MAY** be undefined. ===== Transform [cols="1h,1,2"] |=== -| Type | `math/transform` | Vector transformation +| Operation | `math/transform` | Vector transformation .2+| Input value sockets -| `float4 a` | Vector to transform -| `float4x4 b` | Transformation matrix +| `float2 a` | Vector to transform +| `float2x2 b` | Transformation matrix | Output value sockets -| `float4 value` | Transformed vector +| `float2 value` | Transformed vector |=== -===== Combine - [cols="1h,1,2"] |=== -| Type | `math/combine2` | Combine two floats into a two-component vector +| Operation | `math/transform` | Vector transformation .2+| Input value sockets -| `float a` | First component -| `float b` | Second component +| `float3 a` | Vector to transform +| `float3x3 b` | Transformation matrix | Output value sockets -| `float2 value` | Vector +| `float3 value` | Transformed vector |=== [cols="1h,1,2"] |=== -| Type | `math/combine3` | Combine three floats into a three-component vector -.3+| Input value sockets -| `float a` | First component -| `float b` | Second component -| `float c` | Third component +| Operation | `math/transform` | Vector transformation +.2+| Input value sockets +| `float4 a` | Vector to transform +| `float4x4 b` | Transformation matrix | Output value sockets -| `float3 value` | Vector +| `float4 value` | Transformed vector |=== +==== Matrix Nodes + +===== Transpose + [cols="1h,1,2"] |=== -| Type | `math/combine4` | Combine four floats into a four-component vector -.4+| Input value sockets -| `float a` | First component -| `float b` | Second component -| `float c` | Third component -| `float d` | Fourth component +| Operation | `math/transpose` | Transpose operation +| Input value sockets +| `float{2x2\|3x3\|4x4} a` | Matrix to transpose | Output value sockets -| `float4 value` | Vector +| `float{2x2\|3x3\|4x4} value` | Matrix that is the transpose of stem:[a] |=== -===== Extract +The input and output value sockets have the same type. + +===== Determinant [cols="1h,1,2"] |=== -| Type | `math/extract2` | Extract two floats from a two-component vector +| Operation | `math/determinant` | Dot product | Input value sockets -| `float2 a` | Vector -.2+| Output value sockets -| `float 0` | First component -| `float 1` | Second component +| `float{2x2\|3x3\|4x4} a` | Matrix +| Output value sockets +| `float value` | Determinant of stem:[a] |=== +===== Inverse + [cols="1h,1,2"] |=== -| Type | `math/extract3` | Extract three floats from a three-component vector +| Operation | `math/inverse` | Inverse operation | Input value sockets -| `float3 a` | Vector -.3+| Output value sockets -| `float 0` | First component -| `float 1` | Second component -| `float 2` | Third component +| `float{2x2\|3x3\|4x4} a` | Matrix to inverse +| Output value sockets +| `float{2x2\|3x3\|4x4} value` | Matrix that is the inverse of stem:[a] |=== +The input and output value sockets have the same type. + +===== Multiplication + [cols="1h,1,2"] |=== -| Type | `math/extract4` | Extract four floats from a four-component vector -| Input value sockets -| `float4 a` | Vector -.4+| Output value sockets -| `float 0` | First component -| `float 1` | Second component -| `float 2` | Third component -| `float 3` | Fourth component +| Operation | `math/matmul` | Matrix multiplication operation +.2+| Input value sockets +| `float{2x2\|3x3\|4x4} a` | First matrix +| `float{2x2\|3x3\|4x4} b` | Second matrix +| Output value sockets +| `float{2x2\|3x3\|4x4} value` | Matrix product |=== -==== Matrix Nodes +Both input value sockets **MUST** have the same type. -===== Transpose +The output value socket has the same type as the input value sockets. + +[NOTE] +.Authoring Note +==== +See `math/mul` for per-component multiplication. +==== + +==== Swizzle Nodes + +===== Combine [cols="1h,1,2"] |=== -| Type | `math/transpose` | Transpose operation -| Input value sockets -| `float4x4 a` | Matrix to transpose +| Operation | `math/combine2` | Combine two floats into a two-component vector +.2+| Input value sockets +| `float a` | First component +| `float b` | Second component | Output value sockets -| `float4x4 value` | Matrix that is the transpose of stem:[a] +| `float2 value` | Vector |=== -===== Determinant - [cols="1h,1,2"] |=== -| Type | `math/determinant` | Dot product -| Input value sockets -| `float4x4 a` | Matrix +| Operation | `math/combine3` | Combine three floats into a three-component vector +.3+| Input value sockets +| `float a` | First component +| `float b` | Second component +| `float c` | Third component | Output value sockets -| `float value` | Determinant of stem:[a] +| `float3 value` | Vector |=== -===== Inverse - [cols="1h,1,2"] |=== -| Type | `math/inverse` | Inverse operation -| Input value sockets -| `float4x4 a` | Matrix to inverse +| Operation | `math/combine4` | Combine four floats into a four-component vector +.4+| Input value sockets +| `float a` | First component +| `float b` | Second component +| `float c` | Third component +| `float d` | Fourth component | Output value sockets -| `float4x4 value` | Matrix that is the inverse of stem:[a] +| `float4 value` | Vector |=== -===== Multiplication - [cols="1h,1,2"] |=== -| Type | `math/matmul` | Matrix multiplication operation -.2+| Input value sockets -| `float4x4 a` | First matrix -| `float4x4 b` | Second matrix +| Operation | `math/combine2x2` | Combine 4 floats into a 2x2 matrix +.4+| Input value sockets +| `float a` | First row, first column element +| `float b` | Second row, first column element +| `float c` | First row, second column element +| `float d` | Second row, second column element | Output value sockets -| `float4x4 value` | Matrix product +| `float2x2 value` | Matrix |=== -===== Combine +[cols="1h,1,2"] +|=== +| Operation | `math/combine3x3` | Combine 9 floats into a 3x3 matrix +.9+| Input value sockets +| `float a` | First row, first column element +| `float b` | Second row, first column element +| `float c` | Third row, first column element +| `float d` | First row, second column element +| `float e` | Second row, second column element +| `float f` | Third row, second column element +| `float g` | First row, third column element +| `float h` | Second row, third column element +| `float i` | Third row, third column element +| Output value sockets +| `float3x3 value` | Matrix +|=== [cols="1h,1,2"] |=== -| Type | `math/combine4x4` | Combine 16 floats into a 4x4 matrix +| Operation | `math/combine4x4` | Combine 16 floats into a 4x4 matrix .16+| Input value sockets | `float a` | First row, first column element | `float b` | Second row, first column element @@ -1246,19 +1490,81 @@ If the vector stem:[b] is not unit, rotation results may be undefined. [cols="1h,1,2"] |=== -| Type | `math/extract4x4` | Extract 16 floats from a 4x4 matrix +| Operation | `math/extract2` | Extract two floats from a two-component vector | Input value sockets -| `float4x4 a` | Matrix -.16+| Output value sockets -| `float 0` | First row, first column element -| `float 1` | Second row, first column element -| `float 2` | Third row, first column element -| `float 3` | Fourth row, first column element -| `float 4` | First row, second column element -| `float 5` | Second row, second column element -| `float 6` | Third row, second column element -| `float 7` | Fourth row, second column element -| `float 8` | First row, third column element +| `float2 a` | Vector +.2+| Output value sockets +| `float 0` | First component +| `float 1` | Second component +|=== + +[cols="1h,1,2"] +|=== +| Operation | `math/extract3` | Extract three floats from a three-component vector +| Input value sockets +| `float3 a` | Vector +.3+| Output value sockets +| `float 0` | First component +| `float 1` | Second component +| `float 2` | Third component +|=== + +[cols="1h,1,2"] +|=== +| Operation | `math/extract4` | Extract four floats from a four-component vector +| Input value sockets +| `float4 a` | Vector +.4+| Output value sockets +| `float 0` | First component +| `float 1` | Second component +| `float 2` | Third component +| `float 3` | Fourth component +|=== + +[cols="1h,1,2"] +|=== +| Operation | `math/extract2x2` | Extract 4 floats from a 2x2 matrix +| Input value sockets +| `float2x2 a` | Matrix +.4+| Output value sockets +| `float 0` | First row, first column element +| `float 1` | Second row, first column element +| `float 2` | First row, second column element +| `float 3` | Second row, second column element +|=== + +[cols="1h,1,2"] +|=== +| Operation | `math/extract3x3` | Extract 9 floats from a 3x3 matrix +| Input value sockets +| `float3x3 a` | Matrix +.9+| Output value sockets +| `float 0` | First row, first column element +| `float 1` | Second row, first column element +| `float 2` | Third row, first column element +| `float 3` | First row, second column element +| `float 4` | Second row, second column element +| `float 5` | Third row, second column element +| `float 6` | First row, third column element +| `float 7` | Second row, third column element +| `float 8` | Third row, third column element +|=== + +[cols="1h,1,2"] +|=== +| Operation | `math/extract4x4` | Extract 16 floats from a 4x4 matrix +| Input value sockets +| `float4x4 a` | Matrix +.16+| Output value sockets +| `float 0` | First row, first column element +| `float 1` | Second row, first column element +| `float 2` | Third row, first column element +| `float 3` | Fourth row, first column element +| `float 4` | First row, second column element +| `float 5` | Second row, second column element +| `float 6` | Third row, second column element +| `float 7` | Fourth row, second column element +| `float 8` | First row, third column element | `float 9` | Second row, third column element | `float 10` | Third row, third column element | `float 11` | Fourth row, third column element @@ -1276,7 +1582,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/abs` | Absolute value operation +| Operation | `math/abs` | Absolute value operation | Input value sockets | `int a` | Argument | Output value sockets @@ -1288,13 +1594,13 @@ All inputs to these nodes are two's complement 32-bit signed integers. As this node is defined in terms of the negation node (see below), the absolute value of `-2147483648` is `-2147483648`. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -Math.abs(a)|0 +Math.abs(a) | 0 ---- ==== @@ -1302,7 +1608,7 @@ Math.abs(a)|0 [cols="1h,1,2"] |=== -| Type | `math/sign` | Sign operation +| Operation | `math/sign` | Sign operation | Input value sockets | `int a` | Argument | Output value sockets @@ -1317,7 +1623,7 @@ Math.abs(a)|0 [cols="1h,1,2"] |=== -| Type | `math/neg` | Negation operation +| Operation | `math/neg` | Negation operation | Input value sockets | `int a` | Argument | Output value sockets @@ -1326,13 +1632,13 @@ Math.abs(a)|0 Negating `-2147483648` **MUST** return `-2147483648`. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -(-a)|0 +(-a) | 0 ---- ==== @@ -1340,7 +1646,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `math/add` | Addition operation +| Operation | `math/add` | Addition operation .2+| Input value sockets | `int a` | First addend | `int b` | Second addend @@ -1355,13 +1661,13 @@ Arithmetic overflow **MUST** wrap around, for example: 2147483647 + 1 == -2147483648 ---- -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -(a + b)|0 +(a + b) | 0 ---- ==== @@ -1369,7 +1675,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `math/sub` | Subtraction operation +| Operation | `math/sub` | Subtraction operation .2+| Input value sockets | `int a` | Minuend | `int b` | Subtrahend @@ -1384,13 +1690,13 @@ Arithmetic overflow **MUST** wrap around, for example: -2147483648 - 1 == 2147483647 ---- -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -(a - b)|0 +(a - b) | 0 ---- ==== @@ -1398,7 +1704,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `math/mul` | Multiplication operation +| Operation | `math/mul` | Multiplication operation .2+| Input value sockets | `int a` | First factor | `int b` | Second factor @@ -1415,8 +1721,8 @@ Arithmetic overflow **MUST** wrap around, for example: -2147483648 * (-1) == -2147483648 ---- -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] @@ -1429,7 +1735,7 @@ Math.imul(a, b) [cols="1h,1,2"] |=== -| Type | `math/div` | Division operation +| Operation | `math/div` | Division operation .2+| Input value sockets | `int a` | Dividend | `int b` | Divisor @@ -1448,13 +1754,13 @@ Arithmetic overflow is defined as follows: -2147483648 / (-1) == -2147483648 ---- -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -(a / b)|0 +(a / b) | 0 ---- ==== @@ -1462,7 +1768,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `math/rem` | Remainder operation +| Operation | `math/rem` | Remainder operation .2+| Input value sockets | `int a` | Dividend | `int b` | Divisor @@ -1473,13 +1779,13 @@ This is implementable in ECMAScript via the following expression: \end{cases}] |=== -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -(a % b)|0 +(a % b) | 0 ---- ==== @@ -1487,7 +1793,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `math/min` | Minimum operation +| Operation | `math/min` | Minimum operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1499,7 +1805,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `math/max` | Maximum operation +| Operation | `math/max` | Maximum operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1511,7 +1817,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `math/clamp` | Clamp operation +| Operation | `math/clamp` | Clamp operation .3+| Input value sockets | `int a` | Value to clamp | `int b` | First boundary @@ -1521,7 +1827,7 @@ This is implementable in ECMAScript via the following expression: |=== [NOTE] -.Note +.Authoring Note ==== This operation correctly handles a case when stem:[b] is greater than stem:[c]. ==== @@ -1534,7 +1840,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/eq` | Equality operation +| Operation | `math/eq` | Equality operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1546,7 +1852,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/lt` | Less than operation +| Operation | `math/lt` | Less than operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1558,7 +1864,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/le` | Less than or equal to operation +| Operation | `math/le` | Less than or equal to operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1570,7 +1876,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/gt` | Greater than operation +| Operation | `math/gt` | Greater than operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1582,7 +1888,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/ge` | Greater than or equal operation +| Operation | `math/ge` | Greater than or equal operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1598,7 +1904,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/not` | Bitwise NOT operation +| Operation | `math/not` | Bitwise NOT operation | Input value sockets | `int a` | Argument | Output value sockets @@ -1609,7 +1915,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/and` | Bitwise AND operation +| Operation | `math/and` | Bitwise AND operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1621,7 +1927,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/or` | Bitwise OR operation +| Operation | `math/or` | Bitwise OR operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1633,7 +1939,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/xor` | Bitwise XOR operation +| Operation | `math/xor` | Bitwise XOR operation .2+| Input value sockets | `int a` | First argument | `int b` | Second argument @@ -1645,7 +1951,7 @@ All inputs to these nodes are two's complement 32-bit signed integers. [cols="1h,1,2"] |=== -| Type | `math/asr` | Right Shift +| Operation | `math/asr` | Right Shift .2+| Input value sockets | `int a` | Value to be shifted | `int b` | Number of bits to shift by @@ -1659,7 +1965,7 @@ Only the lowest 5 bits of stem:[b] are considered, i.e., its effective range is [cols="1h,1,2"] |=== -| Type | `math/lsl` | Left Shift +| Operation | `math/lsl` | Left Shift .2+| Input value sockets | `int a` | Value to be shifted | `int b` | Number of bits to shift by @@ -1673,7 +1979,7 @@ Only the lowest 5 bits of stem:[b] are considered, i.e., its effective range is [cols="1h,1,2"] |=== -| Type | `math/clz` | Count leading zeros operation +| Operation | `math/clz` | Count leading zeros operation | Input value sockets | `int a` | Argument | Output value sockets @@ -1682,8 +1988,8 @@ Only the lowest 5 bits of stem:[b] are considered, i.e., its effective range is If stem:[a] is 0, the operation returns 32; if stem:[a] is negative, the operation returns 0. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] @@ -1696,7 +2002,7 @@ Math.clz32(a) [cols="1h,1,2"] |=== -| Type | `math/ctz` | Count trailing zeros operation +| Operation | `math/ctz` | Count trailing zeros operation | Input value sockets | `int a` | Argument | Output value sockets @@ -1705,13 +2011,13 @@ Math.clz32(a) If stem:[a] is 0, the operation returns 32. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -a ? (31 - Math.clz32(a & -a)) : 32; +a ? (31 - Math.clz32(a & -a)) : 32 ---- ==== @@ -1719,7 +2025,7 @@ a ? (31 - Math.clz32(a & -a)) : 32; [cols="1h,1,2"] |=== -| Type | `math/popcnt` | Count set bits operation +| Operation | `math/popcnt` | Count set bits operation | Input value sockets | `int a` | Argument | Output value sockets @@ -1734,7 +2040,7 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu [cols="1h,1,2"] |=== -| Type | `math/eq` | Equality operation +| Operation | `math/eq` | Equality operation .2+| Input value sockets | `bool a` | First argument | `bool b` | Second argument @@ -1746,7 +2052,7 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu [cols="1h,1,2"] |=== -| Type | `math/not` | Boolean NOT operation +| Operation | `math/not` | Boolean NOT operation | Input value sockets | `bool a` | Argument | Output value sockets @@ -1757,7 +2063,7 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu [cols="1h,1,2"] |=== -| Type | `math/and` | Boolean AND operation +| Operation | `math/and` | Boolean AND operation .2+| Input value sockets | `bool a` | First argument | `bool b` | Second argument @@ -1769,7 +2075,7 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu [cols="1h,1,2"] |=== -| Type | `math/or` | Boolean OR operation +| Operation | `math/or` | Boolean OR operation .2+| Input value sockets | `bool a` | First argument | `bool b` | Second argument @@ -1781,7 +2087,7 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu [cols="1h,1,2"] |=== -| Type | `math/xor` | Boolean XOR operation +| Operation | `math/xor` | Boolean XOR operation .2+| Input value sockets | `bool a` | First argument | `bool b` | Second argument @@ -1797,20 +2103,20 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu [cols="1h,1,2"] |=== -| Type | `type/boolToInt` | Boolean to integer conversion +| Operation | `type/boolToInt` | Boolean to integer conversion | Input value sockets | `bool a` | Argument | Output value sockets | `int value` | stem:[1] if stem:[a] is true; stem:[0] otherwise |=== -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -a|0 +a | 0 ---- ==== @@ -1818,15 +2124,15 @@ a|0 [cols="1h,1,2"] |=== -| Type | `type/boolToFloat` | Boolean to float conversion +| Operation | `type/boolToFloat` | Boolean to float conversion | Input value sockets | `bool a` | Argument | Output value sockets | `float value` | stem:[1] if stem:[a] is true; stem:[0] otherwise |=== -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] @@ -1841,15 +2147,15 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `type/intToBool` | Integer to boolean conversion +| Operation | `type/intToBool` | Integer to boolean conversion | Input value sockets | `int a` | Argument | Output value sockets | `bool value` | True if stem:[a] is not equal to zero; false otherwise |=== -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] @@ -1862,7 +2168,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `type/intToFloat` | Integer to float conversion +| Operation | `type/intToFloat` | Integer to float conversion | Input value sockets | `int a` | Argument | Output value sockets @@ -1873,8 +2179,8 @@ Since floating-point values have double precision, this conversion **MUST** be l This operation **MUST NOT** produce negative zero. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This operation is no-op in ECMAScript. ==== @@ -1885,15 +2191,15 @@ This operation is no-op in ECMAScript. [cols="1h,1,2"] |=== -| Type | `type/floatToBool` | Float to boolean conversion +| Operation | `type/floatToBool` | Float to boolean conversion | Input value sockets | `float a` | Argument | Output value sockets | `bool value` | False if stem:[a] is NaN or equal to zero; true otherwise |=== -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] @@ -1906,7 +2212,7 @@ This is implementable in ECMAScript via the following expression: [cols="1h,1,2"] |=== -| Type | `type/floatToInt` | Float to integer conversion +| Operation | `type/floatToInt` | Float to integer conversion | Input value sockets | `float a` | Argument | Output value sockets @@ -1914,17 +2220,17 @@ This is implementable in ECMAScript via the following expression: |=== 1. If the stem:[a] input value is zero, infinite, or NaN, return zero and skip the next steps. -2. Let stem:[t] be stem:[a] with its fractional part removed by rounding towards zero. +2. Let stem:[t] be stem:[a] with its fractional part removed by truncating towards zero. 3. Let stem:[k] be a value of the same sign as stem:[t] such that its absolute value is less than stem:[2^32] and stem:[k] is equal to stem:[t - q * 2^32] for some integer stem:[q]. 4. If stem:[k] is greater than or equal to stem:[2^31], return stem:[k - 2^32]; otherwise return stem:[k]. -[NOTE] -.Note +[TIP] +.Implementation Tip ==== This is implementable in ECMAScript via the following expression: [source,js] ---- -a|0 +a | 0 ---- ==== @@ -1936,22 +2242,24 @@ a|0 [cols="1h,1,2"] |=== -| Type | `flow/sequence` | Sequentially activate all connected output flows +| Operation | `flow/sequence` | Sequentially activate all connected output flows | Input flow sockets | `in` | The entry flow into this node | Output flow sockets -| `` | One or more output flows; their names are purely informative +| `` | Zero or more output flows; their ids define the order of activation |=== This node has no internal state. -When the `in` input flow is activated, all output flows are activated one by one in the order they are defined in JSON. +When the `in` input flow is activated, all output flows are activated sequentially (each output flow is activated after the previous output flow completes) in the order as described in the <> section. + +If the number of output flow sockets (as present in JSON) exceeds an implementation-defined limit, the graph **MUST** be rejected. ===== Branch [cols="1h,1,2"] |=== -| Type | `flow/branch` | Branch the execution flow based on a condition +| Operation | `flow/branch` | Branch the execution flow based on a condition | Input flow sockets | `in` | The entry flow into this node | Input value sockets @@ -1969,37 +2277,55 @@ The `condition` input value is evaluated each time the node is executed. [cols="1h,1,2"] |=== -| Type | `flow/switch` | Conditionally route the execution flow to one of the outputs +| Operation | `flow/switch` | Conditionally route the execution flow to one of the outputs | Configuration -| `int[] cases` | The cases on which to perform the switch on; values **MUST** be unique 32-bit signed integers; at least one value **MUST** be present +| `int[] cases` | The cases on which to perform the switch; empty in the default configuration | Input flow sockets | `in` | The entry flow into this node | Input value sockets | `int selection` | The value on which the switch operates .2+| Output flow sockets -| `` | The output flow, `case` is an integer decimal number present in the `cases` configuration array -| `default` | The output flow used when the `selection` input value is not present in the `cases` configuration array +| `` | Zero or more output flows; `` is an integer decimal number +| `default` | The output flow activated when the `selection` input value is not present in the `cases` configuration array |=== -The node has one or more `` output flow sockets named as decimal integers equal to the elements of the `cases` configuration array. Encoded as JSON strings, these output flow socket names **MUST** contain only decimal integers (ASCII characters `0x30 ... 0x39`) and optionally a leading minus sign (ASCII character `0x2D`); other characters and leading zeros are not allowed. +The node has zero or more `` output flow sockets corresponding to the elements of the `cases` configuration array. + +In the default configuration, the `cases` configuration array is empty and the node has only the `default` output flow socket. + +The following procedure defines output flow sockets generation from the provided configuration: + +1. If the `cases` configuration property is not present or if it is not an array, ignore it and use the default configuration. +2. If the `cases` configuration property is present and it is an array, then for each array element `C`: +.. if `C` is not a literal number or if it is not exactly representable as a 32-bit signed integer, ignore the `cases` property and use the default configuration; +.. convert `C` to a base-10 string representation `S` containing only decimal integers (ASCII characters `0x30 ... 0x39`) and a leading minus sign (ASCII character `0x2D`) if `C` is negative; extra leading zeros **MUST NOT** be present; +.. add a flow socket `S` to the set of the output flow sockets of this node or ignore it if an output flow socket with the same id has been already added. +3. Proceed with the generated output flow sockets. + +For example: -For example, if the `cases` configuration array is `[-1, 0, 1]`, the output socket names are exactly `"-1"`, `"0"`, and `"1"`. +* if the `cases` configuration array is `[0.5, 1]`, the default configuration is used because `0.5` is not representable as a 32-bit signed integer; +* if the `cases` configuration array is `[-2147483649, 0]`, the default configuration is used because `-2147483649` is not representable as a 32-bit signed integer; +* if the `cases` configuration array is `[-1.0, 0, 1]`, the output socket ids are exactly `"-1"`, `"0"`, and `"1"` because `-1.0` is equal to an integer `-1`; +* if the `cases` configuration array is `[0.1e1, 2, 2]`, the output socket ids are exactly `"1"` and `"2"` because `0.1e1` is equal to an integer `1` and the duplicate entry is ignored. This node has no internal state. +If the number of output flow sockets (as present in JSON) exceeds an implementation-defined limit, the graph **MUST** be rejected. + When the `in` input flow is activated: 1. Evaluate the `selection` input value. 2. If the `cases` configuration array does not contain the `selection` input value: .. activate the `default` output flow if it is connected. 3. If the `cases` configuration array contains the `selection` input value: -.. activate the output flow named `` if it is connected. +.. activate the output flow with the matching id if it is connected. ===== While Loop [cols="1h,1,2"] |=== -| Type | `flow/while` | Repeatedly activate the output flow based on a condition +| Operation | `flow/while` | Repeatedly activate the output flow based on a condition | Input flow sockets | `in` | The entry flow into this node | Input value sockets @@ -2013,10 +2339,10 @@ This node has no internal state. When the `in` input flow is activated: -1. Evaluate the `condition`. The `condition` **MUST NOT** statically evaluate to true, otherwise the node is invalid. +1. Evaluate the `condition` input value. 2. If the `condition` is true, .. activate the `loopBody` output flow; -.. after completion of the `loopBody` output flow, goto step 1. +.. after completion of the `loopBody` output flow, self-activate the `in` input flow. 3. If the `condition` is false, .. activate the `completed` output flow. @@ -2024,9 +2350,9 @@ When the `in` input flow is activated: [cols="1h,1,2"] |=== -| Type | `flow/for` | Repeatedly activate the output flow based on an incrementing index value +| Operation | `flow/for` | Repeatedly activate the output flow based on an incrementing index value | Configuration -| `int initialIndex` | The index value before the loop starts +| `int initialIndex` | The index value before the loop starts; zero in the default configuration | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets @@ -2039,6 +2365,10 @@ When the `in` input flow is activated: | `int index` | The current index value if the node has ever been activated, `initialIndex` otherwise |=== +In the default configuration, the `initialIndex` configuration value is zero. + +If the `initialIndex` configuration property is not provided by the behavior graph, if it is not a literal number, or if its value is not exactly representable as a 32-bit signed integer, the default configuration **MUST** be used. + The internal state of this node consists of one 32-bit signed integer value `index` initialized to `initialIndex`. When the `in` input flow is activated: @@ -2049,7 +2379,7 @@ When the `in` input flow is activated: 4. If `index` is less than the `endIndex` input value, .. activate the `loopBody` output flow; .. after completion of the `loopBody` output flow, increment the `index` value by 1; -.. goto step 3. +.. self-activate the `in` input flow and goto step 3, i.e., skip steps 1 and 2; 5. If the `index` value is greater than or equal to the `endIndex` input value, .. activate the `completed` output flow. @@ -2057,7 +2387,7 @@ When the `in` input flow is activated: [cols="1h,1,2"] |=== -| Type | `flow/doN` | Activate the output flow no more than N times +| Operation | `flow/doN` | Activate the output flow no more than N times .2+| Input flow sockets | `in` | The entry flow into this node | `reset` | When this flow is activated, the `currentCount` value is reset to 0 @@ -2086,22 +2416,28 @@ When the `in` input flow is activated: [cols="1h,1,2"] |=== -| Type | `flow/multiGate` | Route the execution flow to one of the outputs sequentially or randomly +| Operation | `flow/multiGate` | Route the execution flow to one of the outputs sequentially or randomly .2+| Configuration -| `bool isRandom` | If set to true, output flows are executed in random order, picking a random not used output flow each time until all are done -| `bool isLoop` | If set to true, the outputs will repeat in a loop continuously after all are done +| `bool isRandom` | If set to true, output flows are activated in random order, picking a random not used output flow each time until all are done; false in the default configuration +| `bool isLoop` | If set to true, output flow activations will repeat in a loop continuously after all are done; false in the default configuration .2+| Input flow sockets | `in` | The entry flow into this node | `reset` | When this flow is activated, the `lastIndex` value is reset to -1 and all outputs are marked as not used | Output flow sockets -| `` | One or more output flows; their names are purely informative +| `` | Zero or more output flows; their ids define the order of activation | Output value sockets | `int lastIndex` | The index of the last used output; `-1` if the node has not been activated |=== +If the number of output flow sockets (as present in JSON) exceeds an implementation-defined limit, the graph **MUST** be rejected. + +In the default configuration, both `isRandom` and `isLoop` configuration values are false. + +If any of the two configuration properties is not provided by the behavior graph or if it is not a literal boolean, the default configuration for both properties **MUST** be used. + The internal state of this node consists of one 32-bit signed integer value `lastIndex` initialized to -1 and an array of booleans with all values initialized to false representing used output flows. The size of the boolean array is equal to the number of output flows. -The output flows are ordered as they are defined in JSON. +For the purposes of the `in` input flow operation, the output flows are assigned internal indices starting with zero in the order as described in the <> section. When the `reset` input flow is activated: @@ -2134,9 +2470,9 @@ When the `isRandom` and `isLoop` configuration values are true, the output flow [cols="1h,1,2"] |=== -| Type | `flow/waitAll` | Activate the output flow when all input flows have been activated at least once. +| Operation | `flow/waitAll` | Activate the output flow when all input flows have been activated at least once. | Configuration -| `int inputFlows` | The number of input flows, **MUST** be greater than 0 and less than or equal to 2147483647 +| `int inputFlows` | The number of input flows; zero in the default configuration .2+| Input flow sockets | `` | The `i`-th input flow, `i` is a non-negative integer decimal number less than the `inputFlows` configuration value | `reset` | When this flow is activated, all input flows are marked as unused @@ -2147,32 +2483,42 @@ When the `isRandom` and `isLoop` configuration values are true, the output flow | `int remainingInputs` | The number of not yet activated input flows |=== -The node has one or more input flow sockets named as sequential non-negative decimal integers depending on the `inputFlows` configuration value. Encoded as JSON strings, these input flow socket names contain only decimal integers (ASCII characters `0x30 ... 0x39`); other characters and leading zeros are not allowed. +The node has from zero to 64 input flow sockets with ids assigned sequential non-negative integer decimal numbers depending on the `inputFlows` configuration value. Encoded as base-10 strings, these input flow socket ids contain only decimal integers (ASCII characters `0x30 ... 0x39`); other characters and leading zeros are not used. + +For example, if `inputFlows` is 3, the input flow socket ids are `"0"`, `"1"`, and `"2"` exactly. + +In the default configuration, the `inputFlows` configuration value is zero. -For example, if `inputFlows` is 3, the input socket names are `"0"`, `"1"`, and `"2"` exactly. +If the `inputFlows` configuration property is not provided by the behavior graph, if it is not a literal number, if its value is not exactly representable as an integer, if it is negative, or if it is greater than 64, the default configuration **MUST** be used. -The internal state of this node consists of one 32-bit signed integer value `remainingInputs` initialized to the number of connected input flows not including the `reset` input flow and a map of booleans with all values initialized to false representing used input flow sockets. The size of the boolean map is equal to the initial `remainingInputs` value. +The internal state of this node consists of one 32-bit signed integer value `remainingInputs` initialized to the value of the `inputFlows` configuration property and an array of booleans with all values initialized to false representing activated input flow sockets. The size of the boolean array is equal to the value of the `inputFlows` configuration property. When the `reset` input flow is activated: -1. Reset `remainingInputs` to the number of connected input flows not including the `reset` input flow. -2. Mark all connected input flows as not used in the boolean map. +1. Reset `remainingInputs` to the value of the `inputFlows` configuration property. +2. Mark all input flows as not activated in the boolean array. When any of the `` input flows is activated: -1. If the ``-th input flow is not marked as used in the boolean map: -.. mark the ``-th input flow as used in the boolean map. -.. decrement the `remainingInputs` value by 1; +1. If the ``-th input flow is not marked as activated in the boolean array: +.. mark the ``-th input flow as activated in the boolean array; +.. decrement the `remainingInputs` value by 1. 2. If the `remainingInputs` value is zero: .. activate the `completed` output flow. 3. If the `remainingInputs` value is not zero: .. activate the `out` output flow. +[NOTE] +.Authoring Note +==== +In the default configuration, this node has only the `reset` input flow, the `remainingInputs` output value is always zero, and the output flows are never activated. +==== + ===== Throttle [cols="1h,1,2"] |=== -| Type | `flow/throttle` | Activate the output flow unless it has been activated less than a certain time ago +| Operation | `flow/throttle` | Activate the output flow unless it has been activated less than a certain time ago .2+| Input flow sockets | `in` | The entry flow into this node | `reset` | When this flow is activated, the output flow throttling state is reset @@ -2215,7 +2561,7 @@ When the `in` input flow is activated: [cols="1h,1,2"] |=== -| Type | `flow/setDelay` | Schedule the output flow activation after a certain delay +| Operation | `flow/setDelay` | Schedule the output flow activation after a certain delay .2+| Input flow sockets | `in` | The entry flow into this node | `cancel` | When this flow is activated, all delayed activations scheduled by this node are cancelled @@ -2240,12 +2586,12 @@ When the `in` input flow is activated: 1. Evaluate the `duration` input value. 2. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type, .. activate the `err` output flow and skip the next steps. -3. If scheduling a new activation exceeds any implementation-specific limit, +3. If scheduling a new activation exceeds an implementation-specific limit on the maximum number of simultaneous delays, .. activate the `err` output flow and skip the next steps. 4. Let _activationTime_ be an implementation-defined high-precision time value equal to the sum of the current time value and the `duration` input value converted to the same time type. 5. If _activationTime_ is not valid according to implementation-specific validation rules, e.g., it exceeds an internal threshold value, .. activate the `err` output flow and skip the next steps. -6. Set `lastDelayIndex` to a positive value representing the delayed flow activation being scheduled. This value **MUST** be unique across all previous activations of all `flow/setDelay` nodes of the graph. +6. Set `lastDelayIndex` to a non-negative value representing the delayed flow activation being scheduled. This value **MUST** be unique across all previous activations of all `flow/setDelay` nodes of the graph. 7. Push the value of `lastDelayIndex` to the graph and node arrays of activation indices. 8. Schedule the following actions at the _activationTime_ time: .. Removal of the activation index value from both arrays of activation indices. @@ -2263,7 +2609,7 @@ When the `cancel` input flow is activated: [cols="1h,1,2"] |=== -| Type | `flow/cancelDelay` | Cancel a previously scheduled output flow activation +| Operation | `flow/cancelDelay` | Cancel a previously scheduled output flow activation | Input flow sockets | `in` | The entry flow into this node | Input value sockets @@ -2291,14 +2637,21 @@ Non-positive or not existing delay index values **MUST NOT** cause any runtime e [cols="1h,1,2"] |=== -| Type | `variable/get` | Get a custom variable value +| Operation | `variable/get` | Get a custom variable value | Configuration | `int variable` | The custom variable index | Output value sockets -| `T value` | The custom variable value +| `T value` | The custom variable value |=== -This node gets a custom variable value using the variable index provided by the `variable` configuration value. The type `T` is determined by the referenced variable. The variable index **MUST** be non-negative and less than the total number of custom variables, otherwise the node is invalid. +[CAUTION] +==== +This node does not have a default configuration. +==== + +This node gets a custom variable value using the variable index provided by the `variable` configuration value. + +The type `T` is determined by the referenced variable. The variable index **MUST** be a non-negative integer less than the total number of custom variables, otherwise the node is invalid and the graph **MUST** be rejected. This node has no internal state. @@ -2306,7 +2659,7 @@ This node has no internal state. [cols="1h,1,2"] |=== -| Type | `variable/set` | Set a custom variable value +| Operation | `variable/set` | Set a custom variable value | Configuration | `int variable` | The custom variable index | Input flow sockets @@ -2317,140 +2670,388 @@ This node has no internal state. | `out` | The flow to be activated after the value is set |=== -This node sets a custom variable value using the variable index provided by the `variable` configuration value and the `value` input value. The type `T` is determined by the referenced variable. The variable index **MUST** be non-negative and less than the total number of custom variables, otherwise the node is invalid. +[CAUTION] +==== +This node does not have a default configuration. +==== + +This node sets a custom variable value using the variable index provided by the `variable` configuration value and the `value` input value. + +The type `T` is determined by the referenced variable. The variable index **MUST** be a non-negative integer less than the total number of custom variables, otherwise the node is invalid and the graph **MUST** be rejected. This node has no internal state. When the `in` input flow is activated: 1. Evaluate the `value` input value. -2. Set the custom variable with the `variable` configuration value index to the `value` input value. -3. Activate the `out` output flow. +2. If the _variable interpolation state dynamic array_ (defined below) contains an entry with the same variable reference, remove it from the array. +3. Set the custom variable with the `variable` configuration value index to the `value` input value. +4. Activate the `out` output flow. + +===== Variable Interpolate + +[cols="1h,1,2"] +|=== +| Operation | `variable/interpolate` | Interpolate a variable value +.2+| Configuration +| `int variable` | The custom variable index +| `bool useSlerp` | Whether to use spherical interpolation for quaternions +| Input flow sockets +| `in` | The entry flow into this node +.4+| Input value sockets +| `T value` | The target variable value +| `float duration` | The time, in seconds, in which the variable **SHOULD** reach the target value +| `float2 p1` | Control point P1 +| `float2 p2` | Control point P2 +.3+| Output flow sockets +| `out` | The flow to be activated if the input values are valid +| `err` | The flow to be activated if the input values are invalid +| `done` | The flow to be activated when the variable reaches the target value +|=== + +[CAUTION] +==== +This node does not have a default configuration. +==== + +This node interpolates and updates the specified custom variable multiple times over the specified duration. + +The type `T` is determined by the referenced variable. The variable index **MUST** be a non-negative integer less than the total number of custom variables, otherwise the node is invalid and the graph **MUST** be rejected. + +If the referenced variable is integer or boolean, the node is invalid and the graph **MUST** be rejected. + +The `useSlerp` configuration value **MUST** be a boolean literal and it **MUST NOT** be true if the type `T` is not `float4`, otherwise the node is invalid and the graph **MUST** be rejected. + +This node has no internal state. + +When a node of this type is used in the behavior graph, the global graph state includes an implementation-defined _variable interpolation state dynamic array_ each element of which contains the following data: + +- The reference to the variable being interpolated +- Implementation-defined high precision timestamp value representing the interpolation start time +- Interpolation duration value converted to the implementation-defined high precision time type +- Variable value at the time of the successful node activation +- Information needed for cubic Bézier spline evaluation derived from the node's input values +- Target variable value +- Implementation-specific pointer to the `done` output flow of the node that has added this entry + +This array is initially empty and its maximum size is implementation-specific. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type used for the internal interpolation start time value, +.. activate the `err` output flow and skip the next steps. +3. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, +.. activate the `err` output flow and skip the next steps. +4. If starting a new variable interpolation exceeds an implementation-specific limit of the maximum number of simultaneous variable interpolations, +.. activate the `err` output flow and skip the next steps. +5. If the _variable interpolation state dynamic array_ contains an entry with the same variable reference, +.. remove it from the array. +6. Using the implicitly-defined end points stem:[P_0 (0, 0)] and stem:[P_3 (1, 1)] together with the control points stem:[P_1] and stem:[P_2] provided via the input values construct a cubic Bézier easing function for the stem:[[0, 1]] input range. +7. Add a new entry to the _variable interpolation state dynamic array_ filling it with the required information based on the evaluated input values. +8. Activate the `out` output flow. + +On each tick, for each entry of the _variable interpolation state dynamic array_: + +1. Compute the current input progress position _t_ as the time passed since the interpolation start divided by the interpolation's duration. +2. If _t_ is less than or equal to zero, +.. skip the next steps. +3. If _t_ is NaN or greater than or equal to 1, +.. set the variable to the target value; +.. remove the current entry from the _variable interpolation state dynamic array_; +.. activate the `done` output flow linked to the current entry; +.. skip the next steps. +4. Using the cubic Bézier spline information, compute the output progress position _q_ based on the _t_ value. This step implies that latexmath:[t \in (0; 1)]. +5. Set the variable to the new value computed as a linear or spherical linear interpolation depending on the `useSlerp` configuration value between the original and the target variable values using the output progress position _q_ as the interpolation coefficient. + +[NOTE] +.Authoring Note +==== +Certain control point values can cause the intermediate output progress value to be negative or greater than one. This is not an error. +==== ==== Object Model Access +Operations defined in this section use JSON Pointers (<>) to refer to glTF Asset Object Model properties. These pointers are generated from JSON Pointer Templates specified in the `pointer` configuration values of the nodes. + +JSON Pointers always refer to the properties of the glTF asset that contains the behavior graph. Existence and validity of properties accessed via JSON Pointers do not depend on the current glTF scene index. + +[NOTE] +.Implementation Note +==== +For example: + +* The `/nodes/0/translation` pointer denotes the translation of the glTF node with index 0 in glTF coordinate system. + +* The `/nodes/1/rotation` pointer denotes the rotation quaternion of the glTF node with index 1 in glTF coordinate system using the glTF order of quaternion components, i.e., XYZW, where W is the scalar. + +Both pointers are functional even if the nodes do not belong to the current glTF scene. + +Implementations that import glTF assets into pre-existing scenes may need to maintain mappings between their internal objects and glTF objects defined in the asset. If the implementation's coordinate system is different from the one used in glTF, extra runtime conversions may be necessary for properties that depend on the XYZ axes. +==== + +When a behavior graph is loaded, all JSON Pointer Templates **MUST** be processed as described in the following sections. If a pointer template contains path segments wrapped in curly brackets, called _template parameters_, they define input value socket ids (with the curly brackets stripped) for the node. These template parameters **MUST NOT** be empty or contain `/`, `{`, `}`, or `~` characters; the same parameter **MUST NOT** be used more than once within a template string. If a JSON property used in the Pointer Template string contains curly brackets, they **MUST** be escaped with a back slash character (`\`, `0x5C`). + +[NOTE] +.Implementation Note +==== +None of the glTF properties currently defined in Khronos specifications contain curly brackets in their names but such properties can exist in arbitrary glTF assets within vendor-specific extensions or `extras` objects. +==== + +When an Object Model Access node is activated, its JSON Pointer Template and input values (if present) are used to generate the effective JSON Pointer string value. + +If the property being accessed is also affected by a currently active animation, the animation state **MUST** be applied before getting and/or setting the property value via pointers. + +===== JSON Pointer Template Parsing + +The input to these steps is the `pointer` string configuration value; the output includes a boolean validity flag and a template parameter array. Implementations **MAY** optimize these steps as long as such optimizations do not change the output. + +1. Let the validity flag be true and the template parameter array be empty. +2. If the pointer template string is not a syntactically valid JSON Pointer as defined in <> regardless of the pointer applicability to the glTF asset, reject the pointer template string with a syntax error. +3. Split the pointer template string at all matches of the forward slash character (`/`, `0x2F`). This step produces a path segment array consisting of a substring before the first match, substrings between the matches, and a substring after the last match. +4. For each path segment substring produced during step 3: +.. If the substring starts with a left curly bracket (`{`, `0x7B`): +... Assume the substring to be a template parameter. +... If the substring contains more than one left curly bracket, more than one right curly bracket (`}`, `0x7D`), if it does not end with a right curly bracket (`}`, `0x7D`), if there are no characters between the curly brackets (i.e., if the entire substring is `{}`), or if the substring contains a tilde (`~`, `0x7E`), set the validity flag to false, ignore all other segments, and goto step 5. +... Add the substring to the template parameter array. +... If the template parameter array contains duplicate elements, set the validity flag to false and goto step 5. +.. If the substring does not start with a left curly bracket (`{`, `0x7B`): +... Assume the substring to be a literal path segment, i.e., not a template parameter. +... If the substring contains a left or right curly bracket not escaped by a back slash character (`\`, `0x5C`), set the validity flag to false and goto step 5. +5. If the validity flag is true, output the parameter array; if the validity flag is false, reject the pointer template string with a syntax error. + +[NOTE] +.Valid Syntax Examples +==== +- The template string `"/myProperty"` is syntactically valid and has no template parameters. As it does not represent a recognized glTF Asset Object Model property, using this pointer will result in runtime errors defined by the corresponding nodes. + +- The template string `"/nodes/0/scale"` is syntactically valid and has no template parameters. + +- The template string `"/nodes/{index}/scale"` is syntactically valid and has one template parameter called `{index}`, which would result in an input value socket with id `index`. + +- The template string `"/nodes/{index\\}/scale"` is syntactically valid and has one template parameter called `{index\}`, which corresponds to the input value socket with id `index\`. Note that backslash characters are escaped when used in JSON strings. + +- The template string `"/nodes/{index}/extras/\\{index\\}"` is syntactically valid and has one template parameter called `{index}`. Since the pointer targets the `{index}` JSON property, the curly brackets of the property are escaped. Note that backslash characters are escaped when used in JSON strings. + +- The template string `"/nodes/0/extras/e\\}\\{traProperty"` is syntactically valid and has no template parameters. Since the pointer targets the `e}{traProperty` JSON property, the curly brackets of the property are escaped. Note that backslash characters are escaped when used in JSON strings. +==== + +[NOTE] +.Invalid Syntax Examples +==== +- The template string `"/nodes/{index}/extras/~2"` is syntactically invalid because the `~2` character sequence is invalid in JSON Pointers, see <>. + +- The template string `"/nodes/{index}/weights/{index}"` is syntactically invalid because the `{index}` template parameter is used twice. + +- The template string `"/nodes/{~index}/scale"` is syntactically invalid because the path segment substring that starts with `{` contains the `~` character. + +- The template string `"/nodes/{}/scale"` is syntactically invalid because the path segment substring that starts with `{` has no characters between `{` and `}`. + +- The template string `"/nodes/{index/scale"` is syntactically invalid because the path segment substring that starts with `{` does not end with `}`. + +- The template string `"/nodes/{i}ndex/scale"` is syntactically invalid because the path segment substring that starts with `{` does not end with `}`. + +- The template string `"/nodes/{{index}/scale"` is syntactically invalid because the path segment substring that starts with `{` has more than one `{` character. + +- The template string `"/nodes/{\\{index}/scale"` is syntactically invalid because the path segment substring that starts with `{` has more than one `{` character. + +- The template string `"/nodes/\\{index}/scale"` is syntactically invalid because the path segment substring that does not start with `{` has the unescaped `}` character. + +- The template string `"/nodes/{index}}/scale"` is syntactically invalid because the path segment substring that starts with `{` has more than one `}` character. + +- The template string `"/nodes/0/extras/myData{Index}"` is syntactically invalid because it has a path segment substring that does not start with `{` and contains an unescaped `{` character. +==== + +===== Effective JSON Pointer Generation + +The inputs to these steps are the `pointer` configuration value, the template parameter array, and the corresponding input values provided at runtime by the behavior graph; the output is the effective JSON Pointer string that will be handled by the Object Model Access nodes. Implementations **MAY** optimize these steps as long as such optimizations do not change the output. + +1. Let _P_ be a copy of the `pointer` configuration value. +2. For each element of the template parameter array: +.. assert that the corresponding input socket value is not negative; +.. convert the corresponding input socket value to its decimal string representation containing only ASCII characters `0x30 ... 0x39` with no extra leading zeros. +.. update _P_ by replacing the template parameter substring in it with the converted input socket value. +3. Update _P_ by replacing all occurrences of the `\{` substring in it with `{`. +4. Update _P_ by replacing all occurrences of the `\}` substring in it with `}`. +5. Output _P_ as the effective JSON Pointer string. + +[NOTE] +.Examples +==== +- Let the `pointer` configuration value be `"/nodes/{N}/weights/{W}"`. Then the nodes using this template pointer string have the `N` and `W` input value sockets. Let the runtime `N` value be 1 and the runtime `W` value be 2. Then the effective JSON Pointer is `"/nodes/1/weights/2"`. + +- Let the `pointer` configuration value be `"/nodes/{index}/extras/\\{index\\}"`. Then the nodes using this template pointer string have the `index` input value socket. Let the runtime `index` value be 2. Then the effective JSON Pointer is `"/nodes/2/extras/{index}"`. +==== + ===== Pointer Get [cols="1h,1,2"] |=== -| Type | `pointer/get` | Get an object model property value -| Configuration -| `string pointer` | The JSON Pointer or JSON Pointer template +| Operation | `pointer/get` | Get an object model property value +.2+| Configuration +| `string pointer` | JSON Pointer Template +| `int type` | Property type index | Input value sockets -| `int ` | Zero or more JSON Pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) +| `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) .2+| Output value sockets | `T value` | The resolved property value | `bool isValid` | True if the property value can be resolved, false otherwise |=== -This node gets a glTF Asset Object Model value using the provided JSON Pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the pointer string has invalid syntax or if the type of the referenced property cannot be statically determined, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. - -[NOTE] -.Examples +[CAUTION] ==== -The `pointer` configuration value `"/nodes/1024/scale"` is valid even if the `nodes` array does not have enough elements; the type `T` is `float3`; the output values are set as described below. - -The `pointer` configuration value `"/nodes/-1/scale"` is invalid because `-1` is not a valid array element reference according to the JSON Pointer standard. - -The `pointer` configuration value `"/myProperty"` is invalid because this path is not defined in the glTF Asset Object Model. +This node does not have a default configuration. ==== -The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime by converting each `` input value to a base-10 string representation. All input values **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. +This node gets a glTF Asset Object Model property value using the effective JSON Pointer string derived from the JSON Pointer Template configuration value and the runtime values of the input value sockets. + +The type `T` is determined by the `type` configuration value which points to the element of the <> array. Input value socket ids are defined by parsing the JSON Pointer Template string as described above. [NOTE] -.Example +.Examples ==== -If the `pointer` configuration value is `"/nodes/{myId}/scale"`, the behavior graph node has the `myId` input value socket, which value denotes the glTF node index. +- If the `pointer` configuration value is `"/nodes/0/translation"`, the behavior graph node has no input value sockets and the pointer always refers to the `translation` property of the glTF node 0. + +- If the `pointer` configuration value is `"/nodes/{myId}/scale"`, the behavior graph node has the `myId` input value socket, which value denotes the glTF node index. ==== -This node has no internal state. +If the `pointer` configuration value is not provided, if it is not a string, or if it is invalid (as defined in the previous sections), the node is invalid and the graph **MUST** be rejected. -If any of the input values is negative, the `value` output value is the default value for its type and the `isValid` output value is false. +If the `type` configuration value is not provided, if it is not a literal number, if it is not exactly representable as a 32-bit signed integer, if it is negative, or if it is greater than or equal to the length of the `types` array, the node is invalid and the graph **MUST** be rejected. -If the pointer or the pointer template with all its substitutions applied can be resolved, the `value` output value is the resolved property value and the `isValid` output value is true. +When this node is activated, i.e., when one of its output value sockets is being accessed: -If the pointer or the pointer template with all its substitutions applied cannot be resolved, the `value` output value is the default value for its type and the `isValid` output value is false. +1. Evaluate all input values. +2. If any of the input values is negative: +.. set the `isValid` output value to false; +.. set the `value` output value to the default value for the type `T`; +.. skip the next steps. +3. Generate the effective JSON Pointer as described in the previous sections. +4. If the effective JSON Pointer cannot be resolved against the glTF asset or if the Object Model type of the resolved property does not match the type `T`, +.. set the `isValid` output value to false; +.. set the `value` output value to the default value for the type `T`; +.. skip the next steps. +5. Set the `isValid` output value to true and the `value` output value to the value of the resolved glTF Asset Object Model property. + +Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. [NOTE] -.Note +.Examples ==== -When the `isValid` output value is false, it means that the pointer string could be resolved in principle, i.e., it represents a known glTF property and its type can be determined, but the property does not exist in the current asset. For example, an array index is out of bounds or an optional JSON object, e.g., an extension, does not exist at that location. Refer to the glTF Asset Object Model Specification for the JSON Pointer resolution rules. +- If the `pointer` configuration value is `"/nodes/{myId}/scale"`, the type `T` is `float3`, and the `myId` input value is negative or greater than or equal to the total number of glTF nodes, then the `isValid` output value is false and the `value` output value is `[NaN, NaN, NaN]`. + +- If the `pointer` configuration value is `"/nodes/{myId}/scale"` and the type `T` is `float4`, then the `isValid` output value is false and the `value` output value is `[NaN, NaN, NaN, NaN]`. Note that `myId` input value becomes irrelevant in this case because even if it is valid the Object Model property type does not match the declared type `T`. ==== ===== Pointer Set [cols="1h,1,2"] |=== -| Type | `pointer/set` | Set an object model property value -| Configuration -| `string pointer` | The JSON Pointer or JSON Pointer template +| Operation | `pointer/set` | Set an object model property value +.2+| Configuration +| `string pointer` | JSON Pointer Template +| `int type` | Property type index | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets -| `int ` | Zero or more JSON Pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) +| `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) | `T value` | The new property value .2+| Output flow sockets -| `out` | The flow to be activated if the JSON Pointer can be resolved -| `err` | The flow to be activated if the JSON Pointer cannot be resolved +| `out` | The flow to be activated if the property was set +| `err` | The flow to be activated if the property was not set |=== -This node sets a glTF Asset Object Model value using the provided JSON Pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the pointer string has invalid syntax, if the type of the referenced property cannot be statically determined, or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. +[CAUTION] +==== +This node does not have a default configuration. +==== + +This node sets a glTF Asset Object Model property value using the effective JSON Pointer string derived from the JSON Pointer Template configuration value and the runtime values of the `` input value sockets. -The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime by converting each `` input value to a base-10 string representation. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. +The type `T` is determined by the `type` configuration value which points to the element of the <> array. Input value socket ids are defined by parsing the JSON Pointer Template string as described above. -If the `value` input value is not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. +If the `pointer` configuration value is not provided, if it is not a string, if it is invalid (as defined in the previous sections), or if it contains a template parameter `{value}`, the node is invalid and the graph **MUST** be rejected. + +If the `type` configuration value is not provided, if it is not a literal number, if it is not exactly representable as a 32-bit signed integer, if it is negative, or if it is greater than or equal to the length of the `types` array, the node is invalid and the graph **MUST** be rejected. + +If the `value` input value is not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. This is not an error. Implementations **MAY** generate runtime warnings in this case as deemed possible. + +[NOTE] +.Example +==== +If the resolved glTF property is `"/materials/0/emissiveFactor"` and it is being set to `[1, 2, 3]`, the effective emissive factor becomes undefined. Querying it afterwards with `pointer/get` could return any `float3` value including but not limited to `[0, 0, 0]`, `[1, 1, 1]`, `[1, 2, 3]`, or `[NaN, NaN, NaN]`. +==== This node has no internal state. When the `in` input flow is activated: 1. Evaluate all input values. -2. If any of the pointer segment input values is negative, +2. If any of the `` input values is negative, .. activate the `err` output flow and skip the next steps. -3. If the pointer or the pointer template with all its substitutions applied can be resolved, -.. if the _pointer interpolation state dynamic array_ (defined below) contains an entry with the same resolved JSON Pointer value, remove it from the array; -.. set the resolved property to the `value` input value; -.. activate the `out` output flow. -4. If the pointer or the pointer template with all its substitutions applied cannot be resolved, -.. activate the `err` output flow. +3. Generate the effective JSON Pointer as described in the previous sections. +4. If the effective JSON Pointer cannot be resolved against the glTF asset, if the Object Model type of the resolved property does not match the type `T`, or if the property is not mutable, +.. activate the `err` output flow and skip the next steps. +5. If the _pointer interpolation state dynamic array_ (defined in the next section) contains an entry with the effective JSON Pointer, +.. remove the entry from the array. +6. Set the resolved glTF Asset Object Model property to the `value` input value. +7. Activate the `out` output flow. ===== Pointer Interpolate [cols="1h,1,2"] |=== -| Type | `pointer/interpolate` | Interpolate an object model property value -| Configuration -| `string pointer` | The JSON Pointer or JSON Pointer template +| Operation | `pointer/interpolate` | Interpolate an object model property value +.2+| Configuration +| `string pointer` | JSON Pointer Template +| `int type` | Property type index | Input flow sockets | `in` | The entry flow into this node .5+| Input value sockets -| `int ` | Zero or more JSON Pointer template path substitutions to be evaluated at runtime; input value socket names correspond to the pointer's path segments wrapped with curly braces (`{}`) +| `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) | `T value` | The target property value | `float duration` | The time, in seconds, in which the property **SHOULD** reach the target value | `float2 p1` | Control point P1 | `float2 p2` | Control point P2 .3+| Output flow sockets -| `out` | The flow to be activated if the JSON Pointer can be resolved and the input values are valid -| `err` | The flow to be activated if the JSON Pointer cannot be resolved or the input values are invalid -| `done` | The flow to be activated when the property reaches the target value +| `out` | The flow to be activated if the property interpolation has been started +| `err` | The flow to be activated if the property interpolation has not been started +| `done` | The flow to be activated after the property reaches the target value |=== -This node interpolates and updates the specified glTF Asset Object Model property multiple times over the specified duration using the provided JSON Pointer. The type `T` is determined by the pointer string according to the glTF Asset Object Model Specification. If the pointer string has invalid syntax, if the type of the referenced property cannot be statically determined, if it is integer or boolean, or if the referenced property is not mutable, the node is invalid. Pointers containing `extras` properties are out of scope of this specification but **MAY** be supported by implementations. +[CAUTION] +==== +This node does not have a default configuration. +==== -The pointer string **MAY** be a template pointer string, i.e., it **MAY** contain path segments substituted at runtime by converting each `` input value to a base-10 string representation. All input values used for path segment substitutions **MUST** be of `int` type. Path segments, if used, **MUST** substitute only array indices in the pointer templates as listed in the glTF Asset Object Model Specification. +This node interpolates and updates the glTF Asset Object Model property multiple times over the specified duration using the effective JSON Pointer string derived from the JSON Pointer Template configuration value, the runtime values of the `` input value sockets, and several interpolation inputs. -If the `value` input value or any intermediate interpolated value are not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. +The type `T` is determined by the `type` configuration value which points to the element of the <> array. Input value socket ids are defined by parsing the JSON Pointer Template string as described above. -This node has no internal state. +If the `pointer` configuration value is not provided, if it is not a string, if it is invalid (as defined in the previous sections), or if it contains template parameters `{value}`, `{duration}`, `{p1}`, or `{p2}` the node is invalid and the graph **MUST** be rejected. -When a node of this type is used in the behavior graph, the global graph state includes an implementation-defined _pointer interpolation state dynamic array_ each element of which contains the following data: +If the `type` configuration value is not provided, if it is not a literal number, if it is not exactly representable as a 32-bit signed integer, if it is negative, if it is greater than or equal to the length of the `types` array, or if it point to the type entry with `bool` or `int` type signatures, the node is invalid and the graph **MUST** be rejected. -- The resolved JSON Pointer to the Object Model property being interpolated -- Implementation-defined high precision timestamp value representing the interpolation start time -- Interpolation duration value converted to the implementation-defined high precision time type -- Object Model property value at the time of the successful node activation -- Information needed for cubic Bézier spline evaluation derived from the node's input values -- Target property value +If the `value` input value or any intermediate interpolated value are not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. This is not an error. Implementations **MAY** generate runtime warnings in this case as deemed possible. + +[NOTE] +.Example +==== +If the resolved glTF property is `"/materials/0/pbrMetallicRoughness/metallicFactor"`, its current value is zero, and the interpolation target value is two, the effective emissive factor becomes undefined when the interpolated value is greater than one. +==== + +If the current glTF Asset Object Model property value is already undefined due to previous invocations of `pointer/set` or `pointer/interpolate` nodes with invalid values (as defined above), the interpolated property remains undefined during and after the interpolation. This is not an error. Implementations **MAY** generate runtime warnings in this case as deemed possible. + +This node has no internal state. + +When a `pointer/interpolate` node is used in the behavior graph, the global graph state includes an implementation-defined _pointer interpolation state dynamic array_ each element of which contains the following data: + +- The resolved JSON Pointer to the Object Model property being interpolated +- Implementation-defined high precision timestamp value representing the interpolation start time +- Interpolation duration value converted to the implementation-defined high precision time type +- Object Model property value at the time of the successful node activation +- Information needed for cubic Bézier spline evaluation derived from the node's input values +- Target property value - Implementation-specific pointer to the `done` output flow of the node that has added this entry This array is initially empty and its maximum size is implementation-specific. @@ -2458,38 +3059,41 @@ This array is initially empty and its maximum size is implementation-specific. When the `in` input flow is activated: 1. Evaluate all input values. -2. If any of the pointer segment input values is negative, +2. If any of the `` input values is negative, .. activate the `err` output flow and skip the next steps. -3. If the pointer or the pointer template with all its substitutions applied cannot be resolved, +3. Generate the effective JSON Pointer as described in the previous sections. +4. If the effective JSON Pointer cannot be resolved against the glTF asset, if the Object Model type of the resolved property does not match the type `T`, or if the property is not mutable, .. activate the `err` output flow and skip the next steps. -4. If the `duration` input value is NaN, infinite, negative, or not convertible into an implementation-specific time type used for the internal interpolation start time value, +5. If the `duration` input value is NaN, infinite, negative, or greater than the maximum property interpolation duration supported by the implementation, .. activate the `err` output flow and skip the next steps. -5. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, +6. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, .. activate the `err` output flow and skip the next steps. -6. If starting a new pointer interpolation exceeds any implementation-specific limit, +7. If starting a new pointer interpolation exceeds an implementation-specific limit of the maximum number of simultaneous property interpolations, .. activate the `err` output flow and skip the next steps. -7. If the _pointer interpolation state dynamic array_ contains an entry with the same resolved JSON Pointer value, +8. If the _pointer interpolation state dynamic array_ contains an entry with the same effective JSON Pointer value, .. remove it from the array. -8. Using the implicitly-defined end points stem:[P_0 (0, 0)] and stem:[P_3 (1, 1)] together with the control points stem:[P_1] and stem:[P_2] provided via the input values construct a cubic Bézier easing function for the stem:[[0, 1]] input range. -9. Add a new entry to the _pointer interpolation state dynamic array_ filling it with the required information based on the evaluated input values. -10. Activate the `out` output flow. +9. Using the implicitly-defined end points stem:[P_0 (0, 0)] and stem:[P_3 (1, 1)] together with the control points stem:[P_1] and stem:[P_2] provided via the input values construct a cubic Bézier easing function for the stem:[[0, 1]] input range. +10. Add a new entry to the _pointer interpolation state dynamic array_ filling it with the required information based on the evaluated input values. +11. Activate the `out` output flow. -On each asset animation update, for each entry in the _pointer interpolation state dynamic array_: +On each tick, for each entry in the _pointer interpolation state dynamic array_: 1. Compute the current input progress position _t_ as the time passed since the interpolation start divided by the interpolation's duration. -2. If _t_ is less than or equal to zero +2. If _t_ is less than or equal to zero, .. skip the next steps. 3. If _t_ is NaN or greater than or equal to 1, -.. set the Object Model property to the target value; +.. set the target property to the target value; .. remove the current entry from the _pointer interpolation state dynamic array_; -.. activate the `done` output flow linked to the current entry +.. activate the `done` output flow linked to the current entry; .. skip the next steps. -4. Using the cubic Bézier spline information, compute the output progress position _q_ based on the _t_ value. This step implies that latexmath:[t \in [0; 1\]]. -5. Set the linked Object Model property to the new value computed as a linear interpolation between the original and the target property values using the output progress position _q_ as the interpolation coefficient. +4. Using the cubic Bézier spline information, compute the output progress position _q_ based on the _t_ value. This step implies that latexmath:[t \in (0; 1)]. +5. Set the glTF Asset Object Model property to the new value computed as a linear interpolation between the original and the target property values using the output progress position _q_ as the interpolation coefficient. If the glTF Asset Object Model property is a quaternion, e.g., `/nodes/0/rotation`, spherical linear interpolation **MUST** be used. -If the Object Model property is a quaternion, spherical linear interpolation expression **SHOULD** be used. - -Intermediate output progress values **MAY** be less than zero or greater than one. +[NOTE] +.Authoring Note +==== +Certain control point values can cause the intermediate output progress value to be negative or greater than one. This is not an error. +==== ==== Animation Control Nodes @@ -2497,7 +3101,7 @@ Intermediate output progress values **MAY** be less than zero or greater than on [cols="1h,1,2"] |=== -| Type | `animation/start` | Start playing an animation +| Operation | `animation/start` | Start playing an animation | Input flow sockets | `in` | The entry flow into this node .4+| Input value sockets @@ -2530,14 +3134,14 @@ latexmath:[t=\begin{cases} This node has no internal state. -When a node of this type is used in the behavior graph, the global graph state includes an implementation-defined _animation state dynamic array_ each element of which contains the following data: +When an `animation/start` node is used in the behavior graph, the global graph state includes an implementation-defined _animation state dynamic array_ each element of which contains the following data: - Animation index - Start time value - End time value - Stop time value (see `animation/stopAt`) - Speed value -- Implementation-specific creation timestamp value associated with the system time when this entry was added +- Implementation-specific _entry creation_ timestamp value associated with the system time when this entry was added - Implementation-specific _end completion_ pointer to the `done` output flow of the node that has added this entry - Implementation-specific _stop completion_ pointer to the `done` output flow of the node that has scheduled its stopping (see `animation/stopAt`) @@ -2552,7 +3156,7 @@ When the `in` input flow is activated: .. activate the `err` output flow and skip the next steps. 4. If the `speed` input value is NaN, infinite, or less than or equal to zero, .. activate the `err` output flow and skip the next steps. -5. If starting a new animation exceeds any implementation-specific limit, +5. If starting a new animation exceeds an implementation-specific limit of the maximum number of active animations, .. activate the `err` output flow and skip the next steps. 6. If the _animation state dynamic array_ contains an entry with the same animation index, .. remove it from the array; the previously set `done` flows **MUST NOT** be activated. @@ -2561,13 +3165,38 @@ When the `in` input flow is activated: On each asset animation update, for each entry in the _animation state dynamic array_: -1. TBD +1. If the _start time_ is equal to the _end time_, +.. let the requested timestamp stem:[r] be equal to the _start time_; +.. compute the effective timestamp stem:[t] from stem:[r] as defined above and apply the glTF animation state at the timestamp stem:[t] to the asset; +.. let `endDone` be the _end completion_ pointer stored in the current animation state entry; +.. remove the current animation state entry from the array; +.. activate the `done` output flow referenced by the `endDone` pointer; +.. skip the next steps. +2. Let the _elapsed time_ be the non-negative difference between _entry creation_ timestamp and the current system time; this step assumes that the current system time is not behind the _entry creation_ timestamp. +3. Let the _scaled elapsed time_ be the product of the _elapsed time_ and the _animation speed_ value; if the _start time_ is greater than the _end time_, negate the _scaled elapsed time_ value. +4. Let the _current timestamp_ be the sum of the _start time_ and the _scaled elapsed time_. +5. If the _start time_ is less than the _end time_, the _current timestamp_ is greater than or equal to the _stop time_, the _stop time_ is greater than or equal to the _start time_, and the _stop time_ is less than the _end time_; or if the _start time_ is greater than the _end time_, the _current timestamp_ is less than or equal to the _stop time_, the _stop time_ is less than or equal to the _start time_, and the _stop time_ is greater than the _end time_: +... let the requested timestamp stem:[r] be equal to the _stop time_; +... compute the effective timestamp stem:[t] from stem:[r] as defined above and apply the glTF animation state at the timestamp stem:[t] to the asset; +... let `stopDone` be the _stop completion_ pointer stored in the current animation state entry; +... remove the current animation state entry from the array; +... activate the `done` output flow referenced by the `stopDone` pointer; +... skip the next steps. +6. If the _start time_ is less than the _end time_ and the _current timestamp_ is greater than or equal to the _end time_; or if the _start time_ is greater than the _end time_ and the _current timestamp_ is less than or equal to the _end time_: +... let the requested timestamp stem:[r] be equal to the _end time_; +... compute the effective timestamp stem:[t] from stem:[r] as defined above and apply the glTF animation state at the timestamp stem:[t] to the asset; +... let `endDone` be the end completion pointer stored in the current animation state entry; +... remove the current animation state entry from the array; +... activate the `done` output flow referenced by the `endDone` pointer; +... skip the next steps. +7. Let the requested timestamp stem:[r] be equal to the _current timestamp_. +8. Compute the effective timestamp stem:[t] from stem:[r] as defined above and apply the glTF animation state at the timestamp stem:[t] to the asset. ===== Animation Stop [cols="1h,1,2"] |=== -| Type | `animation/stop` | Immediately stop a playing animation +| Operation | `animation/stop` | Immediately stop a playing animation | Input flow sockets | `in` | The entry flow into this node | Input value sockets @@ -2594,7 +3223,7 @@ When the `in` input flow is activated: [cols="1h,1,2"] |=== -| Type | `animation/stopAt` | Schedule stopping a playing animation +| Operation | `animation/stopAt` | Schedule stopping a playing animation | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets @@ -2618,8 +3247,8 @@ When the `in` input flow is activated: 3. If the `stopTime` input value is NaN, .. activate the `err` output flow and skip the next steps. 4. If the _animation state dynamic array_ exists and does contain an entry with the same animation index, -.. update the entry's stop completion pointer to the `done` output flow of this node; -.. update the entry's stop time to the `stopTime` input value. +.. update the entry's _stop completion_ pointer to the `done` output flow of this node; +.. update the entry's _stop time_ to the `stopTime` input value. 5. Activate the `out` output flow. === Event Nodes @@ -2630,7 +3259,7 @@ When the `in` input flow is activated: [cols="1h,1,2"] |=== -| Type | `event/onStart` | Start event +| Operation | `event/onStart` | Start event | Output flow sockets | `out` | The flow to be activated when the start event happens |=== @@ -2639,11 +3268,13 @@ This node is activated when all glTF asset resources are loaded and ready for re This node has no internal state. +If multiple instances of this node exist in the graph, they **MUST** be activated sequentially in the order they appear in JSON. + ===== On Tick [cols="1h,1,2"] |=== -| Type | `event/onTick` | Tick event +| Operation | `event/onTick` | Tick event .2+| Output value sockets | `float timeSinceStart` | Relative time in seconds since the graph execution start | `float timeSinceLastTick` | Relative time in seconds since the last tick occurred @@ -2655,13 +3286,19 @@ This node is activated when a tick occurs. There will be at most one tick per re The internal state of this node consists of two floating-point time values initialized to NaN. They **MUST** be set to their effective values before the `out` output flow is activated. +The first activation of this node **MUST** happen after activating all instances of `event/onStart` if the latter is present in the graph. + +On the first activation of this node, the `timeSinceStart` output value **MUST** be set to zero and the `timeSinceLastTick` output value **MUST** remain NaN. + +If multiple instances of this node exist in the graph, they **MUST** be activated sequentially in the order they appear in JSON and they **MUST** have the same output values within the same tick. + ==== Custom Event Nodes ===== Receive [cols="1h,1,2"] |=== -| Type | `event/receive` | Receive a custom event +| Operation | `event/receive` | Receive a custom event | Configuration | `int event` | The custom event index | Output value sockets @@ -2670,19 +3307,30 @@ The internal state of this node consists of two floating-point time values initi | `out` | The flow to be activated when the custom event happens |=== -This node is activated when a custom event specified by the `event` configuration value occurs. The types, names, and semantics of the output value sockets are defined by the custom event index. +[CAUTION] +==== +This node does not have a default configuration. +==== + +This node is activated when a custom event specified by the `event` configuration value occurs. The types, ids, and semantics of the output value sockets are defined by the custom event index. -The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid. +The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid and the graph **MUST** be rejected. -The internal state of this node consists of all output value sockets initialized to type-default values, i.e., NaN for floating-point types, zero for integers, and false for bools. +The internal state of this node consists of all output value sockets initialized to <> values or to the initial values defined by the custom event index. If the event is originated by an external environment, output values not set by the external environment **MUST** be reset to type-default or initial values on each node activation. -The output value sockets **MUST** be updated before activating the `out` output flow. +[NOTE] +.Example +==== +Let's say an event has two value sockets: `a` and `b`; before the first activation, they have initial or type-default values. Let's say the external environment generates this event and sets only the output value `a`. The output value `b` then retains its initial or type-default value. Now if the external environment generates this event again but sets only the output value `b`, the output value `a` is reset to its initial or type-default value. +==== + +If multiple instances of this node with the same event index exist in the graph, they **MUST** be activated sequentially in the order they appear in JSON and they **MUST** have the same output values within the same event occurrence. ===== Send [cols="1h,1,2"] |=== -| Type | `event/send` | Send a custom event +| Operation | `event/send` | Send a custom event | Configuration | `int event` | The custom event index | Input flow sockets @@ -2693,15 +3341,20 @@ The output value sockets **MUST** be updated before activating the `out` output | `out` | The flow to be activated after sending the event |=== -This node sends a custom event specified by the `event` configuration value. The types and names of the input value sockets are defined by the custom event index. +[CAUTION] +==== +This node does not have a default configuration. +==== -[NOTE] -.Note +This node sends a custom event specified by the `event` configuration value. The types and ids of the input value sockets are defined by the custom event index. + +[TIP] +.Authoring Tip ==== If the graph needs to know whether the event has been received and/or processed by an external environment, the latter could send another event in response. ==== -The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid. +The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid and the graph **MUST** be rejected. This node has no internal state. @@ -2715,250 +3368,829 @@ When the `in` input flow is activated: This Specification defines additional glTF Object Model pointers for use with `pointer/*` nodes. -=== Active Camera Information +=== Implementation-Specific Runtime Limits + +TBD + +=== Active Camera State In some viewers, such as, but not limited to, augmented reality viewers and virtual reality viewers, the viewer implementation gives the user direct control over a virtual camera. This virtual camera **MAY** be controlled by user head movements, by movements of the user's phone with their hands, or by mouse, keyboard or touch input on a laptop, or by other means. It is useful for interactivity to be able to react to the position of this virtual camera. This Specification defines the “active camera” as the camera transformation that ought to be reacted to by interactivity. When there is only one camera being displayed to the user the implementation **SHOULD** use this camera as the “active camera”. When there are multiple cameras being controlled by the user, the implementation **MAY** select one such camera or construct a synthetic camera to use as the “active camera” (for example the midpoint of two stereoscopic camera positions). When zero cameras are being controlled by the user but views from one or more cameras are being displayed to the user, the implementation **SHOULD** select one of the cameras that is being displayed as the “active camera”. -The `position` read-only property represents the “active camera” position in the global space. The `rotation` read-only property represents the “active camera” rotation quaternion; the identity quaternion corresponds to the camera orientation defined in the glTF 2.0 Specification. +The `position` read-only property represents the “active camera” position in the global space using the glTF coordinate system. The `rotation` read-only property represents the “active camera” rotation quaternion (using XYZW notation); the identity quaternion corresponds to the camera orientation defined in the glTF 2.0 Specification. An implementation **MAY** provide no “active camera” data, for example for privacy reasons or if no cameras are being displayed to the user. If the “active camera” position is unavailable, the `position` property **MUST** be set to all NaNs; if the “active camera” rotation is unavailable, the `rotation` property **MUST** be set to all NaNs. -The following pointers represent the read-only properties defined in this section. +The following pointers represent the properties defined in this section. + +[options="header",cols="50%,15%"] +|=== +| Pointer | Type +| `/extensions/KHR_interactivity/activeCamera/rotation` | `float4` +| `/extensions/KHR_interactivity/activeCamera/position` | `float3` +|=== + +=== Animation State + +To efficiently control animations, graphs usually need to access various states specific to glTF animation objects. The interactivity extension adds the following five runtime properties to the glTF animation objects. + +The `isPlaying` read-only property is true when the animation is playing, false otherwise. + +The `minTime` and `maxTime` read-only properties represent the timestamps of the first and the last keyframes as stored in the glTF animation object. The values **MUST** be derived from the `min` and `max` properties of the used sampler input accessors. Unused animation samplers, i.e., samplers not referenced by the animation channels, **MUST** be ignored. If the animation object is invalid as defined in the core glTF 2.0 specification, these properties **MUST** return NaNs. + +The `playhead` read-only property represents the current animation position within the glTF animation data. For valid glTF animations, the property value is equal to the last effective timestamp, so it is always greater than or equal to zero and less than or equal to `maxTime`. Before the animation start, this property value is zero; when the animation stops, the property retains its last value until the animation is restarted. For invalid glTF animations, the property value is always NaN. + +The `virtualPlayhead` read-only property represents the current animation position on the infinite timeline that is used for the input value sockets of the `animation/start` and `animation/stop` operations. For valid glTF animations, the property value is equal to the last requested timestamp. Before the animation start, this property is zero; when the animation stops, the property value retains the its last value until the animation is restarted. For invalid glTF animations, the property value is always NaN. + +The following pointers represent the properties defined in this section. [options="header",cols="50%,15%"] |=== -| Pointer | Type -| `/activeCamera/rotation` | `float4` -| `/activeCamera/position` | `float3` +| Pointer | Type +| `/animations/{}/extensions/KHR_interactivity/isPlaying` | `bool` +| `/animations/{}/extensions/KHR_interactivity/minTime` | `float` +| `/animations/{}/extensions/KHR_interactivity/maxTime` | `float` +| `/animations/{}/extensions/KHR_interactivity/playhead` | `float` +| `/animations/{}/extensions/KHR_interactivity/virtualPlayhead` | `float` |=== = JSON Syntax +[[json-general]] == General -A `KHR_interactivity` extension object is added to the root-level `extensions` property. It contains four arrays corresponding to four interactivity concepts: `types`, `events`, `variables`, and `nodes`. As with the core glTF spec, if a JSON array is empty, it **MUST** be omitted from the asset. +A `KHR_interactivity` extension object is added to the root-level `extensions` property. It contains an array of interactivity graphs each element of which contains five arrays corresponding to five interactivity concepts: `types`, `variables`, `events`, `declarations`, and `nodes`, and an optional `graph` property that selects the default graph to use. -```json +Different elements of the `graphs` array are completely isolated from each other and exist in separate scopes. One invalid graph does not invalidate other elements of the `graphs` array. + +As with the core glTF spec, if a JSON array is empty, it **MUST** be omitted from JSON. + +```javascript { "asset": { "version": "2.0" }, - "extensionsUsed": ["KHR_interactivity"], + "extensionsUsed": [ "KHR_interactivity" ], "extensions": { "KHR_interactivity": { - "types": [ - // - ], - "events": [ - // - ], - "variables": [ - // + "graphs": [ + { + "types": [ + // + ], + "variables": [ + // + ], + "events": [ + // + ], + "declarations": [ + // + ], + "nodes": [ + // + ] + } ], - "nodes": [ - // - ] + "graph": 0 } } } ``` +The `graph` property refers to the `graphs` array element that **SHOULD** be selected by default by the execution environment. If the `graph` property is undefined, its value is implicitly set to zero. If the `graph` property is negative or greater than the number of elements in the `graphs` array, the interactivity extension object is invalid. + +If the currently selected graph is invalid or if the interactivity extension object is invalid, implementations **MAY** treat the asset as not having interactivity at all. + +[[json-types]] == Types -The `types` array defines mappings between graph-local type indices and the recognized type signatures. +The `types` array defines mappings between type indices used by the graph and the recognized type signatures. Each entry in this array denotes a distinct type. -The following example defines type `0` as *float2*, type `1` as *int*, and type `2` as *float*: +[NOTE] +.Example +==== +This example defines type `0` as *float2*, type `1` as *int*, and type `2` as *float*: ```json "types": [ + { "signature": "float2" }, + { "signature": "int" }, + { "signature": "float" } +] +``` +==== + +The value of the `signature` property **MUST** be one of the value types defined in this extension specification or `"custom"`. In the latter case, the custom type semantics **MUST** be provided by an additional extension. + +Non-custom signatures **MUST NOT** appear more than once in this array; if two or more entries of the `types` array have the same non-custom signature, the graph is invalid and **MUST** be rejected. Extensions or extras present on the types defined by this Specification do not change type semantics. + +[NOTE] +.Example +==== +This means that, for example, two entries with the signature `"int"` are still disallowed even if they have extensions and/or extras. +==== + +[[json-variables]] +== Variables + +The `variables` array defines variables with their types and optional initial values. + +[NOTE] +.Example +==== +This example defines two variables of `float2` type; the first is explicitly initialized to `[0.5, 0.5]` and the second is implicitly initialized to `[NaN, NaN]`. + +```json +"types": [ + { "signature": "float2" }, +], +"variables": [ { - "signature": "float2" - }, - { - "signature": "int" + "type": 0, + "value": [ 0.5, 0.5 ] }, { - "signature": "float" + "type": 0 } ] ``` +==== + +The type of the variable is determined by the **REQUIRED** `type` property that points to the element of the `types` array. If the `type` property is undefined or its value is negative or greater than or equal to the length of the `types` array`, the variable is invalid and the graph **MUST** be rejected. + +The `value` property is an array that defines the initial variable value. If the `value` property is undefined, the variable is initialized to the default value of its type. The following table defines array lengths and default values for all value types defined in this Specification. -The signature value **MUST** be one of the value types defined in this extension specification or `"custom"`. In the latter case, the type semantic **MUST** be provided by an additional extension. +[cols="1,1,2", options="header"] +|=== +| Type | Array length | Default value +| `bool` | 1 | Boolean false +| `float` | 1 | Floating-point NaN +| `float2` | 2 | Two floating-point NaNs +| `float3` | 3 | Three floating-point NaNs +| `float4` | 4 | Four floating-point NaN +| `float2x2` | 4 | Four floating-point NaNs +| `float3x3` | 9 | Nine floating-point NaNs +| `float4x4` | 16 | Sixteen floating-point NaNs +| `int` | 1 | Integer zero +|=== + +If the `value` property array length does not match the array length for the specified type, the variable is invalid and the graph **MUST** be rejected. + +If the variable type is **bool** and the only array element is not a JSON boolean literal, i.e., neither `true` nor `false`, the variable is invalid and the graph **MUST** be rejected. -Non-custom signature **MUST NOT** appear more than once. +If the variable type is any of the **floatN** types and any of the array elements is not a JSON number, the variable is invalid and the graph **MUST** be rejected. +If the variable type is **int** and the only array element is not a JSON number exactly representable as a 32-bit signed integer, the variable is invalid and the graph **MUST** be rejected. + +If the variable type is custom, the `value` property is defined by the extension defining the custom type. + +[[json-events]] == Events -The `events` array defines external identifiers and value socket types for custom events. +The `events` array defines external ids and value sockets for custom events. -The following example defines a custom "`checkout`" event with an external identifier and one value socket: +[NOTE] +.Example +==== +This example defines two custom events. The first event is internal to the graph and has no value sockets; the second event has an external id `"checkout"` and one integer value socket with id `"variant"` and an initial value of -1. ```json +"types": [ + { "signature": "int" }, +], "events": [ + { }, { "id": "checkout", - "values": [ - { - "id": "variant", - "type": 1 + "values": { + "variant": { + "type": 0, + "value": [ -1 ] } - ] + } } ] ``` +==== -The event ID value is an application-specific event identifier recognized by the execution environment. If the `id` property is undefined, the event is considered internal to the graph. +The event id is an application-specific event identifier recognized by the execution environment. If the `id` property is undefined, the event is considered internal to the graph. If the same id is defined for two or more events, the graph is invalid and **MUST** be rejected. -The `values` array defines IDs and type indices of the sockets associated with the event. If the array is undefined, the event has no associated value sockets. +The properties of the `values` object define ids and the values of those properties define types and optional initial values of the value sockets associated with the event. If the `values` object is undefined, the event has no associated value sockets. -== Variables +The type of the event value socket is determined by the **REQUIRED** `type` property that points to the element of the `types` array. If the `type` property is undefined or its value is negative or greater than or equal to the length of the `types` array, the event is invalid and the graph **MUST** be rejected. + +The `value` property of the event value socket has the same syntax and semantics as the `value` property of variable definitions (see the previous section). + +[[json-declarations]] +== Declarations -The `variables` array defines custom variables with their types and optional initialization values. +The `declarations` array defines mappings between node declaration indices used by the graph and the operations. -The following example defines a custom variable with its initial value: +[NOTE] +.Example +==== +This example defines declaration `0` as `math/min` and declaration `1` as `variable/set`. ```json -"variables": [ +"declarations": [ + { "op": "math/min" }, + { "op": "variable/set" } +] +``` +==== + +The `op` property is **REQUIRED**; it contains the operation identifier; if this property is undefined, the declaration is invalid and the graph **MUST** be rejected. + +If the operation is not defined by this Specification, the `extension` property **MUST** be defined and it contains the additional interactivity extension name that defines the operation. If the `extension` property is not defined and the operation is not defined by this Specification, the declaration is invalid and the graph **MUST** be rejected. + +If the operation is defined in an additional interactivity extension and it uses input value sockets, the `inputValueSockets` object **MUST** be present. Its properties define ids and the values of its properties define types of the input value sockets. If the `inputValueSockets` object is undefined, the operation has no input value sockets. + +If the operation is defined in an additional interactivity extension and it uses output value sockets, the `outputValueSockets` object **MUST** be present. Its properties define ids and the values of its properties define types of the output value sockets. If the `outputValueSockets` object is undefined, the operation has no output value sockets. + +If the `extension` property is undefined, the operation with all its value sockets is assumed to be provided by this Specification and therefore `inputValueSockets` and `outputValueSockets` objects **MUST NOT** be defined. + +[NOTE] +.Example +==== +This example defines a declaration that maps to the `event/onSelect` operation defined in the `KHR_node_selectability` extension. The operations has three output value sockets and zero input value sockets. + +```json +"types": [ + { "signature": "int" }, + { "signature": "float3" } +], +"declarations": [ { - "type": 0, - "value": [0.5, 0.5] + "op": "event/onSelect", + "extension": "KHR_node_selectability", + "outputValueSockets": { + "selectedNodeIndex": { "type": 0 }, + "controllerIndex": { "type": 0 }, + "selectionPoint": { "type": 1 } + } } ] ``` +==== -The `type` value defines the index of the variable type. +The type of the value socket is determined by the **REQUIRED** `type` property that points to the element of the `types` array. If the `type` property is undefined or its value is negative or greater than or equal to the length of the `types` array, the declaration is invalid and the graph **MUST** be rejected. -The `value` array, if present, defines the initial variable value. The following table defines array size and default values for all value types defined in this extension. +Two declarations are considered equal if their `op` properties have the same value, their `extension` properties (if present) have the same value, and their `inputValueSockets` objects (if present) define the same socket ids with the same type indices. The `declarations` array **MUST NOT** have equal declarations; if two or more declarations are equal, all of them are invalid and the graph **MUST** be rejected. -[cols="1,1,2", options="header"] -|=== -| Type | Array size | Default value -| `bool` | 1 | Boolean false -| `float` | 1 | Floating-point NaN -| `float2` | 2 | Two floating-point NaNs -| `float3` | 3 | Three floating-point NaNs -| `float4` | 4 | Four floating-point NaN -| `float4x4` | 16 | Sixteen floating-point NaNs -| `int` | 1 | Integer zero -|=== +[NOTE] +.Example +==== +All three declarations in this example are equal thus they all are invalid. -If the variable type is custom, the `value` property is defined by the extension defining the custom type. +```json +"types": [ + { "signature": "int" }, + { "signature": "float" } +], +"declarations": [ + { + "op": "math/min3", + "extension": "VND_interactivity_min3", + "inputValueSockets": { + "a": { "type": 0 }, + "b": { "type": 0 }, + "c": { "type": 0 } + }, + "outputValueSockets": { + "value": { "type": 0 } + } + }, + { + "op": "math/min3", + "extension": "VND_interactivity_min3", + "inputValueSockets": { + "b": { "type": 0 }, + "a": { "type": 0 }, + "c": { "type": 0 } + }, + "outputValueSockets": { + "value": { "type": 0 } + } + }, + { + "op": "math/min3", + "extension": "VND_interactivity_min3", + "inputValueSockets": { + "a": { "type": 0 }, + "b": { "type": 0 }, + "c": { "type": 0 } + }, + "outputValueSockets": { + "value": { "type": 1 } + } + } +] +``` +==== +=== Unsupported Declarations + +A declaration is considered unsupported if any of the following conditions is true: + +- The declaration refers to an unsupported or disabled extension. +- The referred extension does not define the operation. +- Neither of the definitions of the operation in the referred extension has exactly the same input and output value sockets with regards to their ids and types. + +If the declaration is unsupported, the nodes referring to it are demoted to <>. + +[[json-nodes]] == Nodes -The `nodes` array defines the behavior graph. +The `nodes` array defines the nodes and their connections. + +Each element of the `nodes` array specifies the node's operation via a declaration index, sources for the input value sockets, pointers for the output flow sockets, and its configuration. + +=== Operation + +The operation is specified by the **REQUIRED** `declaration` property that points to an element of the `declarations` array. If that property is undefined or its value is negative or greater than or equal to the number of declarations, the node is invalid and the graph **MUST** be rejected. + +[NOTE] +.Example +==== +A `math/e` node with its declaration. + +```json +"declarations": [ + { "op": "math/e" } +], +"nodes": [ + { "declaration": 0 } +] +``` +==== + +=== Input Value Sockets + +If the operation has input value sockets, the `values` object **MUST** be defined and its properties **MUST** match the input value socket ids of the declaration; if the properties of the `values` object do not match the declaration, the node is invalid and the graph **MUST** be rejected. If the operation does not have input value sockets, the `values` object **MUST NOT** be defined. + +Some nodes, e.g., `pointer/get` or `variable/get`, define their input value socket ids and/or types based on the node's configuration. Therefore, the configuration **MAY** need to be processed prior to the input value sockets. + +The values of the `values` object properties are JSON objects that define effective input value socket types and value sources. Each value source is either an inline constant value, a <> value, or a reference to another node's output value socket. If no source is defined or if the socket type does not match the declaration, the node is invalid and the graph **MUST** be rejected. + +Some nodes have multiple variants to support the same operation on different input value socket types, e.g., math operations like `math/add` support all numeric types including integers, vectors, and matrices. Therefore, effective input value socket types **MAY** be needed to fully resolve the operation. + +[NOTE] +.Example +==== +For example, the `math/add` operation defined in this Specification exists only for matching input value socket types so any node that refers to it and uses different types for `a` and `b` input value sockets is invalid. +==== + +==== Inline Values + +If the `value` property is defined in the object representing the input value socket, the input value socket source is an inline constant. + +The `value` property has the same syntax as the `value` property of variable definitions. The type of the input value socket is determined by the `type` property that points to the element of the `types` array and **MUST** be defined. If the `type` property value is negative or greater than or equal to the number of types, the node is invalid and the graph **MUST** be rejected. + +[NOTE] +.Example +==== +A `math/add` node with two integer inline values: 1 and 2. + +```json +"types": [ + { "signature": "int" } +] +"declarations": [ + { "op": "math/add" } +], +"nodes": [ + { + "declaration": 0, + "values": { + "a": { "value": [ 1 ], "type": 0 }, + "b": { "value": [ 2 ], "type": 0 } + } + } +] +``` +==== + +==== Output Socket References -Each element of the `nodes` array represents a node instance, i.e., it specifies node's type, configuration, sources of input value sockets, and pointers of the output flow sockets. +If the `node` property is defined in the object representing the input value socket, the input value socket source is the output value socket of another node of the graph. If both `node` and `value` properties are defined for the same input value socket, the node is invalid and the graph **MUST** be rejected. -Input value sockets **MAY** have inline constant values; in this case, the value socket type **MUST** be explicitly defined. +The `node` property contains the index of the other node and the `socket` property contains the id of the output socket of that node. -Configuration values are always implicitly typed based on the node's type. +If the `node` property value is negative or greater than or equal to the index of the current node, the node is invalid and the graph **MUST** be rejected. [NOTE] .Rationale ==== -Some nodes have configuration values of array and/or string types that cannot be expressed with the explicit types defined in this Specification. +This ensures that value sockets do not form loops and simplifies input value socket type derivation. ==== -Inline values and configurations use JSON arrays similarly to the initial variable values. +If the `socket` property is defined, it **MUST** correspond to an output value socket existing in the referenced node, otherwise the current node is invalid and the graph **MUST** be rejected. If the `socket` property is undefined, the default socket id `"value"` is used implicitly. Therefore, if the referenced node does not have an output value socket with id `"value"`, the `socket` property **MUST** be defined. + +If both `node` and `type` properties are defined, the type referred by the `type` property **MUST** match the type of the referenced output value socket; if the types do not match, the current node is invalid and the graph **MUST** be rejected. + +[NOTE] +.Rationale +==== +Although explicitly defining input value socket types is generally redundant for input value sockets referring to other nodes, providing this information could improve debugging experience during graph development. +==== -The following example instantiates a `math/add` node that has both its input value sockets filled with inline integer values. +[NOTE] +.Example +==== +A `math/sub` node with two input value sockets referring to output value sockets of two other nodes. The input socket `a` refers to the output socket id explicitly and the input socket `b` relies on the implicit output socket id. ```json +"types": [ + { "signature": "float" } +] +"declarations": [ + { "op": "math/pi" } + { "op": "math/e" } + { "op": "math/sub" } +], "nodes": [ + { "declaration": 0 }, + { "declaration": 1 }, { - "type": "math/add", - "values": [ - { - "id": "a", - "value": [1], - "type": 1 - }, - { - "id": "b", - "value": [2], - "type": 1 - } - ] + "declaration": 2, + "values": { + "a": { "node": 0, "socket": "value" }, + "b": { "node": 1 } + } } ] ``` +==== -The following example instantiates three nodes. The `math/sub` node has both its input value sockets connected to output value sockets of two other nodes: `math/pi` and `math/e`. +==== Type-Default Values + +If neither `value` nor `node` properties are defined in the object representing the input value socket, the input value socket has a <> value determined by the `type` property that points to the element of the `types` array and **MUST** be defined. If the `type` property value is negative or greater than or equal to the number of types, the node is invalid and the graph **MUST** be rejected. + +[NOTE] +.Example +==== +A `math/isnan` node with a type-default input value socket. The output value of this node is true because the input value socket `a` has a constant value of NaN (type-default for `float`). ```json +"types": [ + { "signature": "float" } +] +"declarations": [ + { "op": "math/isnan" } +], "nodes": [ { - "type": "math/pi" + "declaration": 0, + "values": { + "a": { "type": 0 } + } + } +] +``` +==== + +=== Output Flow Socket Pointers + +Pointers for the output flow sockets are defined in the `flows` object of the node. + +Properties of the `flows` object link output flow sockets of the current node with input flow sockets of other nodes. If an output flow socket id of the current node is not present in the `flows` object, that output flow socket is unconnected and activating it has have no effect. + +The `flows` object **MAY** contain properties not corresponding to output flows of the current node; such properties do not affect functionality of the node but their values **MUST** still be validated as described below. + +Each property of the `flows` object is a JSON object containing a **REQUIRED** `node` property and an **OPTIONAL** `socket` property. The `node` property contains the index of the other node and the `socket` property contains the id of the input flow socket of that node. + +The `node` property value **MUST** be greater than the index of the current node and less then the total number of nodes, otherwise the node is invalid and the graph **MUST** be rejected. + +[NOTE] +.Rationale +==== +This ensures that flow sockets do not form loops. +==== + +If the `socket` property is undefined, it has a default value of `"in"`. + +If the `socket` property value corresponds to an input flow socket existing in the referenced node, the output flow socket of the current node is connected to the referenced input flow socket. If the specified input flow socket does not exist in the referenced node, the output flow socket of the current node is unconnected and activating it **MUST** have no effect. + +[NOTE] +.Example +==== +A `flow/setDelay` node that starts an animation after a certain amount of time since the start of the graph execution. + +The `out` output flow of the `event/onStart` node is connected to the `in` input flow of the `flow/setDelay` node explicitly. Then, the `out` output flow of the latter node is connected to the `in` input flow of the `animation/start` node implicitly. + +```json +"types": [ + { "signature": "float" }, + { "signature": "int" } +] +"declarations": [ + { "op": "event/onStart" }, + { "op": "flow/setDelay" }, + { "op": "math/inf" }, + { "op": "animation/start" } +], +"nodes": [ + { + "declaration": 0, + "flows": { + "out": { "node": 1, "socket": "in" } + } }, { - "type": "math/e" + "declaration": 1, + "values": { + "duration": { "type": 0, "value": [5] } + }, + "flows": { + "out": { "node": 3 } + }, }, { - "type": "math/sub", - "values": [ - { - "id": "a", - "node": 0, - "socket": "value" - }, - { - "id": "b", - "node": 1, - "socket": "value" - } - ] + "declaration": 2 + }, + { + "declaration": 3, + "values": { + "animation": { "type": 1 }, + "startTime": { "type": 0, "value": [0] }, + "endTime": { "node": 2 }, + "speed": { "type": 0, "value": [1] }, + } } ] ``` +==== + +=== Configuration + +Configuration properties are defined in the `configuration` object of the node. + +Each property of the `configuration` object is a JSON object with a single `value` property. The type of the `value` property is determined by the node's specification, i.e., configuration values are implicitly typed. + +[NOTE] +.Rationale +==== +Some nodes have configuration values of types that cannot be expressed with the explicit types defined in this Specification. +==== + +Refer to the <> section and to individual node specifications for details regarding configuration validity. + +Configuration values use JSON arrays similarly to other uses of inline values. + +[cols="1,2", options="header"] +|=== +| Configuration Type | JSON Type +| `bool` | Array of one boolean +| `int` | Array of one number exactly representable as a 32-bit signed integer +| `int[]` | Array of one or more numbers exactly representable as 32-bit signed integers +| `string` | Array of one JSON string +|=== The following example instantiates two nodes. The `variable/set` node sets a custom variable with index `0` when the start event happens. ```json +"types": [ + { "signature": "float" } +] "variables": [ - { - "type": 1 - } + { "type": 0 } +], +"declarations": [ + { "op": "event/onStart" }, + { "op": "variable/set" }, ], "nodes": [ { - "type": "variable/set", - "configuration": [ - { - "id": "variable", - "value": [0] - } - ], - "values": [ - { - "id": "value", - "value": [1], - "type": 1 - } - ] + "declaration": 0, + "flows": { + "out": { "node": 1 } + } }, { - "type": "event/onStart", - "flows": [ - { - "id": "out", - "node": 0, - "socket": "in" - } - ] + "declaration": 1, + "configuration": { + "variable": { "value": [0] } + }, + "values": { + "value": { "type": 0, "value": [1.5] } + } } ] ``` -The `type` property is required; it defines semantics and validation of the `configuration`, `values`, and `flows` arrays. - -The same `id` value **MUST NOT** be used more than once within each of the `configuration`, `values`, and `flows` arrays. - -If the node type has configuration, the `configuration` array **MUST** provide all configuration parameters as inline values. - -If the node type has input value sockets, the `values` array **MUST** connect all input value sockets to other nodes or fill them with inline values. Additionally, for each element of the `values` array: - -- `value` and `node` properties **MUST NOT** be defined at the same time; -- if `value` is defined, `type` **MUST** also be defined. +[[validation]] += Validation (Informative) + +This section describes steps needed to check validity of the interactivity extension object according to the normative language of the previous sections and the corresponding JSON schemas. + +[[validation-glossary]] +== Validation Glossary + +This section uses the following terms: + +assert:: +continue iff the associated condition is true; otherwise _reject the extension_ + +JSON index:: +a non-negative JSON number that is exactly representable as an integer, e.g., JSON numbers `2`, `2.0`, and `0.2e1` are exactly representable as integer two + +reject the extension:: +the whole interactivity extension object is invalid and thus cannot be used + +reject the graph:: +the interactivity graph is invalid and thus cannot be used; this has no effect on other graphs defined in the extension + +[[validation-extension-object]] +== Extension Object Validation + +1. _Assert_ that the interactivity extension object has the `graphs` property that is a non-empty JSON array. +2. Validate each element of the `graphs` array as described in <>. +3. If the interactivity extension object has the `graph` property: +.. _assert_ that the `graph` property value is a _JSON index_; +.. if the `graph` property value is less the `graphs` array length, +... _reject the extension_; +.. if the graph referenced by the `graph` property value is invalid, +... _reject the extension_. + +[[validation-graph-object]] +== Graph Object Validation + +1. _Assert_ that the element of the `graphs` array is a JSON object ("`the graph`"). +2. If "`the graph`" object has the `types` property: +.. _assert_ that the `types` property is a non-empty JSON array; +.. if the `types` array length is greater than the implementation-specific limit on the number of used types, _reject the graph_; +.. for each element of the `types` array: +... _assert_ that the element of the `types` array is a JSON object ("`the type`"); +... _assert_ that "`the type`" object has the `signature` property that is a JSON string; +... if the `signature` property value is not `"bool"`, `"custom"`, `"float"`, `"float2"`, `"float3"`, `"float4"`, `"float2x2"`, `"float3x3"`, `"float4x4"`, or `"int"`, +.... _reject the graph_; +.. if two or more elements of the `types` array have the same `signature` value that is not `"custom"`, +... _reject the graph_. +3. If "`the graph`" object has the `variables` property: +.. _assert_ that the graph has the `types` property; +.. _assert_ that the `variables` property is a non-empty JSON array; +.. if the `variables` array length is greater than the implementation-specific limit on the number of variables, +... _reject the graph_; +.. validate each element of the `variables` array as described in the <> section. +4. If "`the graph`" object has the `events` property: +.. _assert_ that the `events` property is a non-empty JSON array; +.. if the `events` array length is greater than the implementation-specific limit on the number of event definitions, +... _reject the graph_; +.. validate each element of the `events` array as described in the <> section; +.. if two or more elements of the `events` array have the same `id` value that is not undefined, +... _reject the graph_. +5. If "`the graph`" object has the `declarations` property: +.. _assert_ that the `declarations` property is a non-empty JSON array; +.. if the `declarations` array length is greater than the implementation-specific limit on the number of declarations, +... _reject the graph_; +.. validate each element of the `declarations` array as described in the <> section. +6. If "`the graph`" object has the `nodes` property: +.. _assert_ that the `nodes` property is a non-empty JSON array; +.. if the `nodes` array length is greater than the implementation-specific limit on the number of nodes, +... _reject the graph_; +.. validate each element of the `nodes` array as described in the <> section. + +[[validation-variable-object]] +== Variable Object Validation + +1. _Assert_ that the element of the `variables` array is a JSON object ("`the variable`"). +2. _Assert_ that "`the variable`" object has the `type` property that is a _JSON index_. +3. If the `type` property value is not less than the `types` graph array length, +.. _reject the graph_. +4. If the "`the variable`" object has the `value` property: +.. _assert_ that the `value` property is a non-empty JSON array; +.. validate the `value` property value according to the <> section using the `type` property value. + +[[validation-event-object]] +== Event Object Validation + +1. _Assert_ that the element of the `events` array is a JSON object ("`the event`"). +2. If "`the event`" object has the `id` property, +.. _assert_ that the `id` property value is a JSON string. +3. If "`the event`" object has the `values` property, +.. _assert_ that the `values` property is a non-empty JSON object; +.. if the `values` object has more properties than the implementation-specific limit on the number of event value sockets, +... _reject the graph_; +.. for each property of the `values` JSON object: +... _assert_ that the property is a JSON object ("`the event value`"); +... _assert_ that "`the event value`" object has the `type` property that is a _JSON index_; +... if the `type` property value is not less than the `types` graph array length, +.... _reject the graph_; +... if the "`the event value`" object has the `value` property: +.... _assert_ that the `value` property is a non-empty JSON array; +.... validate the `value` property value according to the <> using the `type` property. + +[[validation-declaration-object]] +== Declaration Object Validation + +1. _Assert_ that the element of the `declarations` array is a JSON object ("`the declaration`"). +2. _Assert_ that "`the declaration`" object has the `op` property that is a JSON string. +3. If "`the declaration`" object does not have the `extension` property: +.. if the `op` property value does not match any operation defined in this Specification, +... _reject the graph_; +.. if "`the declaration`" object has the `inputValueSockets` and/or `outputValueSockets` properties, +... _reject the graph_; +4. If "`the declaration`" object has the `extension` property: +.. _assert_ that the `extension` property is a JSON string; +.. if the "`the declaration`" object has the `inputValueSockets` property: +... _assert_ that the `inputValueSockets` property is a non-empty JSON object; +... if the `inputValueSockets` object has more properties than the implementation-specific limit on the number of input value sockets for declarations, +.... _reject the graph_; +... _assert_ that the graph has the `types` property; +... for each property of the `inputValueSockets` JSON object: +.... _assert_ that the property is a JSON object ("`the input value socket declaration`"); +.... _assert_ that "`the input value socket declaration`" object has the `type` property that is a _JSON index_; +.... if the `type` property value is not less than the `types` graph array length, +..... _reject the graph_; +.. if "`the declaration`" object has the `outputValueSockets` property: +... _assert_ that the `outputValueSockets` property is a non-empty JSON object; +... if the `outputValueSockets` object has more properties than the implementation-specific limit on the number of output value sockets for declarations, +.... _reject the graph_; +... _assert_ that the graph has the `types` property; +... for each property of the `outputValueSockets` JSON object: +.... _assert_ that the property is a JSON object ("`the output value socket declaration`"); +.... _assert_ that "`the output value socket declaration`" object has the `type` property that is a _JSON index_; +.... if the `type` property value is not less than the `types` graph array length, +..... _reject the graph_. + +[[validation-node-object]] +== Node Object Validation + +1. _Assert_ that the element of the `nodes` array is a JSON object ("`the node`"). +2. _Assert_ that "`the node`" object has the `declaration` property that is a JSON index. +3. If the `declaration` property value is not less than the `declarations` graph array length, +.. _reject the graph_. +4. If "`the node`" object has the `configuration` property: +.. _assert_ that the `configuration` property is a non-empty JSON object; +.. _assert_ that each property of the `configuration` JSON object is a JSON array. +5. If the node is configurable as indicated by the operation referenced by the `declaration` property and the node does not support a default configuration: +.. if "`the node`" does not have the `configuration` property, +... _reject the graph_; +.. if `configuration` object is not valid as defined by the node, +... _reject the graph_. +6. If "`the node`" object has the `values` property: +.. _assert_ that the `values` property is a non-empty JSON object; +.. for each property of the `values` JSON object: +... _assert_ that the property is a JSON object ("`the input value socket`"); +... if "`the input value socket`" object has the `node` property: +.... _assert_ that "`the input value socket`" object does not have the `value` property; +.... _assert_ that the `node` property is a JSON index; +.... if the `node` property value is not less than the index of the current element of the `nodes` array, +..... _reject the graph_; +.... let "`the effective socket id`" be `"value"`; +.... if "`the input value socket`" object has the `socket` property: +..... _assert_ that the `socket` property is a JSON string; +..... set "`the effective socket id`" to the value of the `socket` property; +.... if the graph node referenced by the `node` property does not have the output value socket with id equal to "`the effective socket id`", +..... _reject the graph_; +.... if "`the input value socket`" object has the `type` property: +..... _assert_ that the `type` property is a _JSON index_; +..... if the `type` property value is not less than the `types` graph array length, +...... _reject the graph_; +..... if the type of the referenced output value socket does not match the type referenced by the `type` property value, +...... _reject the graph_; +... if "`the input value socket`" object does not have the `node` property: +.... _assert_ that "`the input value socket`" object has the `type` property that is a _JSON index_; +.... if the `type` property value is not less than the `types` graph array length, +..... _reject the graph_; +.... if "`the input value socket`" object has the `value` property: +..... _assert_ that the `value` property is a non-empty JSON array; +..... validate the `value` property value according to the <> using the `type` property. +7. If the set of input value sockets defined by the declaration referenced by the `declaration` property does not match the set of input value sockets defined by the `values` property with regard to socket ids and types, +.. _reject the graph_. +8. If "`the node`" object has the `flows` property: +.. _assert_ that the `flows` property is a non-empty JSON object; +.. if the `flows` object has more properties than the implementation-specific limit on the number of output flow sockets per node, +... _reject the graph_; +.. for each property of the `flows` JSON object: +... _assert_ that the property is a JSON object ("`the output flow socket`"); +... _assert_ that "`the output flow socket`" object has the `node` property that is a JSON index; +... if the `node` property value is not greater than the index of the current element of the `nodes` array, +.... _reject the graph_; +... if "`the output flow socket`" object has the `socket` property, +.... _assert_ that the `socket` property is a JSON string. + +[[inline-value-validation]] +== Inline Value Object Validation + +1. Let "`the array`" be the JSON array representing the inline value and "`the type signature`" be the type signature associated with it. +2. If "`the type signature`" is `"bool"`, `"float"`, or `"int"` and "`the array`" length is not one, +.. _reject the graph_. +3. If "`the type signature`" is `"float2"` and "`the array`" length is not two, +.. _reject the graph_. +4. If "`the type signature`" is `"float3"` and "`the array`" length is not three, +.. _reject the graph_. +5. If "`the type signature`" is `"float4"` or `"float2x2"` and "`the array`" length is not four, +.. _reject the graph_. +6. If "`the type signature`" is `"float3x3"` and "`the array`" length is not nine, +.. _reject the graph_. +7. If "`the type signature`" is `"float4x4"` and "`the array`" length is not 16, +.. _reject the graph_. +8. If "`the type signature`" is `"bool"` and the only element of "`the array`" is not a JSON boolean, +.. _reject the graph_. +9. If "`the type signature`" is `"int"` and the only element of "`the array`" is not exactly representable as a 32-bit signed integer, +.. _reject the graph_. +10. If "`the type signature`" is any of the seven float types defined in this Specification and any element of "`the array`" is not a JSON number, +.. _reject the graph_. From 45ef4a769969c688f7710fd757395a789be97cef Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 13 Jan 2025 00:00:00 +0000 Subject: [PATCH 44/80] Update GitHub Actions --- .github/workflows/CI.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ebbcd071ee..4839ad65cf 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -23,7 +23,7 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # Build spec targets - name: spec-generate @@ -32,7 +32,7 @@ jobs: make Specification.html Specification.pdf - name: Archive generated files - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: spec-outputs path: | @@ -45,7 +45,7 @@ jobs: make Specification.html - name: Archive generated files - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: interactivity-outputs path: | From 298984e749376554d4904da70844fc011632a3ad Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Sun, 19 Jan 2025 00:00:00 +0000 Subject: [PATCH 45/80] Address feedback --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 13d99e65a3..63e934cf84 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -892,7 +892,7 @@ The value of the output value socket `value` **MUST** be initialized to a random This means that, e.g., a `math/eq` node with both its input value sockets connected to the same `math/random` node always returns true. ==== -The value of the output value socket `value` **SHOULD** be updated when accessed as a result of a new flow socket activation, including self-activations. +The value of the output value socket `value` **MUST** be updated when accessed as a result of a new flow socket activation, including self-activations. [NOTE] .Implementation Note @@ -3037,7 +3037,7 @@ If the `value` input value or any intermediate interpolated value are not valid [NOTE] .Example ==== -If the resolved glTF property is `"/materials/0/pbrMetallicRoughness/metallicFactor"`, its current value is zero, and the interpolation target value is two, the effective emissive factor becomes undefined when the interpolated value is greater than one. +If the resolved glTF property is `"/materials/0/pbrMetallicRoughness/metallicFactor"`, its current value is zero, and the interpolation target value is two, the effective metalness factor becomes undefined when the interpolated value is greater than one. ==== If the current glTF Asset Object Model property value is already undefined due to previous invocations of `pointer/set` or `pointer/interpolate` nodes with invalid values (as defined above), the interpolated property remains undefined during and after the interpolation. This is not an error. Implementations **MAY** generate runtime warnings in this case as deemed possible. From d9aa299241d29b488caf35b6c70b7d8884fa281f Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:00:00 +0000 Subject: [PATCH 46/80] Validation and typo fixes --- .../Khronos/KHR_interactivity/Specification.adoc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 63e934cf84..c6ec2a0202 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -232,7 +232,7 @@ Usually, the node execution includes evaluating its input value sockets (if any) There are four kinds of sockets. -_Output value sockets_ represent data initialized by the node or produced during its execution. For example, it could be results of math operations or parts of the node's internal state. Accessing these sockets either triggers computing the return value on the fly by executing the node or returns a value based on the node's internal state. Exact behavior depends on the node's operation. As a general rule, output value sockets **MUST** retain their values until a node having any number of flow sockets is executed. +_Output value sockets_ represent data initialized by the node or produced during its execution. For example, it could be results of math operations or parts of the node's internal state. Accessing these sockets either triggers computing the return value on the fly by executing the node or returns a value based on the node's internal state. Exact behavior depends on the node's operation. As a general rule, output value sockets **MUST** retain their values until a node with one or more flow sockets is executed. [NOTE] .Implementation Note @@ -2743,7 +2743,7 @@ When the `in` input flow is activated: .. activate the `err` output flow and skip the next steps. 3. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, .. activate the `err` output flow and skip the next steps. -4. If starting a new variable interpolation exceeds an implementation-specific limit of the maximum number of simultaneous variable interpolations, +4. If starting a new variable interpolation exceeds an implementation-specific limit on the maximum number of simultaneous variable interpolations, .. activate the `err` output flow and skip the next steps. 5. If the _variable interpolation state dynamic array_ contains an entry with the same variable reference, .. remove it from the array. @@ -3068,7 +3068,7 @@ When the `in` input flow is activated: .. activate the `err` output flow and skip the next steps. 6. If any component of the `p1` or `p2` input values is NaN or infinite or if any of the first components of these input values is negative or greater than 1, .. activate the `err` output flow and skip the next steps. -7. If starting a new pointer interpolation exceeds an implementation-specific limit of the maximum number of simultaneous property interpolations, +7. If starting a new pointer interpolation exceeds an implementation-specific limit on the maximum number of simultaneous property interpolations, .. activate the `err` output flow and skip the next steps. 8. If the _pointer interpolation state dynamic array_ contains an entry with the same effective JSON Pointer value, .. remove it from the array. @@ -3156,7 +3156,7 @@ When the `in` input flow is activated: .. activate the `err` output flow and skip the next steps. 4. If the `speed` input value is NaN, infinite, or less than or equal to zero, .. activate the `err` output flow and skip the next steps. -5. If starting a new animation exceeds an implementation-specific limit of the maximum number of active animations, +5. If starting a new animation exceeds an implementation-specific limit on the maximum number of active animations or if the referenced glTF animation is invalid as determined by the implementation, .. activate the `err` output flow and skip the next steps. 6. If the _animation state dynamic array_ contains an entry with the same animation index, .. remove it from the array; the previously set `done` flows **MUST NOT** be activated. @@ -4009,7 +4009,7 @@ the interactivity graph is invalid and thus cannot be used; this has no effect o 2. Validate each element of the `graphs` array as described in <>. 3. If the interactivity extension object has the `graph` property: .. _assert_ that the `graph` property value is a _JSON index_; -.. if the `graph` property value is less the `graphs` array length, +.. if the `graph` property value is not less than the `graphs` array length, ... _reject the extension_; .. if the graph referenced by the `graph` property value is invalid, ... _reject the extension_. @@ -4124,7 +4124,9 @@ the interactivity graph is invalid and thus cannot be used; this has no effect o .. _reject the graph_. 4. If "`the node`" object has the `configuration` property: .. _assert_ that the `configuration` property is a non-empty JSON object; -.. _assert_ that each property of the `configuration` JSON object is a JSON array. +.. for each property of the `configuration` JSON object: +... _assert_ that the property is a JSON object ("`the configuration property`"); +... _assert_ that "`the configuration property`" object has the `value` property that is a non-empty JSON array. 5. If the node is configurable as indicated by the operation referenced by the `declaration` property and the node does not support a default configuration: .. if "`the node`" does not have the `configuration` property, ... _reject the graph_; From fa4b37c691f7a749ad424a7a77f8be8989a16b6b Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 27 Jan 2025 00:00:00 +0000 Subject: [PATCH 47/80] Add more tips --- .../KHR_interactivity/Specification.adoc | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index c6ec2a0202..69819a8dbb 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -463,6 +463,12 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 | Output value sockets | `float value` | _Infinity_ |=== +[TIP] +.Authoring Tip +==== +To get negative infinity, combine this node with `math/neg`. +==== + ===== Not a Number [cols="1h,1,2"] @@ -856,6 +862,14 @@ If any input value is _NaN_, the output value is false. | `bool value` | True if stem:[a] is positive or negative infinity; false otherwise |=== +[TIP] +.Authoring Tip +==== +To check whether a value is only a positive infinity, combine `math/eq` and `math/inf` nodes. + +To check whether a value is only a negative infinity, combine `math/eq`, `math/neg`, and `math/inf` nodes. +==== + ===== Select [cols="1h,1,2"] @@ -1237,6 +1251,12 @@ Math.hypot(...a) ---- ==== +[TIP] +.Authoring Tip +==== +To get the squared length of stem:[a], use `math/dot` with stem:[a] provided to its both input value sockets. +==== + ===== Normalize [cols="1h,1,2"] @@ -1264,6 +1284,12 @@ Normalizing a zero-length vector **MUST** return a NaN vector. Both input value sockets **MUST** have the same type. +[NOTE] +.Implementation Note +==== +This operation is frequently used with both input value sockets connected to the same output value socket to compute the squared length of a vector. +==== + ===== Cross Product [cols="1h,1,2"] @@ -2895,7 +2921,7 @@ The inputs to these steps are the `pointer` configuration value, the template pa | `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) .2+| Output value sockets | `T value` | The resolved property value -| `bool isValid` | True if the property value can be resolved, false otherwise +| `bool isValid` | True if the property value can be resolved; false otherwise |=== [CAUTION] From 3e8791fdecec91a0cb44d242e9ba44675f972d64 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 27 Jan 2025 00:00:00 +0000 Subject: [PATCH 48/80] Refine math node definitions --- .../KHR_interactivity/Specification.adoc | 161 +++++++++++++----- 1 file changed, 119 insertions(+), 42 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 69819a8dbb..608ca425b7 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -667,7 +667,7 @@ See `math/matmul` for matrix multiplication. | `floatN value` | latexmath:[\begin{cases} \mathit{NaN} & \text{if } a = \pm \infty \text{ or } b = \pm 0 \\ a & \text{if } a \ne \pm \infty \text{ and } b = \pm \infty \\ - a - (b \cdot trunc(\frac{a}{b})) & \text{otherwise} + a - (b \cdot \operatorname{trunc}(\frac{a}{b})) & \text{otherwise} \end{cases}] |=== @@ -739,7 +739,7 @@ Math.max(a, b) | `floatN b` | First boundary | `floatN c` | Second boundary | Output value sockets -| `floatN value` | latexmath:[min(max(a, min(b, c)), max(b, c))] +| `floatN value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] |=== This node relies on `math/min` and `math/max` nodes defined above. @@ -758,7 +758,7 @@ This operation correctly handles a case when stem:[b] is greater than stem:[c]. | Input value sockets | `floatN a` | Value to saturate | Output value sockets -| `floatN value` | latexmath:[min(max(a, 0), 1)] +| `floatN value` | latexmath:[\min(\max(a, 0), 1)] |=== ===== Interpolate @@ -778,6 +778,8 @@ This operation correctly handles a case when stem:[b] is greater than stem:[c]. If any input value is _NaN_, the output value is false. +For the purposes of these nodes, negative zero is equal to positive zero. + ===== Equality [cols="1h,1,2"] @@ -953,8 +955,9 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Angle | Output value sockets | `floatN value` | latexmath:[\begin{cases} - sin(a) & \text{if } a \ne \pm\infty \\ - \mathit{NaN} & \text{if } a = \pm\infty + \pm0 & \text{if } a = \pm0 \\ + \mathit{NaN} & \text{if } a = \pm\infty \\ + \sin(a) & \text{otherwise} \end{cases}] |=== @@ -967,8 +970,9 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Angle | Output value sockets | `floatN value` | latexmath:[\begin{cases} - cos(a) & \text{if } a \ne \pm\infty \\ - \mathit{NaN} & \text{if } a = \pm\infty + +1 & \text{if } a = \pm0 \\ + \mathit{NaN} & \text{if } a = \pm\infty \\ + \cos(a) & \text{otherwise} \end{cases}] |=== @@ -981,8 +985,9 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Angle | Output value sockets | `floatN value` | latexmath:[\begin{cases} - tan(a) & \text{if } a \ne \pm\infty \\ - \mathit{NaN} & \text{if } a = \pm\infty + \pm0 & \text{if } a = \pm0 \\ + \mathit{NaN} & \text{if } a = \pm\infty \\ + \tan(a) & \text{otherwise} \end{cases}] |=== @@ -1002,8 +1007,9 @@ The closest representable argument values would likely produce latexmath:[\pm163 | `floatN a` | Sine value | Output value sockets | `floatN value` | latexmath:[\begin{cases} - arcsin(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\] & \text{if } \|a\| \le 1 \\ - \mathit{NaN} & \text{if } \|a\| \gt 1 + \pm0 & \text{if } a = \pm0 \\ + \mathit{NaN} & \text{if } \|a\| \gt 1 \\ + \arcsin(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\] & \text{otherwise} \end{cases}] |=== @@ -1016,8 +1022,9 @@ The closest representable argument values would likely produce latexmath:[\pm163 | `floatN a` | Cosine value | Output value sockets | `floatN value` | latexmath:[\begin{cases} - arccos(a) \in [0; \pi\] & \text{if } \|a\| \le 1 \\ - \mathit{NaN} & \text{if } \|a\| \gt 1 + +0 & \text{if } a = 1 \\ + \mathit{NaN} & \text{if } \|a\| \gt 1 \\ + \arccos(a) \in [0; \pi\] & \text{otherwise} \end{cases}] |=== @@ -1029,9 +1036,19 @@ The closest representable argument values would likely produce latexmath:[\pm163 | Input value sockets | `floatN a` | Tangent value | Output value sockets -| `floatN value` | latexmath:[arctan(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\]] +| `floatN value` | latexmath:[\begin{cases} + \pm0 & \text{if } a = \pm0 \\ + \pm\frac{\pi}{2} & \text{if } a = \pm\infty \\ + \arctan(a) \in [-\frac{\pi}{2}; \frac{\pi}{2}\] & \text{otherwise} + \end{cases}] |=== +[NOTE] +.Authoring Note +==== +When stem:[a] is infinite, the returned value is an implementation-specific approximation of latexmath:[\pm\frac{\pi}{2}]. +==== + ===== Arctangent 2 [cols="1h,1,2"] @@ -1041,10 +1058,20 @@ The closest representable argument values would likely produce latexmath:[\pm163 | `floatN a` | Y coordinate | `floatN b` | X coordinate | Output value sockets -| `floatN value` | Angle between the positive X-axis and the vector from the stem:[(0, 0)] origin to the stem:[(X, Y)] point on a 2D plane +| `floatN value` | Angle between the positive X-axis and the vector from the stem:[(0, 0)] origin to the stem:[(X, Y)] point on a 2D plane; see the description for details |=== -Zero and infinity argument values **MUST** be handled according to the <> standard. +This function is defined as the **atan2** operation from the <> standard including return values for all special cases. + +[NOTE] +.Implementation Note +==== +This definition also matches the <> standard so the node is implementable in ECMAScript via the following expression: +[source,js] +---- +Math.atan2(a, b) +---- +==== ==== Hyperbolic Nodes @@ -1060,7 +1087,11 @@ If any input value component is _NaN_, the corresponding output value component | Input value sockets | `floatN a` | Hyperbolic angle value | Output value sockets -| `floatN value` | latexmath:[\dfrac{e^a-e^{-a}}{2}] +| `floatN value` | latexmath:[\begin{cases} + \pm0 & \text{if } a = \pm0 \\ + \pm\infty & \text{if } a = \pm\infty \\ + \sinh(a) & \text{otherwise} + \end{cases}] |=== ===== Hyperbolic Cosine @@ -1071,7 +1102,11 @@ If any input value component is _NaN_, the corresponding output value component | Input value sockets | `floatN a` | Hyperbolic angle value | Output value sockets -| `floatN value` | latexmath:[\dfrac{e^a+e^{-a}}{2}] +| `floatN value` | latexmath:[\begin{cases} + +1 & \text{if } a = \pm0 \\ + +\infty & \text{if } a = \pm\infty \\ + \cosh(a) & \text{otherwise} + \end{cases}] |=== ===== Hyperbolic Tangent @@ -1082,7 +1117,11 @@ If any input value component is _NaN_, the corresponding output value component | Input value sockets | `floatN a` | Hyperbolic angle value | Output value sockets -| `floatN value` | latexmath:[\dfrac{e^a-e^{-a}}{e^a+e^{-a}}] +| `floatN value` | latexmath:[\begin{cases} + \pm0 & \text{if } a = \pm0 \\ + \pm1 & \text{if } a = \pm\infty \\ + \tanh(a) & \text{otherwise} + \end{cases}] |=== ===== Inverse Hyperbolic Sine @@ -1093,7 +1132,12 @@ If any input value component is _NaN_, the corresponding output value component | Input value sockets | `floatN a` | Hyperbolic sine value | Output value sockets -| `floatN value` | latexmath:[ln(a+\sqrt{a^2+1})] +| `floatN value` | latexmath:[\begin{cases} + \pm0 & \text{if } a = \pm0 \\ + \pm\infty & \text{if } a = \pm\infty \\ + \operatorname{arsinh}(a) & \text{otherwise} + \end{cases}] +| `floatN value` | latexmath:[] |=== ===== Inverse Hyperbolic Cosine @@ -1105,8 +1149,10 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Hyperbolic cosine value | Output value sockets | `floatN value` | latexmath:[\begin{cases} - ln(a+\sqrt{a^2-1}) & \text{if } a \ge 1 \\ - \mathit{NaN} & \text{if } a \lt 1 + \mathit{NaN} & \text{if } a \lt 1 \\ + +0 & \text{if } a = 1 \\ + +\infty & \text{if } a = +\infty \\ + \operatorname{arcosh}(a) & \text{otherwise} \end{cases}] |=== @@ -1119,9 +1165,10 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Hyperbolic tangent value | Output value sockets | `floatN value` | latexmath:[\begin{cases} - \dfrac{1}{2}ln\dfrac{1+a}{1-a} & \text{if } \|a\| \lt 1 \\ + \mathit{NaN} & \text{if } \|a\| \gt 1 \\ \pm\infty & \text{if } a = \pm1 \\ - \mathit{NaN} & \text{if } \|a\| \gt 1 + \pm0 & \text{if } a = \pm0 \\ + \operatorname{artanh}(a) & \text{otherwise} \end{cases}] |=== @@ -1129,7 +1176,7 @@ If any input value component is _NaN_, the corresponding output value component These all operate component-wise. The description is per component. -If any input value component is _NaN_, the corresponding output value component is also _NaN_. +If any input value component is _NaN_, the corresponding output value component is also _NaN_ for all nodes except `math/pow`. ===== Exponent @@ -1139,7 +1186,12 @@ If any input value component is _NaN_, the corresponding output value component | Input value sockets | `floatN a` | Power value | Output value sockets -| `floatN value` | stem:[e^a] +| `floatN value` | latexmath:[\begin{cases} + +0 & \text{if } a = -\infty \\ + +1 & \text{if } a = \pm0 \\ + +\infty & \text{if } a = +\infty \\ + e^a & \text{otherwise} + \end{cases}] |=== ===== Natural Logarithm @@ -1151,9 +1203,11 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Argument value | Output value sockets | `floatN value` | latexmath:[\begin{cases} - ln(a) & \text{if } a \gt 0 \\ - -\infty & \text{if } a = 0 \\ - \mathit{NaN} & \text{if } a \lt 0 + \mathit{NaN} & \text{if } a \lt 0 \\ + -\infty & \text{if } a = \pm0 \\ + +0 & \text{if } a = +1 \\ + +\infty & \text{if } a = +\infty \\ + \log_e(a) & \text{otherwise} \end{cases}] |=== @@ -1166,9 +1220,11 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Argument | Output value sockets | `floatN value` | latexmath:[\begin{cases} - log_2(a) & \text{if } a \gt 0 \\ - -\infty & \text{if } a = 0 \\ - \mathit{NaN} & \text{if } a \lt 0 + \mathit{NaN} & \text{if } a \lt 0 \\ + -\infty & \text{if } a = \pm0 \\ + +0 & \text{if } a = +1 \\ + +\infty & \text{if } a = +\infty \\ + \log_2(a) & \text{otherwise} \end{cases}] |=== @@ -1181,9 +1237,11 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Argument | Output value sockets | `floatN value` | latexmath:[\begin{cases} - log_{10}(a) & \text{if } a \gt 0 \\ - -\infty & \text{if } a = 0 \\ - \mathit{NaN} & \text{if } a \lt 0 + \mathit{NaN} & \text{if } a \lt 0 \\ + -\infty & \text{if } a = \pm0 \\ + +0 & \text{if } a = +1 \\ + +\infty & \text{if } a = +\infty \\ + \log_{10}(a) & \text{otherwise} \end{cases}] |=== @@ -1196,8 +1254,10 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Radicand | Output value sockets | `floatN value` | latexmath:[\begin{cases} - \sqrt{a} & \text{if } a \ge 0 \\ - \mathit{NaN} & \text{if } a \lt 0 + \mathit{NaN} & \text{if } a \lt 0 \\ + \pm0 & \text{if } a = \pm0 \\ + +\infty & \text{if } a = +\infty \\ + \sqrt[2\]{a} & \text{otherwise} \end{cases}] |=== @@ -1209,7 +1269,11 @@ If any input value component is _NaN_, the corresponding output value component | Input value sockets | `floatN a` | Radicand | Output value sockets -| `floatN value` | latexmath:[\sqrt[3\]{a}] +| `floatN value` | latexmath:[\begin{cases} + \pm0 & \text{if } a = \pm0 \\ + \pm\infty & \text{if } a = \pm\infty \\ + \sqrt[3\]{a} & \text{otherwise} + \end{cases}] |=== ===== Power @@ -1221,10 +1285,23 @@ If any input value component is _NaN_, the corresponding output value component | `floatN a` | Base | `floatN b` | Exponent | Output value sockets -| `floatN value` | stem:[a^b] +| `floatN value` | stem:[a^b]; see the description for details |=== -Zero and infinity argument values **MUST** be handled according to the <> standard. +This function is defined as the **pow** operation from the <> standard with the following changes applied: + +- latexmath:[\mathit{NaN} ^ {\pm0} = 1] +- latexmath:[+1 ^ {\pm\infty}], latexmath:[-1 ^ {\pm\infty}], and latexmath:[\pm1 ^ \mathit{NaN}] are latexmath:[\mathit{NaN}] + +[NOTE] +.Implementation Note +==== +This definition matches the <> standard so the node is implementable in ECMAScript via the following expression: +[source,js] +---- +a ** b +---- +==== ==== Vector Nodes @@ -1800,7 +1877,7 @@ This is implementable in ECMAScript via the following expression: | `int b` | Divisor | Output value sockets | `int value` | latexmath:[\begin{cases} - a - (b \cdot trunc(\frac{a}{b})) & \text{if } b \ne 0 \\ + a - (b \cdot \operatorname{trunc}(\frac{a}{b})) & \text{if } b \ne 0 \\ 0 & \text{if } b = 0 \end{cases}] |=== @@ -1849,7 +1926,7 @@ This is implementable in ECMAScript via the following expression: | `int b` | First boundary | `int c` | Second boundary | Output value sockets -| `int value` | latexmath:[min(max(a, min(b, c)), max(b, c))] +| `int value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] |=== [NOTE] From 02ca19d85ea8a5f2380df4d13c1702c4c854908d Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 3 Feb 2025 00:00:00 +0000 Subject: [PATCH 49/80] Refine vector and matrix nodes --- .../KHR_interactivity/Specification.adoc | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 608ca425b7..5b0151becb 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -635,7 +635,7 @@ a < 0 ? -Math.round(-a) : Math.round(a) | `floatN value` | Product, stem:[a * b] |=== -For matrix arguments, this operation performs per-component multiplication. +For matrix arguments, this operation performs per-element multiplication. [NOTE] .Authoring Note @@ -1305,7 +1305,7 @@ a ** b ==== Vector Nodes -If any input value component is _NaN_, the output value is _NaN_ or a vector of NaNs. +See individual node definitions for handling special floating-point values. ===== Length @@ -1315,9 +1315,23 @@ If any input value component is _NaN_, the output value is _NaN_ or a vector of | Input value sockets | `float{2\|3\|4} a` | Vector | Output value sockets -| `float value` | Length of stem:[a], e.g., stem:[sqrt(a_x^2 + a_y^2)] for `float2` +| `float value` | Length of stem:[a], e.g., stem:[sqrt(a_x^2 + a_y^2)] for `float2`; see the description for details |=== +If any input value component is positive or negative infinity, the output value is positive infinity. + +If none of the input value components are positive or negative infinity and any input value component is NaN, the output value is NaN. + +If all input value components are positive or negative zeros, the output value is a positive zero. + +If all input value components are finite, the output value is an approximation of the square root of the sum of the input value component squares. + +[NOTE] +.Implementation Note +==== +This definition matches the **hypot** operation from the <> standard including return values for all special cases. +==== + [TIP] .Implementation Tip ==== @@ -1331,7 +1345,7 @@ Math.hypot(...a) [TIP] .Authoring Tip ==== -To get the squared length of stem:[a], use `math/dot` with stem:[a] provided to its both input value sockets. +To get the squared length of stem:[a], use `math/dot` with stem:[a] provided to its both input value sockets. Note that this approach will produce NaN if any vector component is NaN regardless of other components. ==== ===== Normalize @@ -1341,11 +1355,18 @@ To get the squared length of stem:[a], use `math/dot` with stem:[a] provided to | Operation | `math/normalize` | Vector normalization | Input value sockets | `float{2\|3\|4} a` | Vector -| Output value sockets -| `floatN value` | Vector in the same direction as stem:[a] but with a unit length, e.g., latexmath:[\dfrac{\vec{a}}{\sqrt{a_x^2 + a_y^2}}] for `float2` +.2+| Output value sockets +| `float{2\|3\|4} value` | Vector in the same direction as stem:[a] but with a unit length, e.g., latexmath:[\dfrac{\vec{a}}{\sqrt{a_x^2 + a_y^2}}] for `float2`; see the description for details +| `bool isValid` | True if the output vector value has a unit length after normalization; false otherwise |=== -Normalizing a zero-length vector **MUST** return a NaN vector. +The output values are computed as follows: + +1. Let _length_ be the output value of the `math/length` operation on stem:[a] as defined above. + +2. If _length_ is zero, NaN, or positive infinity, the `isValid` output value is false and the `value` output value is a vector of the same type as stem:[a] with all components set to positive zeros. + +3. If _length_ is a positive finite number, the `isValid` output value is true and the `value` output value is a vector of the same type as stem:[a] constructed by dividing each component of stem:[a] by _length_. ===== Dot Product @@ -1361,6 +1382,8 @@ Normalizing a zero-length vector **MUST** return a NaN vector. Both input value sockets **MUST** have the same type. +Since this operation is a shortcut for the combination of multiplications and additions, NaN and infinity values are propagated accordingly. + [NOTE] .Implementation Note ==== @@ -1379,6 +1402,8 @@ This operation is frequently used with both input value sockets connected to the | `float3 value` | Cross product of stem:[a] and stem:[b], i.e., stem:[(a_y * b_z - a_z * b_y, a_z * b_x - a_x * b_z, a_x * b_y - a_y * b_x)] |=== +Since this operation is a shortcut for the combination of multiplications and subtractions, NaN and infinity values are propagated accordingly. + ===== Rotate 2D [cols="1h,1,2"] @@ -1453,6 +1478,8 @@ If the vector stem:[b] is not unit, rotation results **MAY** be undefined. The input and output value sockets have the same type. +This operation only reorders the matrix elements without inspecting or altering their values. + ===== Determinant [cols="1h,1,2"] @@ -1464,6 +1491,8 @@ The input and output value sockets have the same type. | `float value` | Determinant of stem:[a] |=== +Since this operation is a shortcut for the combination of multiplications, subtractions, and additions, NaN and infinity values are propagated accordingly. + ===== Inverse [cols="1h,1,2"] @@ -1471,11 +1500,20 @@ The input and output value sockets have the same type. | Operation | `math/inverse` | Inverse operation | Input value sockets | `float{2x2\|3x3\|4x4} a` | Matrix to inverse -| Output value sockets -| `float{2x2\|3x3\|4x4} value` | Matrix that is the inverse of stem:[a] +.2+| Output value sockets +| `float{2x2\|3x3\|4x4} value` | Matrix that is the inverse of stem:[a]; see the description for details +| `bool isValid` | True if the input matrix is invertible; false otherwise |=== -The input and output value sockets have the same type. +The `value` input value socket and `value` output value socket have the same type. + +The output values are computed as follows: + +1. Let _determinant_ be the output value of the `math/determinant` operation on stem:[a] as defined above. + +2. If _determinant_ is zero, NaN, or infinity, the `isValid` output value is false and the `value` output value is a matrix of the same type as stem:[a] with all elements set to positive zeros. + +3. If _determinant_ is a finite number not equal to zero, the `isValid` output value is true and the `value` output value is a matrix that the inverse of stem:[a]. ===== Multiplication @@ -1493,10 +1531,12 @@ Both input value sockets **MUST** have the same type. The output value socket has the same type as the input value sockets. +Since this operation is a shortcut for the combination of multiplications and additions, NaN and infinity values are propagated accordingly. + [NOTE] .Authoring Note ==== -See `math/mul` for per-component multiplication. +See `math/mul` for per-element multiplication. ==== ==== Swizzle Nodes From a873ea79c8e8bf17bf50911102e5bd217822f369 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 10 Feb 2025 00:00:00 +0000 Subject: [PATCH 50/80] Clarify ordering of inline value components --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 5b0151becb..8befc8256b 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3679,6 +3679,10 @@ The `value` property is an array that defines the initial variable value. If the | `int` | 1 | Integer zero |=== +Values for vector types use the XYZW order of components, that is X component is stored in the array element with index 0, Y component is stored in the array element with index 1, and so forth. + +Values for matrix types use the column-major order of elements. For example, elements of a 2x2 matrix are stored as `[c0r0, c0r1, c1r0, c1r1]`, where `c0r0` is the element in the first column and first row, `c0r1` is the element in the first column and second row, and so forth. + If the `value` property array length does not match the array length for the specified type, the variable is invalid and the graph **MUST** be rejected. If the variable type is **bool** and the only array element is not a JSON boolean literal, i.e., neither `true` nor `false`, the variable is invalid and the graph **MUST** be rejected. From 82bae0911c3723b8e7ed8818b919c2918f3cbd57 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 10 Feb 2025 00:00:00 +0000 Subject: [PATCH 51/80] Clarify that strings are case-sensitive --- .../KHR_interactivity/Specification.adoc | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 8befc8256b..30b56d8502 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3627,6 +3627,8 @@ This example defines type `0` as *float2*, type `1` as *int*, and type `2` as *f The value of the `signature` property **MUST** be one of the value types defined in this extension specification or `"custom"`. In the latter case, the custom type semantics **MUST** be provided by an additional extension. +Values of the `signature` property are case-sensitive. + Non-custom signatures **MUST NOT** appear more than once in this array; if two or more entries of the `types` array have the same non-custom signature, the graph is invalid and **MUST** be rejected. Extensions or extras present on the types defined by this Specification do not change type semantics. [NOTE] @@ -3726,6 +3728,8 @@ The event id is an application-specific event identifier recognized by the execu The properties of the `values` object define ids and the values of those properties define types and optional initial values of the value sockets associated with the event. If the `values` object is undefined, the event has no associated value sockets. +Socket ids defined by the properties of the `values` object are case-sensitive. + The type of the event value socket is determined by the **REQUIRED** `type` property that points to the element of the `types` array. If the `type` property is undefined or its value is negative or greater than or equal to the length of the `types` array, the event is invalid and the graph **MUST** be rejected. The `value` property of the event value socket has the same syntax and semantics as the `value` property of variable definitions (see the previous section). @@ -3750,12 +3754,18 @@ This example defines declaration `0` as `math/min` and declaration `1` as `varia The `op` property is **REQUIRED**; it contains the operation identifier; if this property is undefined, the declaration is invalid and the graph **MUST** be rejected. +Values of the `op` property are case-sensitive. + If the operation is not defined by this Specification, the `extension` property **MUST** be defined and it contains the additional interactivity extension name that defines the operation. If the `extension` property is not defined and the operation is not defined by this Specification, the declaration is invalid and the graph **MUST** be rejected. +Values of the `extension` property are case-sensitive. + If the operation is defined in an additional interactivity extension and it uses input value sockets, the `inputValueSockets` object **MUST** be present. Its properties define ids and the values of its properties define types of the input value sockets. If the `inputValueSockets` object is undefined, the operation has no input value sockets. If the operation is defined in an additional interactivity extension and it uses output value sockets, the `outputValueSockets` object **MUST** be present. Its properties define ids and the values of its properties define types of the output value sockets. If the `outputValueSockets` object is undefined, the operation has no output value sockets. +Socket ids defined by the properties of the `inputValueSockets` and `outputValueSockets` objects are case-sensitive. + If the `extension` property is undefined, the operation with all its value sockets is assumed to be provided by this Specification and therefore `inputValueSockets` and `outputValueSockets` objects **MUST NOT** be defined. [NOTE] @@ -3881,6 +3891,8 @@ Some nodes, e.g., `pointer/get` or `variable/get`, define their input value sock The values of the `values` object properties are JSON objects that define effective input value socket types and value sources. Each value source is either an inline constant value, a <> value, or a reference to another node's output value socket. If no source is defined or if the socket type does not match the declaration, the node is invalid and the graph **MUST** be rejected. +Socket ids defined by the properties of the `values` object are case-sensitive. + Some nodes have multiple variants to support the same operation on different input value socket types, e.g., math operations like `math/add` support all numeric types including integers, vectors, and matrices. Therefore, effective input value socket types **MAY** be needed to fully resolve the operation. [NOTE] @@ -3935,6 +3947,8 @@ This ensures that value sockets do not form loops and simplifies input value soc If the `socket` property is defined, it **MUST** correspond to an output value socket existing in the referenced node, otherwise the current node is invalid and the graph **MUST** be rejected. If the `socket` property is undefined, the default socket id `"value"` is used implicitly. Therefore, if the referenced node does not have an output value socket with id `"value"`, the `socket` property **MUST** be defined. +Socket ids referenced by the `socket` property are case-sensitive. + If both `node` and `type` properties are defined, the type referred by the `type` property **MUST** match the type of the referenced output value socket; if the types do not match, the current node is invalid and the graph **MUST** be rejected. [NOTE] @@ -4004,10 +4018,14 @@ Pointers for the output flow sockets are defined in the `flows` object of the no Properties of the `flows` object link output flow sockets of the current node with input flow sockets of other nodes. If an output flow socket id of the current node is not present in the `flows` object, that output flow socket is unconnected and activating it has have no effect. +Socket ids defined by the properties of the `flows` object are case-sensitive. + The `flows` object **MAY** contain properties not corresponding to output flows of the current node; such properties do not affect functionality of the node but their values **MUST** still be validated as described below. Each property of the `flows` object is a JSON object containing a **REQUIRED** `node` property and an **OPTIONAL** `socket` property. The `node` property contains the index of the other node and the `socket` property contains the id of the input flow socket of that node. +Socket ids referenced by the `socket` property are case-sensitive. + The `node` property value **MUST** be greater than the index of the current node and less then the total number of nodes, otherwise the node is invalid and the graph **MUST** be rejected. [NOTE] @@ -4084,6 +4102,8 @@ Some nodes have configuration values of types that cannot be expressed with the Refer to the <> section and to individual node specifications for details regarding configuration validity. +Configuration properties defined by the properties of the `configuration` object are case-sensitive. + Configuration values use JSON arrays similarly to other uses of inline values. [cols="1,2", options="header"] @@ -4095,7 +4115,10 @@ Configuration values use JSON arrays similarly to other uses of inline values. | `string` | Array of one JSON string |=== -The following example instantiates two nodes. The `variable/set` node sets a custom variable with index `0` when the start event happens. +[NOTE] +.Example +==== +The `variable/set` node sets a custom variable with index `0` when the start event happens. ```json "types": [ @@ -4126,6 +4149,7 @@ The following example instantiates two nodes. The `variable/set` node sets a cus } ] ``` +==== [[validation]] = Validation (Informative) From 8fb857861cbda91674cd8102ee7f235c343bdcb9 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 10 Feb 2025 00:00:00 +0000 Subject: [PATCH 52/80] Clarify descriptions of math/clamp and math/saturate --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 30b56d8502..d08d0186b0 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -742,7 +742,7 @@ Math.max(a, b) | `floatN value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] |=== -This node relies on `math/min` and `math/max` nodes defined above. +This node is defined in terms of `math/min` and `math/max` nodes defined above. [NOTE] .Authoring Note @@ -761,6 +761,8 @@ This operation correctly handles a case when stem:[b] is greater than stem:[c]. | `floatN value` | latexmath:[\min(\max(a, 0), 1)] |=== +This node is defined in terms of `math/min` and `math/max` nodes defined above. + ===== Interpolate [cols="1h,1,2"] @@ -1969,6 +1971,8 @@ This is implementable in ECMAScript via the following expression: | `int value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] |=== +This node is defined in terms of `math/min` and `math/max` nodes defined above. + [NOTE] .Authoring Note ==== From be2fa2195c43201929a758c91924c245a2ce9718 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:00:00 +0000 Subject: [PATCH 53/80] Expand flow socket descriptions --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index d08d0186b0..d691b80cfe 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -242,9 +242,9 @@ At the current state of the Specification, the retention of output value socket _Input value sockets_ represent data accessed during the node's execution. For example, it could be arguments of math operations or execution parameters such as iteration count for loop nodes or duration for time-related nodes. Each of these sockets **MUST** either be given an inline constant value in the node object or connected to an output value socket of a different node. The node **MAY** access its input value sockets multiple times during the execution. The runtime **MUST** guarantee that all input value sockets have defined values when the node execution starts. -_Output flow sockets_ represent "`function pointers`" that the node will call to advance the graph execution. For example, bodies and branches of flow control nodes are output flow sockets that drive further execution when certain condition are fulfilled. Output flow sockets **MAY** be unconnected; in such a case graph execution proceeds as if such sockets are no-ops. +_Output flow sockets_ represent "`function pointers`" that the node will call to advance the graph execution. For example, bodies and branches of flow control nodes are output flow sockets that drive further execution when certain condition are fulfilled. An output flow socket is either connected to exactly one input flow socket of another node or unconnected; in the latter case activating the output flow socket is a no-op. -_Input flow sockets_ represent "`methods`" that could be called on the node. For example, flow control nodes (such as loops and branches) usually have an `in` input flow socket that starts node's execution. Additional input flow sockets **MAY** exist such as `reset` for nodes having an internal state. +_Input flow sockets_ represent "`methods`" that could be called on the node. For example, flow control nodes (such as loops and branches) usually have an `in` input flow socket that starts node's execution. Additional input flow sockets **MAY** exist such as `reset` for nodes having an internal state. An input flow socket is either connected to one or more output flow sockets of other nodes or unconnected; in the latter case the node's "`method`" represented by the socket is never called. Input and output value sockets have associated data types, e.g., floats, integers, booleans, etc. From cdabac1a4edfb34385e875e1fbb339dc8f7ca1f7 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:00:00 +0000 Subject: [PATCH 54/80] Fix math/asinh table formatting --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index d691b80cfe..e1713e4933 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1139,7 +1139,6 @@ If any input value component is _NaN_, the corresponding output value component \pm\infty & \text{if } a = \pm\infty \\ \operatorname{arsinh}(a) & \text{otherwise} \end{cases}] -| `floatN value` | latexmath:[] |=== ===== Inverse Hyperbolic Cosine From c2259bf4cb9b4464d461f672695ec6bd869ca4b0 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:00:00 +0000 Subject: [PATCH 55/80] Add math/matCompose --- .../KHR_interactivity/Specification.adoc | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index e1713e4933..79ad3440f3 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1540,6 +1540,48 @@ Since this operation is a shortcut for the combination of multiplications and ad See `math/mul` for per-element multiplication. ==== +===== Transform Compose + +[cols="1h,1,2"] +|=== +| Operation | `math/matCompose` | Compose a 4x4 transform matrix +.3+| Input value sockets +| `float3 translation` | Translation vector +| `float4 rotation` | Rotation quaternion +| `float3 scale` | Scale vector +| Output value sockets +| `float4x4 value` | Matrix composed from the TRS properties +|=== + +Let + +- stem:[t_x], stem:[t_y], and stem:[t_z] be the translation vector components; +- stem:[r_x], stem:[r_y], stem:[r_z], and stem:[r_w] be the rotation quaternion components; +- stem:[s_x], stem:[s_y], and stem:[s_z] be the scale vector components. + +Then the `value` output socket value is computed as follows: + +[stem] ++++++ +((1, 0, 0, t_x), + (0, 1, 0, t_y), + (0, 0, 1, t_z), + (0, 0, 0, 1)) cdot +((1 - 2(r_y^2 + r_z^2), 2(r_xr_y - r_zr_w), 2(r_xr_z + r_yr_w), 0), + (2(r_xr_y + r_zr_w), 1 - 2(r_x^2 + r_z^2), 2(r_yr_z - r_xr_w), 0), + (2(r_xr_z - r_yr_w), 2(r_yr_z + r_xr_w), 1 - 2(r_x^2 + r_y^2), 0), + (0, 0, 0, 1)) cdot +((s_x, 0, 0, 0), + (0, s_y, 0, 0), + (0, 0, s_z, 0), + (0, 0, 0, 1)) = + += ((s_x * (1 - 2(r_y^2 + r_z^2)), s_y * 2(r_xr_y - r_zr_w), s_z * 2(r_xr_z + r_yr_w), t_x), + (s_x * 2(r_xr_y + r_zr_w), s_y * (1 - 2(r_x^2 + r_z^2)), s_z * 2(r_yr_z - r_xr_w), t_y), + (s_x * 2(r_xr_z - r_yr_w), s_y * 2(r_yr_z + r_xr_w), s_z * (1 - 2(r_x^2 + r_y^2)), t_z), + (0, 0, 0, 1)) ++++++ + ==== Swizzle Nodes ===== Combine From c30626a65f2d658e05d59d24e4c1907878427c1c Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Feb 2025 00:00:00 +0000 Subject: [PATCH 56/80] Add math/matDecompose --- .../KHR_interactivity/Specification.adoc | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 79ad3440f3..3062624fd7 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1369,6 +1369,12 @@ The output values are computed as follows: 3. If _length_ is a positive finite number, the `isValid` output value is true and the `value` output value is a vector of the same type as stem:[a] constructed by dividing each component of stem:[a] by _length_. +[TIP] +.Authoring Tip +==== +If the input vector represents a quaternion and the graph expects it to be identity in a case when normalization fails, a `math/select` node could be added to return stem:[(0, 0, 0, 1)] when `isValid` is false. +==== + ===== Dot Product [cols="1h,1,2"] @@ -1540,7 +1546,7 @@ Since this operation is a shortcut for the combination of multiplications and ad See `math/mul` for per-element multiplication. ==== -===== Transform Compose +===== Compose [cols="1h,1,2"] |=== @@ -1582,6 +1588,74 @@ Then the `value` output socket value is computed as follows: (0, 0, 0, 1)) +++++ +Since this operation is a shortcut for the combination of multiplications, subtractions, and additions, NaN and infinity values are propagated accordingly. + +[TIP] +.Authoring Tip +==== +This operation does not implicitly normalize the rotation quaternion. If needed, that step could be done explicitly by adding a `math/normalize` node. +==== + +===== Decompose + +[cols="1h,1,2"] +|=== +| Operation | `math/matDecompose` | Decompose a 4x4 transform matrix to TRS properties +| Input value sockets +| `float4x4 a` | Matrix stem:[A] to decompose +.4+| Output value sockets +| `float3 translation` | Translation vector +| `float4 rotation` | Rotation quaternion +| `float3 scale` | Scale vector +| `bool isValid` | True if the input matrix is decomposable; false otherwise +|=== + +The output values are computed as follows: + +1. If the fourth row of stem:[A] is not stem:[(0, 0, 0, 1)] exactly, set the `isValid` output value to false and goto to the step 11. + +2. Let stem:[s_x], stem:[s_y], and stem:[s_z] be lengths of the first three columns of stem:[A]. For example, stem:[s_x=sqrt(a_{11}^2+a_{21}^2+a_{31}^2)]. + +3. If stem:[s_x], stem:[s_y], or stem:[s_z] are infinite, NaN, or equal to zero, set the `isValid` output value to false and goto to the step 11. + +4. Let stem:[B] be a 3x3 matrix formed by taking the upper-left 3x3 sub-matrix of stem:[A] and dividing each column by stem:[s_x], stem:[s_y], and stem:[s_z] respectively. ++ +[stem] ++++++ +B = ((a_{11}/s_x, a_{12}/s_y, a_{13}/s_z), + (a_{21}/s_x, a_{22}/s_y, a_{23}/s_z), + (a_{31}/s_x, a_{32}/s_y, a_{33}/s_z)) ++++++ + +5. If the absolute value of the determinant of stem:[B] is not close to one within an implementation-defined threshold, set the `isValid` output value to false and goto to the step 11. + +6. Set the `translation` output value to the first three elements of the fourth column of stem:[A], i.e., to stem:[(a_{14}, a_{24}, a_{34})]. + +7. If the determinant of stem:[B] is positive, set the `scale` output value to stem:[(s_x, s_y, s_z)]. + +8. If the determinant of stem:[B] is negative, do one of the following four options. +.. First option: +... set the `scale` output value to latexmath:[(-s_x, s_y, s_z)]; +... negate elements of the first column of stem:[B] in-place. +.. Second option: +... set the `scale` output value to latexmath:[(s_x, -s_y, s_z)]; +... negate elements of the second column of stem:[B] in-place. +.. Third option: +... set the `scale` output value to latexmath:[(s_x, s_y, -s_z)]; +... negate elements of the third column of stem:[B] in-place. +.. Fourth option: +... set the `scale` output value to latexmath:[(-s_x, -s_y, -s_z)]; +... negate all elements of stem:[B] in-place. + +9. Set the `rotation` output value to the unit quaternion corresponding to the rotation matrix stem:[B]. + +10. Set `isValid` output value to true. + +11. If the `isValid` output value is false, +.. set the `translation` output value to stem:[(0, 0, 0)]; +.. set the `rotation` output value to stem:[(0, 0, 0, 1)]; +.. set the `scale` output value to stem:[(1, 1, 1)]. + ==== Swizzle Nodes ===== Combine From 111288d62394301c1e73173f43088df7a1a7bde9 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Feb 2025 00:00:00 +0000 Subject: [PATCH 57/80] Allow unused input value sockets --- .../Khronos/KHR_interactivity/Specification.adoc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 3062624fd7..7493e8bd2a 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -4004,7 +4004,7 @@ A `math/e` node with its declaration. === Input Value Sockets -If the operation has input value sockets, the `values` object **MUST** be defined and its properties **MUST** match the input value socket ids of the declaration; if the properties of the `values` object do not match the declaration, the node is invalid and the graph **MUST** be rejected. If the operation does not have input value sockets, the `values` object **MUST NOT** be defined. +If the operation has input value sockets, the `values` object **MUST** be defined and it *MUST** have properties matching the input value socket ids of the declaration; if the `values` object does not have a corresponding property for each input value socket id, the node is invalid and the graph **MUST** be rejected. The `values` object **MAY** have additional properties not matching the input value socket ids of the operation; such properties have no effect on the operation but their values **MUST** still conform to the JSON schema and other rules defined in this section. If the operation does not have input value sockets, the `values` object **SHOULD NOT** be defined. Some nodes, e.g., `pointer/get` or `variable/get`, define their input value socket ids and/or types based on the node's configuration. Therefore, the configuration **MAY** need to be processed prior to the input value sockets. @@ -4012,12 +4012,14 @@ The values of the `values` object properties are JSON objects that define effect Socket ids defined by the properties of the `values` object are case-sensitive. -Some nodes have multiple variants to support the same operation on different input value socket types, e.g., math operations like `math/add` support all numeric types including integers, vectors, and matrices. Therefore, effective input value socket types **MAY** be needed to fully resolve the operation. +Some nodes have multiple variants to support the same operation on different input value socket types. In all such cases, the variants share the same set of input value socket ids and only their types differ. Therefore, effective input value socket types **MAY** be needed to fully resolve the operation. + +If the operation does not support the input value socket types used by the node, the node is invalid and the graph **MUST** be rejected. [NOTE] .Example ==== -For example, the `math/add` operation defined in this Specification exists only for matching input value socket types so any node that refers to it and uses different types for `a` and `b` input value sockets is invalid. +For example, the `math/add` operation defined in this Specification supports all numeric types, i.e., integers, vectors, and matrices, but only for matching input value socket types. So any node that refers to `math/add` and uses different types for its `a` and `b` input value sockets would be invalid. ==== ==== Inline Values @@ -4450,9 +4452,11 @@ the interactivity graph is invalid and thus cannot be used; this has no effect o .... if "`the input value socket`" object has the `value` property: ..... _assert_ that the `value` property is a non-empty JSON array; ..... validate the `value` property value according to the <> using the `type` property. -7. If the set of input value sockets defined by the declaration referenced by the `declaration` property does not match the set of input value sockets defined by the `values` property with regard to socket ids and types, +7. If any input value socket id defined by the declaration referenced by the `declaration` property is not present in the set of input value sockets defined by the `values` property, +.. _reject the graph_. +8. If the types of the input value sockets defined by the `values` property excluding sockets with ids not defined by the declaration do not match any combination of the input value socket types defined by the declaration, .. _reject the graph_. -8. If "`the node`" object has the `flows` property: +9. If "`the node`" object has the `flows` property: .. _assert_ that the `flows` property is a non-empty JSON object; .. if the `flows` object has more properties than the implementation-specific limit on the number of output flow sockets per node, ... _reject the graph_; From 05e671510a72245f7510f7705142573964c10b3f Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Feb 2025 00:00:00 +0000 Subject: [PATCH 58/80] Adjust value socket definitions --- .../KHR_interactivity/Specification.adoc | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 7493e8bd2a..383dfd9c85 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2911,6 +2911,11 @@ Non-positive or not existing delay index values **MUST NOT** cause any runtime e This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value socket. +==== + This node gets a custom variable value using the variable index provided by the `variable` configuration value. The type `T` is determined by the referenced variable. The variable index **MUST** be a non-negative integer less than the total number of custom variables, otherwise the node is invalid and the graph **MUST** be rejected. @@ -2937,6 +2942,11 @@ This node has no internal state. This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value socket. +==== + This node sets a custom variable value using the variable index provided by the `variable` configuration value and the `value` input value. The type `T` is determined by the referenced variable. The variable index **MUST** be a non-negative integer less than the total number of custom variables, otherwise the node is invalid and the graph **MUST** be rejected. @@ -2976,6 +2986,11 @@ When the `in` input flow is activated: This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value socket. +==== + This node interpolates and updates the specified custom variable multiple times over the specified duration. The type `T` is determined by the referenced variable. The variable index **MUST** be a non-negative integer less than the total number of custom variables, otherwise the node is invalid and the graph **MUST** be rejected. @@ -3165,6 +3180,11 @@ The inputs to these steps are the `pointer` configuration value, the template pa This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + This node gets a glTF Asset Object Model property value using the effective JSON Pointer string derived from the JSON Pointer Template configuration value and the runtime values of the input value sockets. The type `T` is determined by the `type` configuration value which points to the element of the <> array. Input value socket ids are defined by parsing the JSON Pointer Template string as described above. @@ -3228,6 +3248,11 @@ Pointers containing `extras` properties are out of scope of this specification b This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + This node sets a glTF Asset Object Model property value using the effective JSON Pointer string derived from the JSON Pointer Template configuration value and the runtime values of the `` input value sockets. The type `T` is determined by the `type` configuration value which points to the element of the <> array. Input value socket ids are defined by parsing the JSON Pointer Template string as described above. @@ -3286,6 +3311,11 @@ When the `in` input flow is activated: This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + This node interpolates and updates the glTF Asset Object Model property multiple times over the specified duration using the effective JSON Pointer string derived from the JSON Pointer Template configuration value, the runtime values of the `` input value sockets, and several interpolation inputs. The type `T` is determined by the `type` configuration value which points to the element of the <> array. Input value socket ids are defined by parsing the JSON Pointer Template string as described above. @@ -3574,6 +3604,11 @@ If multiple instances of this node exist in the graph, they **MUST** be activate This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + This node is activated when a custom event specified by the `event` configuration value occurs. The types, ids, and semantics of the output value sockets are defined by the custom event index. The `event` configuration value **MUST** be non-negative and less than the total number of custom event definitions, otherwise the node is invalid and the graph **MUST** be rejected. @@ -3608,6 +3643,11 @@ If multiple instances of this node with the same event index exist in the graph, This node does not have a default configuration. ==== +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + This node sends a custom event specified by the `event` configuration value. The types and ids of the input value sockets are defined by the custom event index. [TIP] @@ -4004,9 +4044,9 @@ A `math/e` node with its declaration. === Input Value Sockets -If the operation has input value sockets, the `values` object **MUST** be defined and it *MUST** have properties matching the input value socket ids of the declaration; if the `values` object does not have a corresponding property for each input value socket id, the node is invalid and the graph **MUST** be rejected. The `values` object **MAY** have additional properties not matching the input value socket ids of the operation; such properties have no effect on the operation but their values **MUST** still conform to the JSON schema and other rules defined in this section. If the operation does not have input value sockets, the `values` object **SHOULD NOT** be defined. +If the operation has input value sockets, the `values` object **MUST** be defined and it *MUST** have properties matching the input value socket ids defined by the declaration and/or configuration; if the `values` object does not have a corresponding property for each input value socket id, the node is invalid and the graph **MUST** be rejected. The `values` object **MAY** have additional properties not matching the input value socket ids of the operation; such properties have no effect on the operation but their values **MUST** still conform to the JSON schema and other rules defined in this section. If the operation does not have input value sockets, the `values` object **SHOULD NOT** be defined. -Some nodes, e.g., `pointer/get` or `variable/get`, define their input value socket ids and/or types based on the node's configuration. Therefore, the configuration **MAY** need to be processed prior to the input value sockets. +Some operations, e.g., `pointer/get` or `variable/get`, define their input value socket ids and/or types based on the node's configuration. Therefore, the configuration **MAY** need to be processed prior to the input value sockets. The values of the `values` object properties are JSON objects that define effective input value socket types and value sources. Each value source is either an inline constant value, a <> value, or a reference to another node's output value socket. If no source is defined or if the socket type does not match the declaration, the node is invalid and the graph **MUST** be rejected. @@ -4452,11 +4492,12 @@ the interactivity graph is invalid and thus cannot be used; this has no effect o .... if "`the input value socket`" object has the `value` property: ..... _assert_ that the `value` property is a non-empty JSON array; ..... validate the `value` property value according to the <> using the `type` property. -7. If any input value socket id defined by the declaration referenced by the `declaration` property is not present in the set of input value sockets defined by the `values` property, +7. Let "`the operation inputs`" be the set (or the sets in case of overloaded operations) of input value sockets defined by the declaration and/or derived from the configuration. +8. If any input value socket id present in "`the operation inputs`" is not present in the set of input value sockets defined by the `values` property, .. _reject the graph_. -8. If the types of the input value sockets defined by the `values` property excluding sockets with ids not defined by the declaration do not match any combination of the input value socket types defined by the declaration, +9. If the types of input value sockets defined by the `values` property excluding sockets with ids not present in "`the operation inputs`" do not match any set of the input value socket types present in "`the operation inputs`", .. _reject the graph_. -9. If "`the node`" object has the `flows` property: +10. If "`the node`" object has the `flows` property: .. _assert_ that the `flows` property is a non-empty JSON object; .. if the `flows` object has more properties than the implementation-specific limit on the number of output flow sockets per node, ... _reject the graph_; From 1ffad04683c16368430eec5da542e83d4347982f Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Feb 2025 00:00:00 +0000 Subject: [PATCH 59/80] Tighten validity of configurable nodes --- .../KHR_interactivity/Specification.adoc | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 383dfd9c85..df2f070804 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -415,7 +415,9 @@ Implementations **MAY** restrict the size and complexity of behavior graphs by i * The number of operation declarations * The number of input and output value sockets in operation declarations * The number of nodes -* The number of graph-defined output flow sockets in operations like `flow/sequence`, `flow/switch`, or `flow/multiGate` +* The number of graph-defined output flow sockets in operations like `flow/multiGate` or `flow/sequence` +* The number of configuration-defined output flow sockets in operations like `flow/switch` +* The number of configuration-defined input value sockets in operations like `pointer/get` The graph **MUST** be rejected if it exceeds implementation-defined max values for these properties. @@ -2551,6 +2553,11 @@ The `condition` input value is evaluated each time the node is executed. | `default` | The output flow activated when the `selection` input value is not present in the `cases` configuration array |=== +[CAUTION] +==== +The configuration of this node affects its flow sockets. +==== + The node has zero or more `` output flow sockets corresponding to the elements of the `cases` configuration array. In the default configuration, the `cases` configuration array is empty and the node has only the `default` output flow socket. @@ -2562,19 +2569,20 @@ The following procedure defines output flow sockets generation from the provided .. if `C` is not a literal number or if it is not exactly representable as a 32-bit signed integer, ignore the `cases` property and use the default configuration; .. convert `C` to a base-10 string representation `S` containing only decimal integers (ASCII characters `0x30 ... 0x39`) and a leading minus sign (ASCII character `0x2D`) if `C` is negative; extra leading zeros **MUST NOT** be present; .. add a flow socket `S` to the set of the output flow sockets of this node or ignore it if an output flow socket with the same id has been already added. -3. Proceed with the generated output flow sockets. - -For example: +3. If the number of generated flow sockets plus one exceeds an implementation-defined limit on the maximum number of output flow sockets, the graph **MUST** be rejected. +4. Proceed with the generated output flow sockets. -* if the `cases` configuration array is `[0.5, 1]`, the default configuration is used because `0.5` is not representable as a 32-bit signed integer; -* if the `cases` configuration array is `[-2147483649, 0]`, the default configuration is used because `-2147483649` is not representable as a 32-bit signed integer; -* if the `cases` configuration array is `[-1.0, 0, 1]`, the output socket ids are exactly `"-1"`, `"0"`, and `"1"` because `-1.0` is equal to an integer `-1`; -* if the `cases` configuration array is `[0.1e1, 2, 2]`, the output socket ids are exactly `"1"` and `"2"` because `0.1e1` is equal to an integer `1` and the duplicate entry is ignored. +[NOTE] +.Examples +==== +* If the `cases` configuration array is `[0.5, 1]`, the default configuration is used because `0.5` is not representable as a 32-bit signed integer. +* If the `cases` configuration array is `[-2147483649, 0]`, the default configuration is used because `-2147483649` is not representable as a 32-bit signed integer. +* If the `cases` configuration array is `[-1.0, 0, 1]`, the output socket ids are exactly `"-1"`, `"0"`, and `"1"` because `-1.0` is equal to an integer `-1`. +* If the `cases` configuration array is `[0.1e1, 2, 2]`, the output socket ids are exactly `"1"` and `"2"` because `0.1e1` is equal to an integer `1` and the duplicate entry is ignored. +==== This node has no internal state. -If the number of output flow sockets (as present in JSON) exceeds an implementation-defined limit, the graph **MUST** be rejected. - When the `in` input flow is activated: 1. Evaluate the `selection` input value. @@ -2745,6 +2753,11 @@ When the `isRandom` and `isLoop` configuration values are true, the output flow | `int remainingInputs` | The number of not yet activated input flows |=== +[CAUTION] +==== +The configuration of this node affects its flow sockets. +==== + The node has from zero to 64 input flow sockets with ids assigned sequential non-negative integer decimal numbers depending on the `inputFlows` configuration value. Encoded as base-10 strings, these input flow socket ids contain only decimal integers (ASCII characters `0x30 ... 0x39`); other characters and leading zeros are not used. For example, if `inputFlows` is 3, the input flow socket ids are `"0"`, `"1"`, and `"2"` exactly. @@ -3201,6 +3214,8 @@ If the `pointer` configuration value is not provided, if it is not a string, or If the `type` configuration value is not provided, if it is not a literal number, if it is not exactly representable as a 32-bit signed integer, if it is negative, or if it is greater than or equal to the length of the `types` array, the node is invalid and the graph **MUST** be rejected. +If the number of input value sockets derived from the pointer template string exceeds an implementation-specific limit on the maximum number of input value sockets, the graph **MUST** be rejected. + When this node is activated, i.e., when one of its output value sockets is being accessed: 1. Evaluate all input values. @@ -3261,6 +3276,8 @@ If the `pointer` configuration value is not provided, if it is not a string, if If the `type` configuration value is not provided, if it is not a literal number, if it is not exactly representable as a 32-bit signed integer, if it is negative, or if it is greater than or equal to the length of the `types` array, the node is invalid and the graph **MUST** be rejected. +If the number of input value sockets derived from the pointer template string plus one exceeds an implementation-specific limit on the maximum number of input value sockets, the graph **MUST** be rejected. + If the `value` input value is not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. This is not an error. Implementations **MAY** generate runtime warnings in this case as deemed possible. [NOTE] @@ -3324,6 +3341,8 @@ If the `pointer` configuration value is not provided, if it is not a string, if If the `type` configuration value is not provided, if it is not a literal number, if it is not exactly representable as a 32-bit signed integer, if it is negative, if it is greater than or equal to the length of the `types` array, or if it point to the type entry with `bool` or `int` type signatures, the node is invalid and the graph **MUST** be rejected. +If the number of input value sockets derived from the pointer template string plus four exceeds an implementation-specific limit on the maximum number of input value sockets, the graph **MUST** be rejected. + If the `value` input value or any intermediate interpolated value are not valid for the resolved property, the effective property value becomes implementation-defined and subsequent `pointer/get` evaluations of the property **MAY** return any value of the corresponding type until the property is updated with a valid value. This is not an error. Implementations **MAY** generate runtime warnings in this case as deemed possible. [NOTE] @@ -4464,7 +4483,9 @@ the interactivity graph is invalid and thus cannot be used; this has no effect o ... _reject the graph_; .. if `configuration` object is not valid as defined by the node, ... _reject the graph_. -6. If "`the node`" object has the `values` property: +6. If the node is configurable, the configuration affects the node's sockets, and applying the specified configuration would lead to exceeding any implementation-specific limit, +.. _reject the graph_; +7. If "`the node`" object has the `values` property: .. _assert_ that the `values` property is a non-empty JSON object; .. for each property of the `values` JSON object: ... _assert_ that the property is a JSON object ("`the input value socket`"); @@ -4492,12 +4513,12 @@ the interactivity graph is invalid and thus cannot be used; this has no effect o .... if "`the input value socket`" object has the `value` property: ..... _assert_ that the `value` property is a non-empty JSON array; ..... validate the `value` property value according to the <> using the `type` property. -7. Let "`the operation inputs`" be the set (or the sets in case of overloaded operations) of input value sockets defined by the declaration and/or derived from the configuration. -8. If any input value socket id present in "`the operation inputs`" is not present in the set of input value sockets defined by the `values` property, +8. Let "`the operation inputs`" be the set (or the sets in case of overloaded operations) of input value sockets defined by the declaration and/or derived from the configuration. +9. If any input value socket id present in "`the operation inputs`" is not present in the set of input value sockets defined by the `values` property, .. _reject the graph_. -9. If the types of input value sockets defined by the `values` property excluding sockets with ids not present in "`the operation inputs`" do not match any set of the input value socket types present in "`the operation inputs`", +10. If the types of input value sockets defined by the `values` property excluding sockets with ids not present in "`the operation inputs`" do not match any set of the input value socket types present in "`the operation inputs`", .. _reject the graph_. -10. If "`the node`" object has the `flows` property: +11. If "`the node`" object has the `flows` property: .. _assert_ that the `flows` property is a non-empty JSON object; .. if the `flows` object has more properties than the implementation-specific limit on the number of output flow sockets per node, ... _reject the graph_; From 1493bcd75ab278c245489d922a37d430d90f60ae Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 3 Mar 2025 00:00:00 +0000 Subject: [PATCH 60/80] Add math/switch --- .../KHR_interactivity/Specification.adoc | 106 +++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index df2f070804..33d84fe20c 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -417,7 +417,7 @@ Implementations **MAY** restrict the size and complexity of behavior graphs by i * The number of nodes * The number of graph-defined output flow sockets in operations like `flow/multiGate` or `flow/sequence` * The number of configuration-defined output flow sockets in operations like `flow/switch` -* The number of configuration-defined input value sockets in operations like `pointer/get` +* The number of configuration-defined input value sockets in operations like `pointer/get` or `math/switch` The graph **MUST** be rejected if it exceeds implementation-defined max values for these properties. @@ -891,6 +891,100 @@ To check whether a value is only a negative infinity, combine `math/eq`, `math/n The type `T` represents any supported type including custom types. It **MUST** be the same for the output value socket and the input value sockets stem:[a] and stem:[b]. +===== Switch + +[cols="1h,1,2"] +|=== +| Operation | `math/switch` | Conditionally output one of the input values +| Configuration +| `int[] cases` | The cases on which to perform the switch; empty in the default configuration +.3+| Input value sockets +| `int selection` | The value on which the switch operates +| `T ` | Zero or more input value sockets; `` is an integer decimal number +| `T default` | The value used when the `selection` input value is not present in the `cases` configuration array +| Output value sockets +| `T value` | The value taken from the selected input value socket +|=== + +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + +The node has zero or more `` input value sockets corresponding to the elements of the `cases` configuration array. + +The type `T` represents any supported type including custom types. The type of the `value` output value socket is the same as the type of the `default` input value socket. + +In the default configuration, the `cases` configuration array is empty and the node has only the `default` and `selection` input value sockets. + +The following procedure defines input value sockets generation from the provided configuration: + +1. If the `cases` configuration property is not present or if it is not an array, ignore it and use the default configuration. +2. If the `cases` configuration property is present and it is an array, then for each array element `C`: +.. if `C` is not a literal number or if it is not exactly representable as a 32-bit signed integer, ignore the `cases` property and use the default configuration; ++ +[TIP] +.Implementation Tip +==== +The integer representation check is implementable in ECMAScript via the following expression: +[source,js] +---- +C === (C | 0) +---- +==== +.. convert `C` to a base-10 string representation `S` containing only decimal integers (ASCII characters `0x30 ... 0x39`) and a leading minus sign (ASCII character `0x2D`) if `C` is negative; extra leading zeros **MUST NOT** be present; +.. add a value socket `S` to the set of the input value sockets of this node or ignore it if an input value socket with the same id has been already added. +3. If the number of generated value sockets plus two exceeds an implementation-defined limit on the maximum number of input value sockets, the graph **MUST** be rejected. +4. Proceed with the generated input value sockets. + +[NOTE] +.Examples +==== +* If the `cases` configuration array is `[0.5, 1]`, the default configuration is used because `0.5` is not representable as a 32-bit signed integer. +* If the `cases` configuration array is `[-2147483649, 0]`, the default configuration is used because `-2147483649` is not representable as a 32-bit signed integer. +* If the `cases` configuration array is `[-1.0, 0, 1]`, the output socket ids are exactly `"-1"`, `"0"`, and `"1"` because `-1.0` is equal to an integer `-1`. +* If the `cases` configuration array is `[0.1e1, 2, 2]`, the output socket ids are exactly `"1"` and `"2"` because `0.1e1` is equal to an integer `1` and the duplicate entry is ignored. +==== + +If the nodes's JSON object does not contain all input value sockets generated by the procedure above with the same type as the `default` input value socket, the node is invalid and the graph **MUST** be rejected. + +[NOTE] +.Validation Examples +==== +* If the node does not have `selection` or `default` input value sockets, then the node is invalid. +* If the node has a `selection` input value socket with any type other than integer, then the node is invalid. +* If the default configuration is used and the node has an integer `selection` input value socket and a `default` input value socket of any type, then the node is valid. +* If the `cases` configuration array is `[1, 2]` and the node does not have input value sockets with ids `1` and `2`, then the node is invalid. +* If the `cases` configuration array is `[1, 2]` and the node has an input value socket with id `1` and the same type as the type of the `default` input value socket and an input value socket with id `2` and any other type, then the node is invalid. +==== + +Extra input value sockets with ids not present in the output of the procedure above do not affect the node's operation and validation but they still **MUST** have valid types and value sources. + +[NOTE] +.Validation Examples +==== +* If the default configuration is used and the node has an integer `selection` input value socket, a `default` input value socket of any type, and any other input value socket of any type, then the node is valid. +* If the `cases` configuration array is `[1]` and the node has an integer `selection` input value socket, a `default` input value socket of any type, an input value socket with id `1` and the same type as the type of the `default` input value socket, and any other input value socket of any type, then the node is valid. +==== + +This node has no internal state. + +The `value` output value is computed as follows: + +1. Evaluate all input value sockets. +2. If the `cases` configuration array does not contain the `selection` input value: +.. set the `value` output value to the value of the `default` input value socket. +3. If the `cases` configuration array contains the `selection` input value: +.. set the `value` output value to the value of the input value socket with id equal to the decimal string representation of the `selection` input value. + +[NOTE] +.Operation Examples +==== +* If the default configuration is used, the `value` output value is always the same as the `default` input value. +* If the `cases` configuration array is `[1]` and the `selection` input value is `1`, the `value` output value is the value of the input value socket with id `1`. +* If the `cases` configuration array is `[1]` and the `selection` input value is `2`, the `value` output value is the value of the `default` input value socket even if the the node's JSON has an input value socket with id `2`. +==== + ===== Random [cols="1h,1,2"] @@ -2567,6 +2661,16 @@ The following procedure defines output flow sockets generation from the provided 1. If the `cases` configuration property is not present or if it is not an array, ignore it and use the default configuration. 2. If the `cases` configuration property is present and it is an array, then for each array element `C`: .. if `C` is not a literal number or if it is not exactly representable as a 32-bit signed integer, ignore the `cases` property and use the default configuration; ++ +[TIP] +.Implementation Tip +==== +The integer representation check is implementable in ECMAScript via the following expression: +[source,js] +---- +C === (C | 0) +---- +==== .. convert `C` to a base-10 string representation `S` containing only decimal integers (ASCII characters `0x30 ... 0x39`) and a leading minus sign (ASCII character `0x2D`) if `C` is negative; extra leading zeros **MUST NOT** be present; .. add a flow socket `S` to the set of the output flow sockets of this node or ignore it if an output flow socket with the same id has been already added. 3. If the number of generated flow sockets plus one exceeds an implementation-defined limit on the maximum number of output flow sockets, the graph **MUST** be rejected. From 518b74065872e6e155d2640a9779c7f635f0acda Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 3 Mar 2025 00:00:00 +0000 Subject: [PATCH 61/80] Clarify animation nodes and pointers usage --- .../KHR_interactivity/Specification.adoc | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 33d84fe20c..07b06be1b6 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3512,7 +3512,7 @@ Certain control point values can cause the intermediate output progress value to ==== Animation Control Nodes -===== Animation Play +===== Animation Start [cols="1h,1,2"] |=== @@ -3521,8 +3521,8 @@ Certain control point values can cause the intermediate output progress value to | `in` | The entry flow into this node .4+| Input value sockets | `int animation` | Animation index -| `float startTime` | Start time -| `float endTime` | End time +| `float startTime` | Start time in seconds +| `float endTime` | End time in seconds | `float speed` | Speed multiplier .3+| Output flow sockets | `out` | The flow to be activated if the input values are valid @@ -3547,6 +3547,12 @@ latexmath:[t=\begin{cases} 0 & \text{if } T=0 \\ \end{cases}] +[TIP] +.Authoring Tip +==== +The `/animations/{}/extensions/KHR_interactivity/maxTime` virtual property can be used to query the value of stem:[T] for a given animation. By setting the `endTime` input value socket to that value, the graph can play the animation to completion without statically knowing its duration. +==== + This node has no internal state. When an `animation/start` node is used in the behavior graph, the global graph state includes an implementation-defined _animation state dynamic array_ each element of which contains the following data: @@ -3643,7 +3649,7 @@ When the `in` input flow is activated: | `in` | The entry flow into this node .2+| Input value sockets | `int animation` | Animation index -| `float stopTime` | Stop time +| `float stopTime` | Stop time in seconds .3+| Output flow sockets | `out` | The flow to be activated if the input values are valid | `err` | The flow to be activated if any of the input values is invalid @@ -3824,9 +3830,15 @@ The `isPlaying` read-only property is true when the animation is playing, false The `minTime` and `maxTime` read-only properties represent the timestamps of the first and the last keyframes as stored in the glTF animation object. The values **MUST** be derived from the `min` and `max` properties of the used sampler input accessors. Unused animation samplers, i.e., samplers not referenced by the animation channels, **MUST** be ignored. If the animation object is invalid as defined in the core glTF 2.0 specification, these properties **MUST** return NaNs. +[TIP] +.Authoring Tip +==== +As defined in the base glTF 2.0 specification, animated properties are snapped to the closest keyframes if the requested timestamp is between zero and the timestamp of the first available keyframe. The `minTime` property could be used to query the timestamp of the animation's earliest keyframe data and start the animation from that point if the initial delay potentially present in the animation data needs to be skipped. +==== + The `playhead` read-only property represents the current animation position within the glTF animation data. For valid glTF animations, the property value is equal to the last effective timestamp, so it is always greater than or equal to zero and less than or equal to `maxTime`. Before the animation start, this property value is zero; when the animation stops, the property retains its last value until the animation is restarted. For invalid glTF animations, the property value is always NaN. -The `virtualPlayhead` read-only property represents the current animation position on the infinite timeline that is used for the input value sockets of the `animation/start` and `animation/stop` operations. For valid glTF animations, the property value is equal to the last requested timestamp. Before the animation start, this property is zero; when the animation stops, the property value retains the its last value until the animation is restarted. For invalid glTF animations, the property value is always NaN. +The `virtualPlayhead` read-only property represents the current animation position on the infinite timeline that is used for the input value sockets of the `animation/start` and `animation/stop` operations. For valid glTF animations, the property value is equal to the last requested timestamp. Before the animation start, this property is zero; when the animation stops, the property value retains its last value until the animation is restarted. For invalid glTF animations, the property value is always NaN. The following pointers represent the properties defined in this section. From 9bc12f76c71d1e04cec852abb6242ee0e7121713 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 3 Mar 2025 00:00:00 +0000 Subject: [PATCH 62/80] Add pointer node tips about unsupported pointers --- .../KHR_interactivity/Specification.adoc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 07b06be1b6..fee6e06b96 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3344,6 +3344,12 @@ Pointers containing `extras` properties are out of scope of this specification b - If the `pointer` configuration value is `"/nodes/{myId}/scale"` and the type `T` is `float4`, then the `isValid` output value is false and the `value` output value is `[NaN, NaN, NaN, NaN]`. Note that `myId` input value becomes irrelevant in this case because even if it is valid the Object Model property type does not match the declared type `T`. ==== +[NOTE] +.Authoring Note +==== +The `/meshes/{}/weights` and `/nodes/{}/weights` pointers defined in the glTF Asset Object Model have the `float[]` type that is not supported by this extension. Any `pointer/get` node using those pointers would always have its `isValid` output value as false. +==== + ===== Pointer Set [cols="1h,1,2"] @@ -3405,6 +3411,12 @@ When the `in` input flow is activated: 6. Set the resolved glTF Asset Object Model property to the `value` input value. 7. Activate the `out` output flow. +[NOTE] +.Authoring Note +==== +The `/meshes/{}/weights` and `/nodes/{}/weights` pointers defined in the glTF Asset Object Model have the `float[]` type that is not supported by this extension. Any `pointer/set` node using those pointers would always activate only its `err` output flow. +==== + ===== Pointer Interpolate [cols="1h,1,2"] @@ -3510,6 +3522,12 @@ On each tick, for each entry in the _pointer interpolation state dynamic array_: Certain control point values can cause the intermediate output progress value to be negative or greater than one. This is not an error. ==== +[NOTE] +.Authoring Note +==== +The `/meshes/{}/weights` and `/nodes/{}/weights` pointers defined in the glTF Asset Object Model have the `float[]` type that is not supported by this extension. Any `pointer/interpolate` node using those pointers would always activate only its `err` output flow. +==== + ==== Animation Control Nodes ===== Animation Start From d9bfdb08f0c09c125f588783921d9edceb7ee78c Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 10 Mar 2025 00:00:00 +0000 Subject: [PATCH 63/80] Clarify animations targeting the same properties --- .../Khronos/KHR_interactivity/Specification.adoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index fee6e06b96..8e2979bbff 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3631,6 +3631,20 @@ On each asset animation update, for each entry in the _animation state dynamic a 7. Let the requested timestamp stem:[r] be equal to the _current timestamp_. 8. Compute the effective timestamp stem:[t] from stem:[r] as defined above and apply the glTF animation state at the timestamp stem:[t] to the asset. +If two or more active animations target the same glTF property, it becomes undefined and remains undefined as long as the number of active animations affecting it is greater than one. + +[NOTE] +.Example +==== +Let's say an asset has two animations that target position of the node zero. The first animation lasts 15 seconds and the second animation lasts 5 seconds. The graph starts the first animation, waits 5 seconds, then starts the second animation. Both animations are played to completion once. + +During the first 5 seconds, the position of the node zero is well-defined. It is repeatedly updated by the first animation and could be reliably queried with a `pointer/get` operation. + +During the next 5 seconds (i.e., when both animations are active) the position of the node zero is undefined. Since both animations try to update it at the same time, querying the property with a `pointer/get` operation could return arbitrary values. However, using a `pointer/set` operation on that property would make it well-defined until the next animation update. + +During the last 5 seconds, the position of the node zero is well-defined again. +==== + ===== Animation Stop [cols="1h,1,2"] From 5ef1fad30cb0862d7ff5c680e85e61fa9a258316 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:00:00 +0000 Subject: [PATCH 64/80] Add variable/setMultiple --- .../KHR_interactivity/Specification.adoc | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 8e2979bbff..0e12cea1b8 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -417,7 +417,7 @@ Implementations **MAY** restrict the size and complexity of behavior graphs by i * The number of nodes * The number of graph-defined output flow sockets in operations like `flow/multiGate` or `flow/sequence` * The number of configuration-defined output flow sockets in operations like `flow/switch` -* The number of configuration-defined input value sockets in operations like `pointer/get` or `math/switch` +* The number of configuration-defined input value sockets in operations like `pointer/get`, `math/switch`, or `variable/setMultiple` The graph **MUST** be rejected if it exceeds implementation-defined max values for these properties. @@ -3077,6 +3077,57 @@ When the `in` input flow is activated: 3. Set the custom variable with the `variable` configuration value index to the `value` input value. 4. Activate the `out` output flow. +===== Variable Set Multiple + +[cols="1h,1,2"] +|=== +| Operation | `variable/setMultiple` | Set multiple variables +| Configuration +| `int[] variables` | The array containing variable indices +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| `Ti ` | One or more input value sockets; `` refers to the corresponding variable index and `Ti` refers to its type +| Output flow sockets +| `out` | The flow to be activated after the values are set +|=== + +[CAUTION] +==== +This node does not have a default configuration. +==== + +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + +This node sets multiple custom variable values using the variable indices provided by the `variables` configuration value and the corresponding input values. + +The following procedure defines input value sockets generation from the provided configuration: + +1. If the `variables` configuration property is not present or if it is not a non-empty array, the node is invalid and the graph **MUST** be rejected. +2. If the `variables` configuration property is present and it is a non-empty array, then for each array element `V`: +.. if `V` is not a valid custom variable index, i.e., it is not a non-negative integer less than the total number of custom variables, the node is invalid and the graph **MUST** be rejected; +.. convert `V` to a base-10 string representation `S` containing only decimal integers (ASCII characters `0x30 ... 0x39`); extra leading zeros **MUST NOT** be present; +.. add a value socket with id `S` and the type corresponding to the type of the custom variable with index `V` to the set of the input value sockets of this node or ignore it if an input value socket with the same id has been already added. +3. If the number of generated value sockets exceeds an implementation-defined limit on the maximum number of input value sockets, the graph **MUST** be rejected. +4. Proceed with the generated input value sockets. + +If the nodes's JSON object does not contain all input value sockets generated by the procedure above with proper types, the node is invalid and the graph **MUST** be rejected. + +Extra input value sockets with ids not present in the output of the procedure above do not affect the node's operation and validation but they still **MUST** have valid types and value sources. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. For each unique element `V` of the `variables` configuration array: +.. if the _variable interpolation state dynamic array_ (defined below) contains an entry with the variable index `V`, remove the entry from that array; +.. set the custom variable with the index `V` to the value of the `V` input value socket. +3. Activate the `out` output flow. + ===== Variable Interpolate [cols="1h,1,2"] From 38bc70438364d365289eb11ad28d7ced36bed0fe Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:00:00 +0000 Subject: [PATCH 65/80] Update pointer template syntax --- .../KHR_interactivity/Specification.adoc | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 0e12cea1b8..d15fc60b9e 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3235,12 +3235,12 @@ Both pointers are functional even if the nodes do not belong to the current glTF Implementations that import glTF assets into pre-existing scenes may need to maintain mappings between their internal objects and glTF objects defined in the asset. If the implementation's coordinate system is different from the one used in glTF, extra runtime conversions may be necessary for properties that depend on the XYZ axes. ==== -When a behavior graph is loaded, all JSON Pointer Templates **MUST** be processed as described in the following sections. If a pointer template contains path segments wrapped in curly brackets, called _template parameters_, they define input value socket ids (with the curly brackets stripped) for the node. These template parameters **MUST NOT** be empty or contain `/`, `{`, `}`, or `~` characters; the same parameter **MUST NOT** be used more than once within a template string. If a JSON property used in the Pointer Template string contains curly brackets, they **MUST** be escaped with a back slash character (`\`, `0x5C`). +When a behavior graph is loaded, all JSON Pointer Templates **MUST** be processed as described in the following sections. If a pointer template contains path segments wrapped in curly brackets, called _template parameters_, they define input value socket ids (with the curly brackets stripped) for the node. These template parameters **MUST NOT** be empty; the same parameter **MUST NOT** be used more than once within a template string. If used literally, curly brackets **MUST** be doubled, i.e, `{` **MUST** be written as `{{`, and `}` **MUST** be written as `}}` when used within path segments; curly brackets **MUST NOT** be used within parameters. Tilde (`~`) and forward slash (`/`) characters used in path segments or parameters **MUST** be encoded as `~0` and `~1` respectively as defined in <>. [NOTE] .Implementation Note ==== -None of the glTF properties currently defined in Khronos specifications contain curly brackets in their names but such properties can exist in arbitrary glTF assets within vendor-specific extensions or `extras` objects. +None of the glTF properties currently defined in Khronos specifications contain curly brackets, forward slash, or tilde in their names but such properties can exist in arbitrary glTF assets within vendor-specific extensions or `extras` objects. ==== When an Object Model Access node is activated, its JSON Pointer Template and input values (if present) are used to generate the effective JSON Pointer string value. @@ -3249,20 +3249,28 @@ If the property being accessed is also affected by a currently active animation, ===== JSON Pointer Template Parsing -The input to these steps is the `pointer` string configuration value; the output includes a boolean validity flag and a template parameter array. Implementations **MAY** optimize these steps as long as such optimizations do not change the output. +The input to these steps is the `pointer` string configuration value; the output includes a boolean validity flag and a template parameter array. Each template parameter array entry consists of a string representing the corresponding input value socket id and the parameter substring location (e.g., start and end offsets) in the JSON Pointer Template. + +Implementations **MAY** optimize these steps as long as such optimizations do not change the output. 1. Let the validity flag be true and the template parameter array be empty. -2. If the pointer template string is not a syntactically valid JSON Pointer as defined in <> regardless of the pointer applicability to the glTF asset, reject the pointer template string with a syntax error. +2. If the pointer template string is not a syntactically valid JSON Pointer as defined in <> regardless of the pointer applicability to the glTF asset and/or template parameters, reject the pointer template string with a syntax error. 3. Split the pointer template string at all matches of the forward slash character (`/`, `0x2F`). This step produces a path segment array consisting of a substring before the first match, substrings between the matches, and a substring after the last match. 4. For each path segment substring produced during step 3: -.. If the substring starts with a left curly bracket (`{`, `0x7B`): -... Assume the substring to be a template parameter. -... If the substring contains more than one left curly bracket, more than one right curly bracket (`}`, `0x7D`), if it does not end with a right curly bracket (`}`, `0x7D`), if there are no characters between the curly brackets (i.e., if the entire substring is `{}`), or if the substring contains a tilde (`~`, `0x7E`), set the validity flag to false, ignore all other segments, and goto step 5. -... Add the substring to the template parameter array. -... If the template parameter array contains duplicate elements, set the validity flag to false and goto step 5. -.. If the substring does not start with a left curly bracket (`{`, `0x7B`): -... Assume the substring to be a literal path segment, i.e., not a template parameter. -... If the substring contains a left or right curly bracket not escaped by a back slash character (`\`, `0x5C`), set the validity flag to false and goto step 5. +.. If the path segment consists of a single left curly bracket (`{`, `0x7B`) character, +... set the validity flag to false, ignore all other segments, and goto step 5. +.. If the first character of the path segment substring is a left curly bracket and the second character is not a left curly bracket: +... Assume the path segment substring to be a template parameter. +... If the path segment substring contains left curly brackets not including the first character, right curly brackets (`}`, `0x7D`) not including the last character, if it does not end with a right curly bracket, or if there are no characters between the first and the last curly brackets (i.e., if the entire path segment substring is `{}`), set the validity flag to false, ignore all other segments, and goto step 5. +... Derive the input value socket id as follows: +.... Strip the first and the last characters, i.e., the leading left and trailing right curly brackets. +.... Replace all instances of `~1` with `/`. +.... Replace all instances of `~0` with `~`. +... If the template parameter array contains an element with the same value socket id, set the validity flag to false and goto step 5. +... Add a new entry to the template parameter array consisting of the derived input value socket id and the path segment substring location in the pointer template string. +.. If the first character of the path segment substring is not a left curly bracket or if the first two characters are left curly brackets: +... Assume the path segment substring to be a literal path segment, i.e., not a template parameter. +... If the path segment substring contains an odd number of consecutive left curly brackets or an odd number of consecutive right curly brackets, set the validity flag to false and goto step 5. 5. If the validity flag is true, output the parameter array; if the validity flag is false, reject the pointer template string with a syntax error. [NOTE] @@ -3270,15 +3278,15 @@ The input to these steps is the `pointer` string configuration value; the output ==== - The template string `"/myProperty"` is syntactically valid and has no template parameters. As it does not represent a recognized glTF Asset Object Model property, using this pointer will result in runtime errors defined by the corresponding nodes. -- The template string `"/nodes/0/scale"` is syntactically valid and has no template parameters. +- The template string `"/nodes/0/scale"` is syntactically valid and has no template parameters thus it does not yield any input value socket ids. -- The template string `"/nodes/{index}/scale"` is syntactically valid and has one template parameter called `{index}`, which would result in an input value socket with id `index`. +- The template string `"/nodes/{index}/scale"` is syntactically valid and has one template parameter that yields an input value socket with id `index`. -- The template string `"/nodes/{index\\}/scale"` is syntactically valid and has one template parameter called `{index\}`, which corresponds to the input value socket with id `index\`. Note that backslash characters are escaped when used in JSON strings. +- The template string `"/nodes/{index}/extras/{{index}}"` is syntactically valid and has one template parameter that yields an input value socket with id `index`. The curly brackets that are part of the `{index}` property name are doubled. -- The template string `"/nodes/{index}/extras/\\{index\\}"` is syntactically valid and has one template parameter called `{index}`. Since the pointer targets the `{index}` JSON property, the curly brackets of the property are escaped. Note that backslash characters are escaped when used in JSON strings. +- The template string `"/nodes/{\~0~0index\~0~0}/rotation"` is syntactically valid and has one template parameter that yields an input value socket with id `\~~index~~`. The tilde characters used in the template parameter are encoded as `~0`. -- The template string `"/nodes/0/extras/e\\}\\{traProperty"` is syntactically valid and has no template parameters. Since the pointer targets the `e}{traProperty` JSON property, the curly brackets of the property are escaped. Note that backslash characters are escaped when used in JSON strings. +- The template string `"/nodes/{my~1index}/scale"` is syntactically valid and has one template parameter that yields an input value socket with id `my/index`. The forward slash character used in the template parameter is encoded as `~1`. ==== [NOTE] @@ -3286,46 +3294,43 @@ The input to these steps is the `pointer` string configuration value; the output ==== - The template string `"/nodes/{index}/extras/~2"` is syntactically invalid because the `~2` character sequence is invalid in JSON Pointers, see <>. -- The template string `"/nodes/{index}/weights/{index}"` is syntactically invalid because the `{index}` template parameter is used twice. - -- The template string `"/nodes/{~index}/scale"` is syntactically invalid because the path segment substring that starts with `{` contains the `~` character. - -- The template string `"/nodes/{}/scale"` is syntactically invalid because the path segment substring that starts with `{` has no characters between `{` and `}`. +- The template string `"/nodes/{index}/weights/{index}"` is syntactically invalid because it yields two input value sockets with the same id. -- The template string `"/nodes/{index/scale"` is syntactically invalid because the path segment substring that starts with `{` does not end with `}`. +- The template string `"/nodes/{/scale"` is syntactically invalid because it contains a `{` path segment. -- The template string `"/nodes/{i}ndex/scale"` is syntactically invalid because the path segment substring that starts with `{` does not end with `}`. +- The template string `"/nodes/{}/scale"` is syntactically invalid because the template parameter path segment has no characters between `{` and `}`. -- The template string `"/nodes/{{index}/scale"` is syntactically invalid because the path segment substring that starts with `{` has more than one `{` character. +- The template string `"/nodes/{index/scale"` is syntactically invalid because the template parameter path segment does not end with `}`. -- The template string `"/nodes/{\\{index}/scale"` is syntactically invalid because the path segment substring that starts with `{` has more than one `{` character. +- The template string `"/nodes/{i{ndex}/scale"` is syntactically invalid because the template parameter path segment contains a left curly bracket inside the parameter. -- The template string `"/nodes/\\{index}/scale"` is syntactically invalid because the path segment substring that does not start with `{` has the unescaped `}` character. +- The template string `"/nodes/{i}ndex}/scale"` is syntactically invalid because the template parameter path segment contains a right curly bracket inside the parameter. -- The template string `"/nodes/{index}}/scale"` is syntactically invalid because the path segment substring that starts with `{` has more than one `}` character. +- The template string `"/nodes/0/extras/{{i{ndex}}"` is syntactically invalid because a literal path segment has an odd number of consecutive left curly brackets. -- The template string `"/nodes/0/extras/myData{Index}"` is syntactically invalid because it has a path segment substring that does not start with `{` and contains an unescaped `{` character. +- The template string `"/nodes/0/extras/{{index}"` is syntactically invalid because a literal path segment has an odd number of consecutive right curly brackets. ==== ===== Effective JSON Pointer Generation The inputs to these steps are the `pointer` configuration value, the template parameter array, and the corresponding input values provided at runtime by the behavior graph; the output is the effective JSON Pointer string that will be handled by the Object Model Access nodes. Implementations **MAY** optimize these steps as long as such optimizations do not change the output. -1. Let _P_ be a copy of the `pointer` configuration value. -2. For each element of the template parameter array: -.. assert that the corresponding input socket value is not negative; -.. convert the corresponding input socket value to its decimal string representation containing only ASCII characters `0x30 ... 0x39` with no extra leading zeros. -.. update _P_ by replacing the template parameter substring in it with the converted input socket value. -3. Update _P_ by replacing all occurrences of the `\{` substring in it with `{`. -4. Update _P_ by replacing all occurrences of the `\}` substring in it with `}`. -5. Output _P_ as the effective JSON Pointer string. +1. Assume that the `pointer` configuration value is syntactically valid. +2. Let _P_ be a copy of the `pointer` configuration value. +3. For each element of the template parameter array: +.. Assert that the corresponding input socket value is not negative. +.. Convert the corresponding input socket value to its decimal string representation containing only ASCII characters `0x30 ... 0x39` with no extra leading zeros. +.. Update _P_ by replacing the template parameter substring, as identified by the template parameter location, with the decimal string value of the corresponding input value socket. +4. Update _P_ by replacing all occurrences of the `{{` substring in it with `{`. +5. Update _P_ by replacing all occurrences of the `}}` substring in it with `}`. +6. Output _P_ as the effective JSON Pointer string. [NOTE] .Examples ==== - Let the `pointer` configuration value be `"/nodes/{N}/weights/{W}"`. Then the nodes using this template pointer string have the `N` and `W` input value sockets. Let the runtime `N` value be 1 and the runtime `W` value be 2. Then the effective JSON Pointer is `"/nodes/1/weights/2"`. -- Let the `pointer` configuration value be `"/nodes/{index}/extras/\\{index\\}"`. Then the nodes using this template pointer string have the `index` input value socket. Let the runtime `index` value be 2. Then the effective JSON Pointer is `"/nodes/2/extras/{index}"`. +- Let the `pointer` configuration value be `"/nodes/{index}/extras/{{index}}"`. Then the nodes using this template pointer string have the `index` input value socket. Let the runtime `index` value be 2. Then the effective JSON Pointer is `"/nodes/2/extras/{index}"`. ==== ===== Pointer Get From 6f6b10d8149778d62aa166dee8493291a99c6fdf Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:00:00 +0000 Subject: [PATCH 66/80] Clarify effective JSON Pointer generation --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index d15fc60b9e..bd06d45589 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3317,7 +3317,7 @@ The inputs to these steps are the `pointer` configuration value, the template pa 1. Assume that the `pointer` configuration value is syntactically valid. 2. Let _P_ be a copy of the `pointer` configuration value. -3. For each element of the template parameter array: +3. For each element of the template parameter array taken in the descending order of parameter substring locations: .. Assert that the corresponding input socket value is not negative. .. Convert the corresponding input socket value to its decimal string representation containing only ASCII characters `0x30 ... 0x39` with no extra leading zeros. .. Update _P_ by replacing the template parameter substring, as identified by the template parameter location, with the decimal string value of the corresponding input value socket. From 048bc309bf5fb6340cd07157d9610350e17fb256 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:00:00 +0000 Subject: [PATCH 67/80] Add debug/log --- .../KHR_interactivity/Specification.adoc | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index bd06d45589..0b87ae33be 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -3883,6 +3883,100 @@ When the `in` input flow is activated: 2. Send the custom event. 3. Activate the `out` output flow. +=== Debug Nodes + +==== Debug Output Nodes + +===== Log + +[cols="1h,1,2"] +|=== +| Operation | `debug/log` | Output a debug message +.2+| Configuration +| `int severity` | Message severity +| `string message` | Message template +| Input flow sockets +| `in` | The entry flow into this node +| Input value sockets +| ` ` | Zero or more message template parameters to be evaluated at runtime; input value socket ids correspond to the message substrings wrapped with curly brackets (`{}`) +| Output flow sockets +| `out` | The flow to be activated after the message was logged +|=== + +[CAUTION] +==== +The configuration of this node affects its value sockets. +==== + +This node builds a debug message from the specified template string and input socket values as described below and outputs the message to the user in an implementation-defined manner. + +The `severity` configuration value is intended for filtering messages based on the implementation configuration. The `message` configuration value contains the debug message template string that defines the user-facing text message and input value socket ids (if any). + +The value socket ids are substrings of the `message` value wrapped with curly brackets (`{}`); they are extracted as described below. Literal curly brackets present in the message template string **MUST** be doubled. + +The type `any` represents any value socket type including custom types. This nodes implies that all value types have implementation-defined string representations. + +In the default configuration, the `severity` configuration value is zero, the `message` configuration string is empty, and the node has no input value sockets. + +If the `severity` configuration property is not provided by the behavior graph, if it is not a literal number, or if its value is not exactly representable as a 32-bit signed integer, the default configuration **MUST** be used. + +[TIP] +.Implementation Tip +==== +The integer representation check is implementable in ECMAScript via the following expression: +[source,js] +---- +s === (s | 0) +---- +==== + +If the `message` configuration property is not provided by the behavior graph, if it is not a literal string, or if its value is not syntactically valid as described below, the default configuration **MUST** be used. + +The following procedure outputs input value socket id locations within the `message` configuration string and returns whether the string is syntactically valid: + +1. Let _state_ be a temporary integer variable initialized to zero. During the procedure execution, its value will always be one of 0, 1, 2, or 3. +2. Let _paramStart_ be a temporary integer variable initialized to unspecified value. +3. Let `M[i]` be the character (Unicode code point) of the `message` configuration string at the position `i` (zero-based). +4. For `i` from zero (inclusive) to the `message` length (exclusive), do: +.. If `M[i]` is `"{"` (without quotes): +... If _state_ is 0, set _state_ to 1; +... else if _state_ is 1, set _state_ to 0; +... else (if _state_ is 2 or 3) return false. +.. Else if `M[i]` is `"}"` (without quotes): +... If _state_ is 0, set _state_ to 3; +... else if _state_ is 3, set _state_ to 0; +... else if _state_ is 2, +.... output the (_paramStart_, `i`) tuple as the inclusive range defining the template parameter location in the `message` string; +.... set _state_ to 0; +... else (if _state_ is 1) return false. +.. Else if `M[i]` is neither `"{"` nor `"}"` (without quotes): +... If _state_ is 1, +.... set _paramStart_ to `i - 1`; +.... set _state_ to 2; +... else if _state_ is 3, return false. +5. If _state_ is not 0, return false. +6. Return true. + +If the procedure above returns false, the `message` configuration string is invalid and the default configuration is used. If the procedure above returns true, node's input value socket ids match the substrings identified by the template parameter locations with curly brackets removed. + +Extra input value sockets with ids not present in the output of the procedure above do not affect the node's operation and validation but they still **MUST** have valid types and value sources. + +This node has no internal state. + +When the `in` input flow is activated: + +1. Evaluate all input values. +2. Generate the effective message string as follows. +.. Let _M_ be a copy of the `message` configuration value. +.. For each element of the template parameter array taken in the descending order of parameter substring locations: +... Convert the corresponding input socket value to its string representation. +... If the string representation of the input socket value contains curly brackets, they **MUST** be doubled. +... Update _M_ by replacing the template parameter substring, as identified by the template parameter location, with the string representation of the corresponding input value socket. Note that the same template parameter **MAY** appear at multiple locations. +.. Update _M_ by replacing all occurrences of the `"{{"` substring in it with `"{"` (without quotes). +.. Update _M_ by replacing all occurrences of the `"}}"` substring in it with `"}"` (without quotes). +3. Output _M_ as the effective message string. +4. Activate the `out` output flow. + == Extending glTF Object Model This Specification defines additional glTF Object Model pointers for use with `pointer/*` nodes. From 5713a5c8b81bfbfa8382ca498f018833197ddc8a Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:00:00 +0000 Subject: [PATCH 68/80] Update math rotate nodes --- .../KHR_interactivity/Specification.adoc | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 0b87ae33be..bc1ef89204 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1511,28 +1511,69 @@ Since this operation is a shortcut for the combination of multiplications and su [cols="1h,1,2"] |=== -| Operation | `math/rotate2d` | 2D rotation +| Operation | `math/rotate2D` | 2D rotation .2+| Input value sockets | `float2 a` | Vector to rotate | `float b` | Angle in radians | Output value sockets -| `float2 value` | Vector stem:[a] rotated counter-clockwise by stem:[b] +| `float2 value` | Rotated vector |=== +Let latexmath:[a_x] and latexmath:[a_y] be the components of the `a` input socket value. + +Then the components of the `value` output socket value are computed as follows: + +[latexmath] ++++++ +x = a_x \cdot \cos(b) - a_y \cdot \sin(b) \\ +y = a_x \cdot \sin(b) + a_y \cdot \cos(b) ++++++ + +Since this operation is a shortcut for the combination of trigonometry and arithmetic operations, NaN and infinity values are propagated accordingly. + ===== Rotate 3D [cols="1h,1,2"] |=== -| Operation | `math/rotate3d` | 3D rotation -.3+| Input value sockets +| Operation | `math/rotate3D` | 3D rotation +.2+| Input value sockets | `float3 a` | Vector to rotate -| `float3 b` | Vector representing an axis to rotate around -| `float c` | Angle in radians +| `float4 b` | Rotation quaternion | Output value sockets -| `float3 value` | Vector stem:[a] rotated around vector stem:[b] counter-clockwise by stem:[c] +| `float3 value` | Rotated vector |=== -If the vector stem:[b] is not unit, rotation results **MAY** be undefined. +[CAUTION] +==== +This node assumes that the rotation quaternion is unit. +==== + +Let + +- latexmath:[\vec{a}] be a three-component vector formed from the `a` input socket value; +- latexmath:[b_x], latexmath:[b_y], latexmath:[b_z], and latexmath:[b_w] be the components of the `b` input socket value; +- latexmath:[\vec{b}] be a three-component vector formed from latexmath:[b_x], latexmath:[b_y], and latexmath:[b_z]. + +Then the components of the `value` output socket value are computed as the components of the following vector: + +[latexmath] ++++++ +\vec{a} + 2 \cdot (\vec{b} \times (\vec{b} \times \vec{a}) + b_w \cdot (\vec{b} \times \vec{a})) ++++++ + +Since this operation is a shortcut for the combination of arithmetic operations, NaN and infinity values are propagated accordingly. + +[NOTE] +.Implementation Note +==== +This operation is supposed to be more efficient than converting the rotation quaternion to a matrix and transforming the vector with it. +==== + +[TIP] +.Authoring Tip +==== +This operation does not implicitly normalize the rotation quaternion. If needed, that step could be done explicitly by adding a `math/normalize` node. +==== ===== Transform @@ -1655,6 +1696,11 @@ See `math/mul` for per-element multiplication. | `float4x4 value` | Matrix composed from the TRS properties |=== +[CAUTION] +==== +This node assumes that the rotation quaternion is unit. +==== + Let - stem:[t_x], stem:[t_y], and stem:[t_z] be the translation vector components; From b8e8a5a2002b6fa9ab0044db9e68eb68d12b2ba5 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:00:00 +0000 Subject: [PATCH 69/80] Add basic quaternion nodes --- .../KHR_interactivity/Specification.adoc | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index bc1ef89204..572db5934e 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1798,6 +1798,226 @@ B = ((a_{11}/s_x, a_{12}/s_y, a_{13}/s_z), .. set the `rotation` output value to stem:[(0, 0, 0, 1)]; .. set the `scale` output value to stem:[(1, 1, 1)]. +==== Quaternion Nodes + +===== Conjugation + +[cols="1h,1,2"] +|=== +| Operation | `math/quatConjugate` | Quaternion conjugation operation +| Input value sockets +| `float4 a` | Input quaternion +| Output value sockets +| `float4 value` | Conjugated quaternion +|=== + +Let stem:[a_x], stem:[a_y], stem:[a_z], and stem:[a_w] be the components of stem:[a]. + +Then the components of the `value` output socket value are set as follows: + +[latexmath] ++++++ +x = -a_x \\ +y = -a_y \\ +z = -a_z \\ +w = a_w ++++++ + +Since this operation is a shortcut for the combination of negations, NaN and infinity values are propagated accordingly. + +[TIP] +.Authoring Tip +==== +If the quaternion is unit, this operation also produces the quaternion's inverse. +==== + +===== Multiplication + +[cols="1h,1,2"] +|=== +| Operation | `math/quatMul` | Quaternion multiplication operation +.2+| Input value sockets +| `float4 a` | First quaternion +| `float4 b` | Second quaternion +| Output value sockets +| `float4 value` | Quaternion product +|=== + +Let + +- stem:[a_x], stem:[a_y], stem:[a_z], and stem:[a_w] be the components of stem:[a]; +- stem:[b_x], stem:[b_y], stem:[b_z], and stem:[b_w] be the components of stem:[b]. + +Then the components of the `value` output socket value are computed as follows: + +[stem] ++++++ +x = a_w * b_x + a_x * b_w + a_y * b_z - a_z * b_y \ +y = a_w * b_y + a_y * b_w + a_z * b_x - a_x * b_z \ +z = a_w * b_z + a_z * b_w + a_x * b_y - a_y * b_x \ +w = a_w * b_w - a_x * b_x - a_y * b_y - a_z * b_z ++++++ + +Since this operation is a shortcut for the combination of multiplications, subtractions, and additions, NaN and infinity values are propagated accordingly. + +===== Angle Between Quaternions + +[cols="1h,1,2"] +|=== +| Operation | `math/quatAngleBetween` | Angle between two quaternions +.2+| Input value sockets +| `float4 a` | First quaternion +| `float4 b` | Second quaternion +| Output value sockets +| `float value` | Angle in radians +|=== + +[CAUTION] +==== +This node assumes that both input quaternions are unit. +==== + +Let + +- stem:[a_x], stem:[a_y], stem:[a_z], and stem:[a_w] be the components of stem:[a]; +- stem:[b_x], stem:[b_y], stem:[b_z], and stem:[b_w] be the components of stem:[b]. + +Then the `value` output socket value is computed as follows: + +[latexmath] ++++++ +2 \cdot \arccos(a_x \cdot b_x + a_y \cdot b_y + a_z \cdot b_z + a_w \cdot b_w) ++++++ + +Since this operation is a shortcut for the combination of arithmetic and trigonometry operations, NaN and infinity values are propagated accordingly. + +[TIP] +.Authoring Tip +==== +This operation does not implicitly normalize the input quaternions. If needed, that step could be done explicitly by adding `math/normalize` nodes. +==== + +===== Quaternion From Axis & Angle + +[cols="1h,1,2"] +|=== +| Operation | `math/quatFromAxisAngle` | Create a quaternion from a rotation axis and an angle +.2+| Input value sockets +| `float3 axis` | Rotation axis +| `float angle` | Angle in radians +| Output value sockets +| `float4 value` | Rotation quaternion +|=== + +[CAUTION] +==== +This node assumes that the rotation axis vector is unit. +==== + +The components of the `value` output socket value are computed as follows: + +[latexmath] ++++++ +x = \textit{axis}_x \cdot \sin(0.5 \cdot \textit{angle}) \\ +y = \textit{axis}_y \cdot \sin(0.5 \cdot \textit{angle}) \\ +z = \textit{axis}_z \cdot \sin(0.5 \cdot \textit{angle}) \\ +w = \cos(0.5 \cdot \textit{angle}) ++++++ + +Since this operation is a shortcut for the combination of multiplications and trigonometry functions, NaN and infinity values are propagated accordingly. + +[TIP] +.Authoring Tip +==== +This operation does not implicitly normalize the rotation axis vector. If needed, that step could be done explicitly by adding a `math/normalize` node. +==== + +===== Quaternion To Axis & Angle + +[cols="1h,1,2"] +|=== +| Operation | `math/quatToAxisAngle` | Decompose a quaternion to a rotation axis and an angle +| Input value sockets +| `float4 a` | Rotation quaternion +.2+| Output value sockets +| `float3 axis` | Rotation axis +| `float angle` | Angle in radians +|=== + +[CAUTION] +==== +This node assumes that the rotation quaternion is unit. +==== + +Let stem:[a_x], stem:[a_y], stem:[a_z], and stem:[a_w] be the components of stem:[a]. + +Then the `angle` and `axis` output socket values are computed as follows: + +1. If the absolute value of stem:[a_w] is close to one within an implementation-defined threshold, set the `angle` socket value to zero and the `axis` socket value to any axis-aligned unit vector, i.e., to one of latexmath:[(\pm1, 0, 0)], latexmath:[(0, \pm1, 0)], or latexmath:[(0, 0, \pm1)]. +2. Else set `angle` and `axis` socket values using the following expressions: ++ +[latexmath] ++++++ +\textit{axis}_x = \frac{a_x}{\sqrt{1 - a^2_w}} \\ +\textit{axis}_y = \frac{a_y}{\sqrt{1 - a^2_w}} \\ +\textit{axis}_z = \frac{a_z}{\sqrt{1 - a^2_w}} \\ +\textit{angle} = 2 \cdot \arccos(a_w) ++++++ + +Since this operation is a shortcut for the combination of arithmetic and trigonometry functions, NaN and infinity values are propagated accordingly. + +[TIP] +.Authoring Tip +==== +This operation does not implicitly normalize the rotation quaternion. If needed, that step could be done explicitly by adding a `math/normalize` node. +==== + +===== Quaternion From Two Directional Vectors + +[cols="1h,1,2"] +|=== +| Operation | `math/quatFromDirections` | Create a quaternion from two directional vectors +.2+| Input value sockets +| `float3 a` | First direction +| `float3 b` | Second direction +| Output value sockets +| `float4 value` | Rotation quaternion +|=== + +[CAUTION] +==== +This node assumes that both directions are unit. +==== + +The components of the `value` output socket value are computed as follows: + +1. Let latexmath:[c] be the dot product of latexmath:[\vec{a}] and latexmath:[\vec{b}]. +2. If latexmath:[c] is close to positive one within an implementation-defined threshold, +.. set the `value` output value to an identity quaternion, i.e., set the stem:[w] component of the `value` output value to stem:[\pm1] and set the other three components to zeros; +.. skip the next steps. +3. If latexmath:[c] is close to negative one within an implementation-defined threshold, +.. set the stem:[x], stem:[y], and stem:[z] components of the `value` output socket value to the corresponding components of a unit vector representing a direction perpendicular to latexmath:[\vec{a}]; +.. set the stem:[w] component of the `value` output socket value to zero; +.. skip the next steps. +4. Let latexmath:[\vec{r}] be the normalized cross product of latexmath:[\vec{a}] and latexmath:[\vec{b}]. +5. Set the components of the `value` output socket value using the following expressions: ++ +[latexmath] ++++++ +x = r_x \cdot \sqrt{0.5 - 0.5 \cdot c} \\ +y = r_y \cdot \sqrt{0.5 - 0.5 \cdot c} \\ +z = r_z \cdot \sqrt{0.5 - 0.5 \cdot c} \\ +w = \sqrt{0.5 + 0.5 \cdot c} ++++++ + +Since this operation is a shortcut for the combination of arithmetic and exponential functions, NaN and infinity values are propagated accordingly. + +[TIP] +.Authoring Tip +==== +This operation implies that both input vectors are normalized. A `math/normalize` node could be added to handle non-normalized inputs. +==== + ==== Swizzle Nodes ===== Combine From 4f0e47ebd75bfc3957aa8f0b3a69ded5f359d10b Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:00:00 +0000 Subject: [PATCH 70/80] Update math/rotate* input sockets --- .../Khronos/KHR_interactivity/Specification.adoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 572db5934e..93d89c0065 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1514,19 +1514,19 @@ Since this operation is a shortcut for the combination of multiplications and su | Operation | `math/rotate2D` | 2D rotation .2+| Input value sockets | `float2 a` | Vector to rotate -| `float b` | Angle in radians +| `float angle` | Angle in radians | Output value sockets | `float2 value` | Rotated vector |=== -Let latexmath:[a_x] and latexmath:[a_y] be the components of the `a` input socket value. +Let latexmath:[a_x] and latexmath:[a_y] be the components of the `a` input socket value and latexmath:[\alpha] be the `angle` input socket value. Then the components of the `value` output socket value are computed as follows: [latexmath] +++++ -x = a_x \cdot \cos(b) - a_y \cdot \sin(b) \\ -y = a_x \cdot \sin(b) + a_y \cdot \cos(b) +x = a_x \cdot \cos(\alpha) - a_y \cdot \sin(\alpha) \\ +y = a_x \cdot \sin(\alpha) + a_y \cdot \cos(\alpha) +++++ Since this operation is a shortcut for the combination of trigonometry and arithmetic operations, NaN and infinity values are propagated accordingly. @@ -1538,7 +1538,7 @@ Since this operation is a shortcut for the combination of trigonometry and arith | Operation | `math/rotate3D` | 3D rotation .2+| Input value sockets | `float3 a` | Vector to rotate -| `float4 b` | Rotation quaternion +| `float4 rotation` | Rotation quaternion | Output value sockets | `float3 value` | Rotated vector |=== @@ -1551,14 +1551,14 @@ This node assumes that the rotation quaternion is unit. Let - latexmath:[\vec{a}] be a three-component vector formed from the `a` input socket value; -- latexmath:[b_x], latexmath:[b_y], latexmath:[b_z], and latexmath:[b_w] be the components of the `b` input socket value; -- latexmath:[\vec{b}] be a three-component vector formed from latexmath:[b_x], latexmath:[b_y], and latexmath:[b_z]. +- latexmath:[r_x], latexmath:[r_y], latexmath:[r_z], and latexmath:[r_w] be the components of the `rotation` input socket value; +- latexmath:[\vec{r}] be a three-component vector formed from latexmath:[r_x], latexmath:[r_y], and latexmath:[r_z]. Then the components of the `value` output socket value are computed as the components of the following vector: [latexmath] +++++ -\vec{a} + 2 \cdot (\vec{b} \times (\vec{b} \times \vec{a}) + b_w \cdot (\vec{b} \times \vec{a})) +\vec{a} + 2 \cdot (\vec{r} \times (\vec{r} \times \vec{a}) + r_w \cdot (\vec{r} \times \vec{a})) +++++ Since this operation is a shortcut for the combination of arithmetic operations, NaN and infinity values are propagated accordingly. From 72fe934689bd3a84eed66963e56ae49c1c3af9ff Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Mar 2025 00:00:00 +0000 Subject: [PATCH 71/80] Update math nodes spelling --- .../KHR_interactivity/Specification.adoc | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 93d89c0065..a843d45e83 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -445,7 +445,7 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 [cols="1h,1,2"] |=== -| Operation | `math/e` | Euler's number +| Operation | `math/E` | Euler's number | Output value sockets | `float value` | 2.718281828459045 |=== @@ -453,7 +453,7 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 [cols="1h,1,2"] |=== -| Operation | `math/pi` | Ratio of a circle's circumference to its diameter +| Operation | `math/Pi` | Ratio of a circle's circumference to its diameter | Output value sockets | `float value` | 3.141592653589793 |=== @@ -461,7 +461,7 @@ In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3 [cols="1h,1,2"] |=== -| Operation | `math/inf` | Positive infinity +| Operation | `math/Inf` | Positive infinity | Output value sockets | `float value` | _Infinity_ |=== @@ -475,7 +475,7 @@ To get negative infinity, combine this node with `math/neg`. [cols="1h,1,2"] |=== -| Operation | `math/nan` | Not a Number +| Operation | `math/NaN` | Not a Number | Output value sockets | `float value` | _NaN_ |=== @@ -642,7 +642,7 @@ For matrix arguments, this operation performs per-element multiplication. [NOTE] .Authoring Note ==== -See `math/matmul` for matrix multiplication. +See `math/matMul` for matrix multiplication. ==== ===== Division @@ -850,7 +850,7 @@ For the purposes of these nodes, negative zero is equal to positive zero. [cols="1h,1,2"] |=== -| Operation | `math/isnan` | Not a Number check operation +| Operation | `math/isNaN` | Not a Number check operation | Input value sockets | `float a` | Argument | Output value sockets @@ -861,7 +861,7 @@ For the purposes of these nodes, negative zero is equal to positive zero. [cols="1h,1,2"] |=== -| Operation | `math/isinf` | Infinity check operation +| Operation | `math/isInf` | Infinity check operation | Input value sockets | `float a` | Argument | Output value sockets @@ -871,9 +871,9 @@ For the purposes of these nodes, negative zero is equal to positive zero. [TIP] .Authoring Tip ==== -To check whether a value is only a positive infinity, combine `math/eq` and `math/inf` nodes. +To check whether a value is only a positive infinity, combine `math/eq` and `math/Inf` nodes. -To check whether a value is only a negative infinity, combine `math/eq`, `math/neg`, and `math/inf` nodes. +To check whether a value is only a negative infinity, combine `math/eq`, `math/neg`, and `math/Inf` nodes. ==== ===== Select @@ -1663,7 +1663,7 @@ The output values are computed as follows: [cols="1h,1,2"] |=== -| Operation | `math/matmul` | Matrix multiplication operation +| Operation | `math/matMul` | Matrix multiplication operation .2+| Input value sockets | `float{2x2\|3x3\|4x4} a` | First matrix | `float{2x2\|3x3\|4x4} b` | Second matrix @@ -4613,11 +4613,11 @@ The operation is specified by the **REQUIRED** `declaration` property that point [NOTE] .Example ==== -A `math/e` node with its declaration. +A `math/E` node with its declaration. ```json "declarations": [ - { "op": "math/e" } + { "op": "math/E" } ], "nodes": [ { "declaration": 0 } @@ -4711,8 +4711,8 @@ A `math/sub` node with two input value sockets referring to output value sockets { "signature": "float" } ] "declarations": [ - { "op": "math/pi" } - { "op": "math/e" } + { "op": "math/Pi" } + { "op": "math/E" } { "op": "math/sub" } ], "nodes": [ @@ -4736,14 +4736,14 @@ If neither `value` nor `node` properties are defined in the object representing [NOTE] .Example ==== -A `math/isnan` node with a type-default input value socket. The output value of this node is true because the input value socket `a` has a constant value of NaN (type-default for `float`). +A `math/isNaN` node with a type-default input value socket. The output value of this node is true because the input value socket `a` has a constant value of NaN (type-default for `float`). ```json "types": [ { "signature": "float" } ] "declarations": [ - { "op": "math/isnan" } + { "op": "math/isNaN" } ], "nodes": [ { @@ -4797,7 +4797,7 @@ The `out` output flow of the `event/onStart` node is connected to the `in` input "declarations": [ { "op": "event/onStart" }, { "op": "flow/setDelay" }, - { "op": "math/inf" }, + { "op": "math/Inf" }, { "op": "animation/start" } ], "nodes": [ From 23bcc6f82645f34ca3c370c74ac6eb0d861e055e Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Mar 2025 00:00:00 +0000 Subject: [PATCH 72/80] Fix typos --- .../KHR_interactivity/Specification.adoc | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index a843d45e83..2c40f7beff 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -259,7 +259,7 @@ For example, a `flow/sequence` node can have an output flow socket with id `"in" [[socket-order]] ==== Socket Order -Although sockets are inherently unordered withing a node (because JSON properties are unordered), some operations such as `flow/sequence` or `flow/multiGate` need a specific socket order to guarantee predictable behavior. In such cases, the sockets are implicitly sorted by their ids in ascending order. +Although sockets are inherently unordered within a node (because JSON properties are unordered), some operations such as `flow/sequence` or `flow/multiGate` need a specific socket order to guarantee predictable behavior. In such cases, the sockets are implicitly sorted by their ids in ascending order. For any given ids `a` and `b`, the following procedure **MUST** be used to determine if `a` is less than `b`. @@ -4391,7 +4391,7 @@ This example defines two variables of `float2` type; the first is explicitly ini ```json "types": [ - { "signature": "float2" }, + { "signature": "float2" } ], "variables": [ { @@ -4449,7 +4449,7 @@ This example defines two custom events. The first event is internal to the graph ```json "types": [ - { "signature": "int" }, + { "signature": "int" } ], "events": [ { }, @@ -4659,7 +4659,7 @@ A `math/add` node with two integer inline values: 1 and 2. ```json "types": [ { "signature": "int" } -] +], "declarations": [ { "op": "math/add" } ], @@ -4709,10 +4709,10 @@ A `math/sub` node with two input value sockets referring to output value sockets ```json "types": [ { "signature": "float" } -] +], "declarations": [ - { "op": "math/Pi" } - { "op": "math/E" } + { "op": "math/Pi" }, + { "op": "math/E" }, { "op": "math/sub" } ], "nodes": [ @@ -4741,7 +4741,7 @@ A `math/isNaN` node with a type-default input value socket. The output value of ```json "types": [ { "signature": "float" } -] +], "declarations": [ { "op": "math/isNaN" } ], @@ -4793,7 +4793,7 @@ The `out` output flow of the `event/onStart` node is connected to the `in` input "types": [ { "signature": "float" }, { "signature": "int" } -] +], "declarations": [ { "op": "event/onStart" }, { "op": "flow/setDelay" }, @@ -4810,11 +4810,11 @@ The `out` output flow of the `event/onStart` node is connected to the `in` input { "declaration": 1, "values": { - "duration": { "type": 0, "value": [5] } + "duration": { "type": 0, "value": [ 5 ] } }, "flows": { "out": { "node": 3 } - }, + } }, { "declaration": 2 @@ -4823,9 +4823,9 @@ The `out` output flow of the `event/onStart` node is connected to the `in` input "declaration": 3, "values": { "animation": { "type": 1 }, - "startTime": { "type": 0, "value": [0] }, + "startTime": { "type": 0, "value": [ 0 ] }, "endTime": { "node": 2 }, - "speed": { "type": 0, "value": [1] }, + "speed": { "type": 0, "value": [ 1 ] } } } ] @@ -4867,13 +4867,13 @@ The `variable/set` node sets a custom variable with index `0` when the start eve ```json "types": [ { "signature": "float" } -] +], "variables": [ { "type": 0 } ], "declarations": [ { "op": "event/onStart" }, - { "op": "variable/set" }, + { "op": "variable/set" } ], "nodes": [ { @@ -4885,10 +4885,10 @@ The `variable/set` node sets a custom variable with index `0` when the start eve { "declaration": 1, "configuration": { - "variable": { "value": [0] } + "variable": { "value": [ 0 ] } }, "values": { - "value": { "type": 0, "value": [1.5] } + "value": { "type": 0, "value": [ 1.5 ] } } } ] From f6af2bfa07f546b25a7ed209dfd4d7ba1d35bb11 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 6 Oct 2025 00:00:00 +0000 Subject: [PATCH 73/80] Fix table formatting --- .../KHR_interactivity/Specification.adoc | 548 +++++++++--------- 1 file changed, 274 insertions(+), 274 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 2c40f7beff..47cc9ddca0 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -607,8 +607,8 @@ a < 0 ? -Math.round(-a) : Math.round(a) |=== | Operation | `math/add` | Addition operation .2+| Input value sockets -| `floatN a` | First addend -| `floatN b` | Second addend +d| `floatN a` | First addend +d| `floatN b` | Second addend | Output value sockets | `floatN value` | Sum, stem:[a + b] |=== @@ -619,8 +619,8 @@ a < 0 ? -Math.round(-a) : Math.round(a) |=== | Operation | `math/sub` | Subtraction operation .2+| Input value sockets -| `floatN a` | Minuend -| `floatN b` | Subtrahend +d| `floatN a` | Minuend +d| `floatN b` | Subtrahend | Output value sockets | `floatN value` | Difference, stem:[a - b] |=== @@ -631,8 +631,8 @@ a < 0 ? -Math.round(-a) : Math.round(a) |=== | Operation | `math/mul` | Multiplication operation .2+| Input value sockets -| `floatN a` | First factor -| `floatN b` | Second factor +d| `floatN a` | First factor +d| `floatN b` | Second factor | Output value sockets | `floatN value` | Product, stem:[a * b] |=== @@ -651,8 +651,8 @@ See `math/matMul` for matrix multiplication. |=== | Operation | `math/div` | Division operation .2+| Input value sockets -| `floatN a` | Dividend -| `floatN b` | Divisor +d| `floatN a` | Dividend +d| `floatN b` | Divisor | Output value sockets | `floatN value` | Quotient, stem:[a / b] |=== @@ -663,8 +663,8 @@ See `math/matMul` for matrix multiplication. |=== | Operation | `math/rem` | Remainder operation .2+| Input value sockets -| `floatN a` | Dividend -| `floatN b` | Divisor +d| `floatN a` | Dividend +d| `floatN b` | Divisor | Output value sockets | `floatN value` | latexmath:[\begin{cases} \mathit{NaN} & \text{if } a = \pm \infty \text{ or } b = \pm 0 \\ @@ -689,8 +689,8 @@ a % b |=== | Operation | `math/min` | Minimum operation .2+| Input value sockets -| `floatN a` | First argument -| `floatN b` | Second argument +d| `floatN a` | First argument +d| `floatN b` | Second argument | Output value sockets | `floatN value` | Smallest of the arguments |=== @@ -713,8 +713,8 @@ Math.min(a, b) |=== | Operation | `math/max` | Maximum operation .2+| Input value sockets -| `floatN a` | First argument -| `floatN b` | Second argument +d| `floatN a` | First argument +d| `floatN b` | Second argument | Output value sockets | `floatN value` | Largest of the arguments |=== @@ -737,9 +737,9 @@ Math.max(a, b) |=== | Operation | `math/clamp` | Clamp operation .3+| Input value sockets -| `floatN a` | Value to clamp -| `floatN b` | First boundary -| `floatN c` | Second boundary +d| `floatN a` | Value to clamp +d| `floatN b` | First boundary +d| `floatN c` | Second boundary | Output value sockets | `floatN value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] |=== @@ -771,9 +771,9 @@ This node is defined in terms of `math/min` and `math/max` nodes defined above. |=== | Operation | `math/mix` | Linear interpolation operation .3+| Input value sockets -| `floatN a` | Interpolated value at stem:[0] -| `floatN b` | Interpolated value at stem:[1] -| `floatN c` | Unclamped interpolation coefficient +d| `floatN a` | Interpolated value at stem:[0] +d| `floatN b` | Interpolated value at stem:[1] +d| `floatN c` | Unclamped interpolation coefficient | Output value sockets | `floatN value` | stem:[(1 - c) * a + c * b] |=== @@ -790,8 +790,8 @@ For the purposes of these nodes, negative zero is equal to positive zero. |=== | Operation | `math/eq` | Equality operation .2+| Input value sockets -| `floatN a` | First argument -| `floatN b` | Second argument +d| `floatN a` | First argument +d| `floatN b` | Second argument | Output value sockets | `bool value` | True if the input arguments are equal, per-component; false otherwise |=== @@ -802,8 +802,8 @@ For the purposes of these nodes, negative zero is equal to positive zero. |=== | Operation | `math/lt` | Less than operation .2+| Input value sockets -| `float a` | First argument -| `float b` | Second argument +d| `float a` | First argument +d| `float b` | Second argument | Output value sockets | `bool value` | True if stem:[a < b]; false otherwise |=== @@ -814,8 +814,8 @@ For the purposes of these nodes, negative zero is equal to positive zero. |=== | Operation | `math/le` | Less than or equal to operation .2+| Input value sockets -| `float a` | First argument -| `float b` | Second argument +d| `float a` | First argument +d| `float b` | Second argument | Output value sockets | `bool value` | True if stem:[a <= b]; false otherwise |=== @@ -826,8 +826,8 @@ For the purposes of these nodes, negative zero is equal to positive zero. |=== | Operation | `math/gt` | Greater than operation .2+| Input value sockets -| `float a` | First argument -| `float b` | Second argument +d| `float a` | First argument +d| `float b` | Second argument | Output value sockets | `bool value` | True if stem:[a > b]; false otherwise |=== @@ -838,8 +838,8 @@ For the purposes of these nodes, negative zero is equal to positive zero. |=== | Operation | `math/ge` | Greater than or equal operation .2+| Input value sockets -| `float a` | First argument -| `float b` | Second argument +d| `float a` | First argument +d| `float b` | Second argument | Output value sockets | `bool value` | True if stem:[a >= b]; false otherwise |=== @@ -882,9 +882,9 @@ To check whether a value is only a negative infinity, combine `math/eq`, `math/n |=== | Operation | `math/select` | Conditional selection operation .3+| Input value sockets -| `bool condition` | Value selecting the value returned -| `T a` | Positive selection option -| `T b` | Negative selection option +d| `bool condition` | Value selecting the value returned +d| `T a` | Positive selection option +d| `T b` | Negative selection option | Output value sockets | `T value` | stem:[a] if the `condition` input value is true; stem:[b] otherwise |=== @@ -899,9 +899,9 @@ The type `T` represents any supported type including custom types. It **MUST** b | Configuration | `int[] cases` | The cases on which to perform the switch; empty in the default configuration .3+| Input value sockets -| `int selection` | The value on which the switch operates -| `T ` | Zero or more input value sockets; `` is an integer decimal number -| `T default` | The value used when the `selection` input value is not present in the `cases` configuration array +d| `int selection` | The value on which the switch operates +d| `T ` | Zero or more input value sockets; `` is an integer decimal number +d| `T default` | The value used when the `selection` input value is not present in the `cases` configuration array | Output value sockets | `T value` | The value taken from the selected input value socket |=== @@ -1153,8 +1153,8 @@ When stem:[a] is infinite, the returned value is an implementation-specific appr |=== | Operation | `math/atan2` | Arctangent 2 function .2+| Input value sockets -| `floatN a` | Y coordinate -| `floatN b` | X coordinate +d| `floatN a` | Y coordinate +d| `floatN b` | X coordinate | Output value sockets | `floatN value` | Angle between the positive X-axis and the vector from the stem:[(0, 0)] origin to the stem:[(X, Y)] point on a 2D plane; see the description for details |=== @@ -1379,8 +1379,8 @@ If any input value component is _NaN_, the corresponding output value component |=== | Operation | `math/pow` | Power function .2+| Input value sockets -| `floatN a` | Base -| `floatN b` | Exponent +d| `floatN a` | Base +d| `floatN b` | Exponent | Output value sockets | `floatN value` | stem:[a^b]; see the description for details |=== @@ -1453,8 +1453,8 @@ To get the squared length of stem:[a], use `math/dot` with stem:[a] provided to | Input value sockets | `float{2\|3\|4} a` | Vector .2+| Output value sockets -| `float{2\|3\|4} value` | Vector in the same direction as stem:[a] but with a unit length, e.g., latexmath:[\dfrac{\vec{a}}{\sqrt{a_x^2 + a_y^2}}] for `float2`; see the description for details -| `bool isValid` | True if the output vector value has a unit length after normalization; false otherwise +d| `float{2\|3\|4} value` | Vector in the same direction as stem:[a] but with a unit length, e.g., latexmath:[\dfrac{\vec{a}}{\sqrt{a_x^2 + a_y^2}}] for `float2`; see the description for details +d| `bool isValid` | True if the output vector value has a unit length after normalization; false otherwise |=== The output values are computed as follows: @@ -1477,8 +1477,8 @@ If the input vector represents a quaternion and the graph expects it to be ident |=== | Operation | `math/dot` | Dot product .2+| Input value sockets -| `float{2\|3\|4} a` | First vector -| `float{2\|3\|4} b` | Second vector of the same type as stem:[a] +d| `float{2\|3\|4} a` | First vector +d| `float{2\|3\|4} b` | Second vector of the same type as stem:[a] | Output value sockets | `float value` | Sum of per-component products of stem:[a] and stem:[b], e.g., stem:[a_x * b_x + a_y * b_y] for `float2` |=== @@ -1499,8 +1499,8 @@ This operation is frequently used with both input value sockets connected to the |=== | Operation | `math/cross` | Cross product .2+| Input value sockets -| `float3 a` | Vector -| `float3 b` | Vector +d| `float3 a` | Vector +d| `float3 b` | Vector | Output value sockets | `float3 value` | Cross product of stem:[a] and stem:[b], i.e., stem:[(a_y * b_z - a_z * b_y, a_z * b_x - a_x * b_z, a_x * b_y - a_y * b_x)] |=== @@ -1513,8 +1513,8 @@ Since this operation is a shortcut for the combination of multiplications and su |=== | Operation | `math/rotate2D` | 2D rotation .2+| Input value sockets -| `float2 a` | Vector to rotate -| `float angle` | Angle in radians +d| `float2 a` | Vector to rotate +d| `float angle` | Angle in radians | Output value sockets | `float2 value` | Rotated vector |=== @@ -1537,8 +1537,8 @@ Since this operation is a shortcut for the combination of trigonometry and arith |=== | Operation | `math/rotate3D` | 3D rotation .2+| Input value sockets -| `float3 a` | Vector to rotate -| `float4 rotation` | Rotation quaternion +d| `float3 a` | Vector to rotate +d| `float4 rotation` | Rotation quaternion | Output value sockets | `float3 value` | Rotated vector |=== @@ -1581,8 +1581,8 @@ This operation does not implicitly normalize the rotation quaternion. If needed, |=== | Operation | `math/transform` | Vector transformation .2+| Input value sockets -| `float2 a` | Vector to transform -| `float2x2 b` | Transformation matrix +d| `float2 a` | Vector to transform +d| `float2x2 b` | Transformation matrix | Output value sockets | `float2 value` | Transformed vector |=== @@ -1591,8 +1591,8 @@ This operation does not implicitly normalize the rotation quaternion. If needed, |=== | Operation | `math/transform` | Vector transformation .2+| Input value sockets -| `float3 a` | Vector to transform -| `float3x3 b` | Transformation matrix +d| `float3 a` | Vector to transform +d| `float3x3 b` | Transformation matrix | Output value sockets | `float3 value` | Transformed vector |=== @@ -1601,8 +1601,8 @@ This operation does not implicitly normalize the rotation quaternion. If needed, |=== | Operation | `math/transform` | Vector transformation .2+| Input value sockets -| `float4 a` | Vector to transform -| `float4x4 b` | Transformation matrix +d| `float4 a` | Vector to transform +d| `float4x4 b` | Transformation matrix | Output value sockets | `float4 value` | Transformed vector |=== @@ -1645,8 +1645,8 @@ Since this operation is a shortcut for the combination of multiplications, subtr | Input value sockets | `float{2x2\|3x3\|4x4} a` | Matrix to inverse .2+| Output value sockets -| `float{2x2\|3x3\|4x4} value` | Matrix that is the inverse of stem:[a]; see the description for details -| `bool isValid` | True if the input matrix is invertible; false otherwise +d| `float{2x2\|3x3\|4x4} value` | Matrix that is the inverse of stem:[a]; see the description for details +d| `bool isValid` | True if the input matrix is invertible; false otherwise |=== The `value` input value socket and `value` output value socket have the same type. @@ -1665,8 +1665,8 @@ The output values are computed as follows: |=== | Operation | `math/matMul` | Matrix multiplication operation .2+| Input value sockets -| `float{2x2\|3x3\|4x4} a` | First matrix -| `float{2x2\|3x3\|4x4} b` | Second matrix +d| `float{2x2\|3x3\|4x4} a` | First matrix +d| `float{2x2\|3x3\|4x4} b` | Second matrix | Output value sockets | `float{2x2\|3x3\|4x4} value` | Matrix product |=== @@ -1689,9 +1689,9 @@ See `math/mul` for per-element multiplication. |=== | Operation | `math/matCompose` | Compose a 4x4 transform matrix .3+| Input value sockets -| `float3 translation` | Translation vector -| `float4 rotation` | Rotation quaternion -| `float3 scale` | Scale vector +d| `float3 translation` | Translation vector +d| `float4 rotation` | Rotation quaternion +d| `float3 scale` | Scale vector | Output value sockets | `float4x4 value` | Matrix composed from the TRS properties |=== @@ -1746,10 +1746,10 @@ This operation does not implicitly normalize the rotation quaternion. If needed, | Input value sockets | `float4x4 a` | Matrix stem:[A] to decompose .4+| Output value sockets -| `float3 translation` | Translation vector -| `float4 rotation` | Rotation quaternion -| `float3 scale` | Scale vector -| `bool isValid` | True if the input matrix is decomposable; false otherwise +d| `float3 translation` | Translation vector +d| `float4 rotation` | Rotation quaternion +d| `float3 scale` | Scale vector +d| `bool isValid` | True if the input matrix is decomposable; false otherwise |=== The output values are computed as follows: @@ -1837,8 +1837,8 @@ If the quaternion is unit, this operation also produces the quaternion's inverse |=== | Operation | `math/quatMul` | Quaternion multiplication operation .2+| Input value sockets -| `float4 a` | First quaternion -| `float4 b` | Second quaternion +d| `float4 a` | First quaternion +d| `float4 b` | Second quaternion | Output value sockets | `float4 value` | Quaternion product |=== @@ -1866,8 +1866,8 @@ Since this operation is a shortcut for the combination of multiplications, subtr |=== | Operation | `math/quatAngleBetween` | Angle between two quaternions .2+| Input value sockets -| `float4 a` | First quaternion -| `float4 b` | Second quaternion +d| `float4 a` | First quaternion +d| `float4 b` | Second quaternion | Output value sockets | `float value` | Angle in radians |=== @@ -1903,8 +1903,8 @@ This operation does not implicitly normalize the input quaternions. If needed, t |=== | Operation | `math/quatFromAxisAngle` | Create a quaternion from a rotation axis and an angle .2+| Input value sockets -| `float3 axis` | Rotation axis -| `float angle` | Angle in radians +d| `float3 axis` | Rotation axis +d| `float angle` | Angle in radians | Output value sockets | `float4 value` | Rotation quaternion |=== @@ -1940,8 +1940,8 @@ This operation does not implicitly normalize the rotation axis vector. If needed | Input value sockets | `float4 a` | Rotation quaternion .2+| Output value sockets -| `float3 axis` | Rotation axis -| `float angle` | Angle in radians +d| `float3 axis` | Rotation axis +d| `float angle` | Angle in radians |=== [CAUTION] @@ -1978,8 +1978,8 @@ This operation does not implicitly normalize the rotation quaternion. If needed, |=== | Operation | `math/quatFromDirections` | Create a quaternion from two directional vectors .2+| Input value sockets -| `float3 a` | First direction -| `float3 b` | Second direction +d| `float3 a` | First direction +d| `float3 b` | Second direction | Output value sockets | `float4 value` | Rotation quaternion |=== @@ -2026,8 +2026,8 @@ This operation implies that both input vectors are normalized. A `math/normalize |=== | Operation | `math/combine2` | Combine two floats into a two-component vector .2+| Input value sockets -| `float a` | First component -| `float b` | Second component +d| `float a` | First component +d| `float b` | Second component | Output value sockets | `float2 value` | Vector |=== @@ -2036,9 +2036,9 @@ This operation implies that both input vectors are normalized. A `math/normalize |=== | Operation | `math/combine3` | Combine three floats into a three-component vector .3+| Input value sockets -| `float a` | First component -| `float b` | Second component -| `float c` | Third component +d| `float a` | First component +d| `float b` | Second component +d| `float c` | Third component | Output value sockets | `float3 value` | Vector |=== @@ -2047,10 +2047,10 @@ This operation implies that both input vectors are normalized. A `math/normalize |=== | Operation | `math/combine4` | Combine four floats into a four-component vector .4+| Input value sockets -| `float a` | First component -| `float b` | Second component -| `float c` | Third component -| `float d` | Fourth component +d| `float a` | First component +d| `float b` | Second component +d| `float c` | Third component +d| `float d` | Fourth component | Output value sockets | `float4 value` | Vector |=== @@ -2059,10 +2059,10 @@ This operation implies that both input vectors are normalized. A `math/normalize |=== | Operation | `math/combine2x2` | Combine 4 floats into a 2x2 matrix .4+| Input value sockets -| `float a` | First row, first column element -| `float b` | Second row, first column element -| `float c` | First row, second column element -| `float d` | Second row, second column element +d| `float a` | First row, first column element +d| `float b` | Second row, first column element +d| `float c` | First row, second column element +d| `float d` | Second row, second column element | Output value sockets | `float2x2 value` | Matrix |=== @@ -2071,15 +2071,15 @@ This operation implies that both input vectors are normalized. A `math/normalize |=== | Operation | `math/combine3x3` | Combine 9 floats into a 3x3 matrix .9+| Input value sockets -| `float a` | First row, first column element -| `float b` | Second row, first column element -| `float c` | Third row, first column element -| `float d` | First row, second column element -| `float e` | Second row, second column element -| `float f` | Third row, second column element -| `float g` | First row, third column element -| `float h` | Second row, third column element -| `float i` | Third row, third column element +d| `float a` | First row, first column element +d| `float b` | Second row, first column element +d| `float c` | Third row, first column element +d| `float d` | First row, second column element +d| `float e` | Second row, second column element +d| `float f` | Third row, second column element +d| `float g` | First row, third column element +d| `float h` | Second row, third column element +d| `float i` | Third row, third column element | Output value sockets | `float3x3 value` | Matrix |=== @@ -2088,22 +2088,22 @@ This operation implies that both input vectors are normalized. A `math/normalize |=== | Operation | `math/combine4x4` | Combine 16 floats into a 4x4 matrix .16+| Input value sockets -| `float a` | First row, first column element -| `float b` | Second row, first column element -| `float c` | Third row, first column element -| `float d` | Fourth row, first column element -| `float e` | First row, second column element -| `float f` | Second row, second column element -| `float g` | Third row, second column element -| `float h` | Fourth row, second column element -| `float i` | First row, third column element -| `float j` | Second row, third column element -| `float k` | Third row, third column element -| `float l` | Fourth row, third column element -| `float m` | First row, fourth column element -| `float n` | Second row, fourth column element -| `float o` | Third row, fourth column element -| `float p` | Fourth row, fourth column element +d| `float a` | First row, first column element +d| `float b` | Second row, first column element +d| `float c` | Third row, first column element +d| `float d` | Fourth row, first column element +d| `float e` | First row, second column element +d| `float f` | Second row, second column element +d| `float g` | Third row, second column element +d| `float h` | Fourth row, second column element +d| `float i` | First row, third column element +d| `float j` | Second row, third column element +d| `float k` | Third row, third column element +d| `float l` | Fourth row, third column element +d| `float m` | First row, fourth column element +d| `float n` | Second row, fourth column element +d| `float o` | Third row, fourth column element +d| `float p` | Fourth row, fourth column element | Output value sockets | `float4x4 value` | Matrix |=== @@ -2116,8 +2116,8 @@ This operation implies that both input vectors are normalized. A `math/normalize | Input value sockets | `float2 a` | Vector .2+| Output value sockets -| `float 0` | First component -| `float 1` | Second component +d| `float 0` | First component +d| `float 1` | Second component |=== [cols="1h,1,2"] @@ -2126,9 +2126,9 @@ This operation implies that both input vectors are normalized. A `math/normalize | Input value sockets | `float3 a` | Vector .3+| Output value sockets -| `float 0` | First component -| `float 1` | Second component -| `float 2` | Third component +d| `float 0` | First component +d| `float 1` | Second component +d| `float 2` | Third component |=== [cols="1h,1,2"] @@ -2137,10 +2137,10 @@ This operation implies that both input vectors are normalized. A `math/normalize | Input value sockets | `float4 a` | Vector .4+| Output value sockets -| `float 0` | First component -| `float 1` | Second component -| `float 2` | Third component -| `float 3` | Fourth component +d| `float 0` | First component +d| `float 1` | Second component +d| `float 2` | Third component +d| `float 3` | Fourth component |=== [cols="1h,1,2"] @@ -2149,10 +2149,10 @@ This operation implies that both input vectors are normalized. A `math/normalize | Input value sockets | `float2x2 a` | Matrix .4+| Output value sockets -| `float 0` | First row, first column element -| `float 1` | Second row, first column element -| `float 2` | First row, second column element -| `float 3` | Second row, second column element +d| `float 0` | First row, first column element +d| `float 1` | Second row, first column element +d| `float 2` | First row, second column element +d| `float 3` | Second row, second column element |=== [cols="1h,1,2"] @@ -2161,15 +2161,15 @@ This operation implies that both input vectors are normalized. A `math/normalize | Input value sockets | `float3x3 a` | Matrix .9+| Output value sockets -| `float 0` | First row, first column element -| `float 1` | Second row, first column element -| `float 2` | Third row, first column element -| `float 3` | First row, second column element -| `float 4` | Second row, second column element -| `float 5` | Third row, second column element -| `float 6` | First row, third column element -| `float 7` | Second row, third column element -| `float 8` | Third row, third column element +d| `float 0` | First row, first column element +d| `float 1` | Second row, first column element +d| `float 2` | Third row, first column element +d| `float 3` | First row, second column element +d| `float 4` | Second row, second column element +d| `float 5` | Third row, second column element +d| `float 6` | First row, third column element +d| `float 7` | Second row, third column element +d| `float 8` | Third row, third column element |=== [cols="1h,1,2"] @@ -2178,22 +2178,22 @@ This operation implies that both input vectors are normalized. A `math/normalize | Input value sockets | `float4x4 a` | Matrix .16+| Output value sockets -| `float 0` | First row, first column element -| `float 1` | Second row, first column element -| `float 2` | Third row, first column element -| `float 3` | Fourth row, first column element -| `float 4` | First row, second column element -| `float 5` | Second row, second column element -| `float 6` | Third row, second column element -| `float 7` | Fourth row, second column element -| `float 8` | First row, third column element -| `float 9` | Second row, third column element -| `float 10` | Third row, third column element -| `float 11` | Fourth row, third column element -| `float 12` | First row, fourth column element -| `float 13` | Second row, fourth column element -| `float 14` | Third row, fourth column element -| `float 15` | Fourth row, fourth column element +d| `float 0` | First row, first column element +d| `float 1` | Second row, first column element +d| `float 2` | Third row, first column element +d| `float 3` | Fourth row, first column element +d| `float 4` | First row, second column element +d| `float 5` | Second row, second column element +d| `float 6` | Third row, second column element +d| `float 7` | Fourth row, second column element +d| `float 8` | First row, third column element +d| `float 9` | Second row, third column element +d| `float 10` | Third row, third column element +d| `float 11` | Fourth row, third column element +d| `float 12` | First row, fourth column element +d| `float 13` | Second row, fourth column element +d| `float 14` | Third row, fourth column element +d| `float 15` | Fourth row, fourth column element |=== ==== Integer Arithmetic Nodes @@ -2270,8 +2270,8 @@ This is implementable in ECMAScript via the following expression: |=== | Operation | `math/add` | Addition operation .2+| Input value sockets -| `int a` | First addend -| `int b` | Second addend +d| `int a` | First addend +d| `int b` | Second addend | Output value sockets | `int value` | Sum, stem:[a + b] |=== @@ -2299,8 +2299,8 @@ This is implementable in ECMAScript via the following expression: |=== | Operation | `math/sub` | Subtraction operation .2+| Input value sockets -| `int a` | Minuend -| `int b` | Subtrahend +d| `int a` | Minuend +d| `int b` | Subtrahend | Output value sockets | `int value` | Difference, stem:[a - b] |=== @@ -2328,8 +2328,8 @@ This is implementable in ECMAScript via the following expression: |=== | Operation | `math/mul` | Multiplication operation .2+| Input value sockets -| `int a` | First factor -| `int b` | Second factor +d| `int a` | First factor +d| `int b` | Second factor | Output value sockets | `int value` | Product, stem:[a * b] |=== @@ -2359,8 +2359,8 @@ Math.imul(a, b) |=== | Operation | `math/div` | Division operation .2+| Input value sockets -| `int a` | Dividend -| `int b` | Divisor +d| `int a` | Dividend +d| `int b` | Divisor | Output value sockets | `int value` | latexmath:[\begin{cases} \frac{a}{b} & \text{if } b \ne 0 \\ @@ -2392,8 +2392,8 @@ This is implementable in ECMAScript via the following expression: |=== | Operation | `math/rem` | Remainder operation .2+| Input value sockets -| `int a` | Dividend -| `int b` | Divisor +d| `int a` | Dividend +d| `int b` | Divisor | Output value sockets | `int value` | latexmath:[\begin{cases} a - (b \cdot \operatorname{trunc}(\frac{a}{b})) & \text{if } b \ne 0 \\ @@ -2417,8 +2417,8 @@ This is implementable in ECMAScript via the following expression: |=== | Operation | `math/min` | Minimum operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `int value` | Smallest of the arguments |=== @@ -2429,8 +2429,8 @@ This is implementable in ECMAScript via the following expression: |=== | Operation | `math/max` | Maximum operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `int value` | Largest of the arguments |=== @@ -2441,9 +2441,9 @@ This is implementable in ECMAScript via the following expression: |=== | Operation | `math/clamp` | Clamp operation .3+| Input value sockets -| `int a` | Value to clamp -| `int b` | First boundary -| `int c` | Second boundary +d| `int a` | Value to clamp +d| `int b` | First boundary +d| `int c` | Second boundary | Output value sockets | `int value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] |=== @@ -2466,8 +2466,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/eq` | Equality operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `bool value` | True if the input arguments are equal; false otherwise |=== @@ -2478,8 +2478,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/lt` | Less than operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `bool value` | True if stem:[a < b]; false otherwise |=== @@ -2490,8 +2490,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/le` | Less than or equal to operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `bool value` | True if stem:[a <= b]; false otherwise |=== @@ -2502,8 +2502,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/gt` | Greater than operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `bool value` | True if stem:[a > b]; false otherwise |=== @@ -2514,8 +2514,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/ge` | Greater than or equal operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `bool value` | True if stem:[a >= b]; false otherwise |=== @@ -2541,8 +2541,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/and` | Bitwise AND operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `int value` | `a & b` |=== @@ -2553,8 +2553,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/or` | Bitwise OR operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `int value` | `a \| b` |=== @@ -2565,8 +2565,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/xor` | Bitwise XOR operation .2+| Input value sockets -| `int a` | First argument -| `int b` | Second argument +d| `int a` | First argument +d| `int b` | Second argument | Output value sockets | `int value` | `a ^ b` |=== @@ -2577,8 +2577,8 @@ All inputs to these nodes are two's complement 32-bit signed integers. |=== | Operation | `math/asr` | Right Shift .2+| Input value sockets -| `int a` | Value to be shifted -| `int b` | Number of bits to shift by +d| `int a` | Value to be shifted +d| `int b` | Number of bits to shift by | Output value sockets | `int value` | `a >> b` |=== @@ -2591,8 +2591,8 @@ Only the lowest 5 bits of stem:[b] are considered, i.e., its effective range is |=== | Operation | `math/lsl` | Left Shift .2+| Input value sockets -| `int a` | Value to be shifted -| `int b` | Number of bits to shift by +d| `int a` | Value to be shifted +d| `int b` | Number of bits to shift by | Output value sockets | `int value` | `a << b` |=== @@ -2666,8 +2666,8 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu |=== | Operation | `math/eq` | Equality operation .2+| Input value sockets -| `bool a` | First argument -| `bool b` | Second argument +d| `bool a` | First argument +d| `bool b` | Second argument | Output value sockets | `bool value` | True if and only if both stem:[a] and stem:[b] have the same value; false otherwise |=== @@ -2689,8 +2689,8 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu |=== | Operation | `math/and` | Boolean AND operation .2+| Input value sockets -| `bool a` | First argument -| `bool b` | Second argument +d| `bool a` | First argument +d| `bool b` | Second argument | Output value sockets | `bool value` | True if and only if both stem:[a] and stem:[b] are true; false otherwise |=== @@ -2701,8 +2701,8 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu |=== | Operation | `math/or` | Boolean OR operation .2+| Input value sockets -| `bool a` | First argument -| `bool b` | Second argument +d| `bool a` | First argument +d| `bool b` | Second argument | Output value sockets | `bool value` | False if and only if both stem:[a] and stem:[b] are false; true otherwise |=== @@ -2713,8 +2713,8 @@ If stem:[a] is 0, the operation returns 0; if stem:[a] is -1, the operation retu |=== | Operation | `math/xor` | Boolean XOR operation .2+| Input value sockets -| `bool a` | First argument -| `bool b` | Second argument +d| `bool a` | First argument +d| `bool b` | Second argument | Output value sockets | `bool value` | True if and only if stem:[a] is not equal to stem:[b]; false otherwise |=== @@ -2889,8 +2889,8 @@ If the number of output flow sockets (as present in JSON) exceeds an implementat | Input value sockets | `bool condition` | Value selecting the branch taken .2+| Output flow sockets -| `true` | The flow to be activated if the `condition` input value is true -| `false` | The flow to be activated if the `condition` input value is false +d| `true` | The flow to be activated if the `condition` input value is true +d| `false` | The flow to be activated if the `condition` input value is false |=== This node has no internal state. @@ -2909,8 +2909,8 @@ The `condition` input value is evaluated each time the node is executed. | Input value sockets | `int selection` | The value on which the switch operates .2+| Output flow sockets -| `` | Zero or more output flows; `` is an integer decimal number -| `default` | The output flow activated when the `selection` input value is not present in the `cases` configuration array +d| `` | Zero or more output flows; `` is an integer decimal number +d| `default` | The output flow activated when the `selection` input value is not present in the `cases` configuration array |=== [CAUTION] @@ -2971,8 +2971,8 @@ When the `in` input flow is activated: | Input value sockets | `bool condition` | Loop condition .2+| Output flow sockets -| `loopBody` | The flow to be activated while the `condition` input value is true -| `completed` | The flow to be activated once the `condition` input value is false +d| `loopBody` | The flow to be activated while the `condition` input value is true +d| `completed` | The flow to be activated once the `condition` input value is false |=== This node has no internal state. @@ -2996,11 +2996,11 @@ When the `in` input flow is activated: | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets -| `int startIndex` | The start index of the loop -| `int endIndex` | The end index of the loop +d| `int startIndex` | The start index of the loop +d| `int endIndex` | The end index of the loop .2+| Output flow sockets -| `loopBody` | The flow to be activated if the `index` value is less than the `endIndex` input value -| `completed` | The flow to be activated if the `index` value is greater than or equal to the `endIndex` input value +d| `loopBody` | The flow to be activated if the `index` value is less than the `endIndex` input value +d| `completed` | The flow to be activated if the `index` value is greater than or equal to the `endIndex` input value | Output value sockets | `int index` | The current index value if the node has ever been activated, `initialIndex` otherwise |=== @@ -3029,8 +3029,8 @@ When the `in` input flow is activated: |=== | Operation | `flow/doN` | Activate the output flow no more than N times .2+| Input flow sockets -| `in` | The entry flow into this node -| `reset` | When this flow is activated, the `currentCount` value is reset to 0 +d| `in` | The entry flow into this node +d| `reset` | When this flow is activated, the `currentCount` value is reset to 0 | Input value sockets | `int n` | Maximum number of times the `out` output flow is activated | Output flow sockets @@ -3058,11 +3058,11 @@ When the `in` input flow is activated: |=== | Operation | `flow/multiGate` | Route the execution flow to one of the outputs sequentially or randomly .2+| Configuration -| `bool isRandom` | If set to true, output flows are activated in random order, picking a random not used output flow each time until all are done; false in the default configuration -| `bool isLoop` | If set to true, output flow activations will repeat in a loop continuously after all are done; false in the default configuration +d| `bool isRandom` | If set to true, output flows are activated in random order, picking a random not used output flow each time until all are done; false in the default configuration +d| `bool isLoop` | If set to true, output flow activations will repeat in a loop continuously after all are done; false in the default configuration .2+| Input flow sockets -| `in` | The entry flow into this node -| `reset` | When this flow is activated, the `lastIndex` value is reset to -1 and all outputs are marked as not used +d| `in` | The entry flow into this node +d| `reset` | When this flow is activated, the `lastIndex` value is reset to -1 and all outputs are marked as not used | Output flow sockets | `` | Zero or more output flows; their ids define the order of activation | Output value sockets @@ -3114,11 +3114,11 @@ When the `isRandom` and `isLoop` configuration values are true, the output flow | Configuration | `int inputFlows` | The number of input flows; zero in the default configuration .2+| Input flow sockets -| `` | The `i`-th input flow, `i` is a non-negative integer decimal number less than the `inputFlows` configuration value -| `reset` | When this flow is activated, all input flows are marked as unused +d| `` | The `i`-th input flow, `i` is a non-negative integer decimal number less than the `inputFlows` configuration value +d| `reset` | When this flow is activated, all input flows are marked as unused .2+| Output flow sockets -| `out` | The flow to be activated after every input flow activation except the last missing input -| `completed` | The flow to be activated when the last missing input flow is activated +d| `out` | The flow to be activated after every input flow activation except the last missing input +d| `completed` | The flow to be activated when the last missing input flow is activated | Output value sockets | `int remainingInputs` | The number of not yet activated input flows |=== @@ -3165,13 +3165,13 @@ In the default configuration, this node has only the `reset` input flow, the `re |=== | Operation | `flow/throttle` | Activate the output flow unless it has been activated less than a certain time ago .2+| Input flow sockets -| `in` | The entry flow into this node -| `reset` | When this flow is activated, the output flow throttling state is reset +d| `in` | The entry flow into this node +d| `reset` | When this flow is activated, the output flow throttling state is reset | Input value sockets | `float duration` | The time, in seconds, to wait after an output flow activation before allowing subsequent output flow activations .2+| Output flow sockets -| `out` | The flow to be activated if the output flow is not currently throttled -| `err` | The flow to be activated if the `duration` input value is negative, infinite, or NaN +d| `out` | The flow to be activated if the output flow is not currently throttled +d| `err` | The flow to be activated if the `duration` input value is negative, infinite, or NaN | Output value sockets | `float lastRemainingTime` | The remaining throttling time, in seconds, at the moment of the last valid activation of the input flow or NaN if the input flow has never been activated with a valid `duration` input value |=== @@ -3208,16 +3208,16 @@ When the `in` input flow is activated: |=== | Operation | `flow/setDelay` | Schedule the output flow activation after a certain delay .2+| Input flow sockets -| `in` | The entry flow into this node -| `cancel` | When this flow is activated, all delayed activations scheduled by this node are cancelled +d| `in` | The entry flow into this node +d| `cancel` | When this flow is activated, all delayed activations scheduled by this node are cancelled | Input value sockets | `float duration` | The duration, in seconds, to delay the `done` output flow activation | Output value sockets | `int lastDelayIndex` | The delay index assigned during the last successful node execution .3+| Output flow sockets -| `out` | The flow to be activated if the `duration` value is valid -| `err` | The flow to be activated if the `duration` value is invalid -| `done` | The flow to be activated after the delay +d| `out` | The flow to be activated if the `duration` value is valid +d| `err` | The flow to be activated if the `duration` value is invalid +d| `done` | The flow to be activated after the delay |=== The internal state of this node consists of an integer `lastDelayIndex` value initialized to -1 and a dynamic array of activation indices scheduled by the node. This array is initially empty and its maximum size is implementation-specific. @@ -3400,19 +3400,19 @@ When the `in` input flow is activated: |=== | Operation | `variable/interpolate` | Interpolate a variable value .2+| Configuration -| `int variable` | The custom variable index -| `bool useSlerp` | Whether to use spherical interpolation for quaternions +d| `int variable` | The custom variable index +d| `bool useSlerp` | Whether to use spherical interpolation for quaternions | Input flow sockets | `in` | The entry flow into this node .4+| Input value sockets -| `T value` | The target variable value -| `float duration` | The time, in seconds, in which the variable **SHOULD** reach the target value -| `float2 p1` | Control point P1 -| `float2 p2` | Control point P2 +d| `T value` | The target variable value +d| `float duration` | The time, in seconds, in which the variable **SHOULD** reach the target value +d| `float2 p1` | Control point P1 +d| `float2 p2` | Control point P2 .3+| Output flow sockets -| `out` | The flow to be activated if the input values are valid -| `err` | The flow to be activated if the input values are invalid -| `done` | The flow to be activated when the variable reaches the target value +d| `out` | The flow to be activated if the input values are valid +d| `err` | The flow to be activated if the input values are invalid +d| `done` | The flow to be activated when the variable reaches the target value |=== [CAUTION] @@ -3605,13 +3605,13 @@ The inputs to these steps are the `pointer` configuration value, the template pa |=== | Operation | `pointer/get` | Get an object model property value .2+| Configuration -| `string pointer` | JSON Pointer Template -| `int type` | Property type index +d| `string pointer` | JSON Pointer Template +d| `int type` | Property type index | Input value sockets | `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) .2+| Output value sockets -| `T value` | The resolved property value -| `bool isValid` | True if the property value can be resolved; false otherwise +d| `T value` | The resolved property value +d| `bool isValid` | True if the property value can be resolved; false otherwise |=== [CAUTION] @@ -3678,16 +3678,16 @@ The `/meshes/{}/weights` and `/nodes/{}/weights` pointers defined in the glTF As |=== | Operation | `pointer/set` | Set an object model property value .2+| Configuration -| `string pointer` | JSON Pointer Template -| `int type` | Property type index +d| `string pointer` | JSON Pointer Template +d| `int type` | Property type index | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets -| `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) -| `T value` | The new property value +d| `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) +d| `T value` | The new property value .2+| Output flow sockets -| `out` | The flow to be activated if the property was set -| `err` | The flow to be activated if the property was not set +d| `out` | The flow to be activated if the property was set +d| `err` | The flow to be activated if the property was not set |=== [CAUTION] @@ -3745,20 +3745,20 @@ The `/meshes/{}/weights` and `/nodes/{}/weights` pointers defined in the glTF As |=== | Operation | `pointer/interpolate` | Interpolate an object model property value .2+| Configuration -| `string pointer` | JSON Pointer Template -| `int type` | Property type index +d| `string pointer` | JSON Pointer Template +d| `int type` | Property type index | Input flow sockets | `in` | The entry flow into this node .5+| Input value sockets -| `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) -| `T value` | The target property value -| `float duration` | The time, in seconds, in which the property **SHOULD** reach the target value -| `float2 p1` | Control point P1 -| `float2 p2` | Control point P2 +d| `int ` | Zero or more JSON Pointer template parameters to be evaluated at runtime; input value socket ids correspond to the pointer's path segments wrapped with curly brackets (`{}`) +d| `T value` | The target property value +d| `float duration` | The time, in seconds, in which the property **SHOULD** reach the target value +d| `float2 p1` | Control point P1 +d| `float2 p2` | Control point P2 .3+| Output flow sockets -| `out` | The flow to be activated if the property interpolation has been started -| `err` | The flow to be activated if the property interpolation has not been started -| `done` | The flow to be activated after the property reaches the target value +d| `out` | The flow to be activated if the property interpolation has been started +d| `err` | The flow to be activated if the property interpolation has not been started +d| `done` | The flow to be activated after the property reaches the target value |=== [CAUTION] @@ -3860,14 +3860,14 @@ The `/meshes/{}/weights` and `/nodes/{}/weights` pointers defined in the glTF As | Input flow sockets | `in` | The entry flow into this node .4+| Input value sockets -| `int animation` | Animation index -| `float startTime` | Start time in seconds -| `float endTime` | End time in seconds -| `float speed` | Speed multiplier +d| `int animation` | Animation index +d| `float startTime` | Start time in seconds +d| `float endTime` | End time in seconds +d| `float speed` | Speed multiplier .3+| Output flow sockets -| `out` | The flow to be activated if the input values are valid -| `err` | The flow to be activated if any of the input values is invalid -| `done` | The flow to be activated after the animation ends +d| `out` | The flow to be activated if the input values are valid +d| `err` | The flow to be activated if any of the input values is invalid +d| `done` | The flow to be activated after the animation ends |=== This node starts playing an animation using the specified input values. @@ -3977,8 +3977,8 @@ During the last 5 seconds, the position of the node zero is well-defined again. | Input value sockets | `int animation` | Animation index .2+| Output flow sockets -| `out` | The flow to be activated if the animation index is valid -| `err` | The flow to be activated if the animation index is invalid +d| `out` | The flow to be activated if the animation index is valid +d| `err` | The flow to be activated if the animation index is invalid |=== This node stops a playing animation. @@ -4002,12 +4002,12 @@ When the `in` input flow is activated: | Input flow sockets | `in` | The entry flow into this node .2+| Input value sockets -| `int animation` | Animation index -| `float stopTime` | Stop time in seconds +d| `int animation` | Animation index +d| `float stopTime` | Stop time in seconds .3+| Output flow sockets -| `out` | The flow to be activated if the input values are valid -| `err` | The flow to be activated if any of the input values is invalid -| `done` | The flow to be activated after the animation stops +d| `out` | The flow to be activated if the input values are valid +d| `err` | The flow to be activated if any of the input values is invalid +d| `done` | The flow to be activated after the animation stops |=== This node stops a playing animation. @@ -4051,8 +4051,8 @@ If multiple instances of this node exist in the graph, they **MUST** be activate |=== | Operation | `event/onTick` | Tick event .2+| Output value sockets -| `float timeSinceStart` | Relative time in seconds since the graph execution start -| `float timeSinceLastTick` | Relative time in seconds since the last tick occurred +d| `float timeSinceStart` | Relative time in seconds since the graph execution start +d| `float timeSinceLastTick` | Relative time in seconds since the last tick occurred | Output flow sockets | `out` | The flow to be activated when the tick event happens |=== @@ -4159,8 +4159,8 @@ When the `in` input flow is activated: |=== | Operation | `debug/log` | Output a debug message .2+| Configuration -| `int severity` | Message severity -| `string message` | Message template +d| `int severity` | Message severity +d| `string message` | Message template | Input flow sockets | `in` | The entry flow into this node | Input value sockets From 151988bdb65c4ee8f07b51516d217040a08d2ba3 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 6 Oct 2025 00:00:00 +0000 Subject: [PATCH 74/80] Support matrix types only for arithmetic operations --- .../KHR_interactivity/Specification.adoc | 203 +++++++++++------- 1 file changed, 127 insertions(+), 76 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 47cc9ddca0..31650ec66a 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -437,7 +437,9 @@ These limits are exposed to behavior graphs via additional glTF Object Model poi === Math Nodes -In this section, `floatN` is a placeholder for any of `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, or `float4x4` types. All value sockets of `floatN` types have the same type within a node. +Starting from this section, `floatN` is a placeholder for `float`, `float2`, `float3`, and `float4` types; `floatNxN` is a placeholder for `float2x2`, `float3x3`, and `float4x4` types. + +All value sockets of `floatN` or `floatNxN` types have the same type within a node. ==== Constants @@ -491,13 +493,15 @@ If any input value component is _NaN_, the corresponding output value component |=== | Operation | `math/abs` | Absolute value operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | latexmath:[\begin{cases} - -a & \text{if } a \lt 0 \\ - +0 & \text{if } a = \pm0 \\ - a & \text{if } a \gt 0 - \end{cases}] +| `floatN value` or + + `floatNxN value` | latexmath:[\begin{cases} + -a & \text{if } a \lt 0 \\ + +0 & \text{if } a = \pm0 \\ + a & \text{if } a \gt 0 + \end{cases}] |=== ===== Sign @@ -506,13 +510,15 @@ If any input value component is _NaN_, the corresponding output value component |=== | Operation | `math/sign` | Sign operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | latexmath:[\begin{cases} - -1 & \text{if } a \lt 0 \\ - a & \text{if } a = \pm0 \\ - +1 & \text{if } a \gt 0 - \end{cases}] +| `floatN value` or + + `floatNxN value` | latexmath:[\begin{cases} + -1 & \text{if } a \lt 0 \\ + a & \text{if } a = \pm0 \\ + +1 & \text{if } a \gt 0 + \end{cases}] |=== ===== Truncate @@ -521,9 +527,11 @@ If any input value component is _NaN_, the corresponding output value component |=== | Operation | `math/trunc` | Truncate operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | Integer value equal to the nearest integer to stem:[a] whose absolute value is not larger than the absolute value of stem:[a] +| `floatN value` or + + `floatNxN value` | Integer value equal to the nearest integer to stem:[a] whose absolute value is not larger than the absolute value of stem:[a] |=== If the argument is infinity, it is returned unchanged. @@ -534,9 +542,11 @@ If the argument is infinity, it is returned unchanged. |=== | Operation | `math/floor` | Floor operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | stem:[floor(a)], value equal to the nearest integer that is less than or equal to stem:[a] +| `floatN value` or + + `floatNxN value` | stem:[floor(a)], value equal to the nearest integer that is less than or equal to stem:[a] |=== If the argument is infinity, it is returned unchanged. @@ -547,9 +557,11 @@ If the argument is infinity, it is returned unchanged. |=== | Operation | `math/ceil` | Ceil operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | stem:[ceil(a)], value equal to the nearest integer that is greater than or equal to stem:[a] +| `floatN value` or + + `floatNxN value` | stem:[ceil(a)], value equal to the nearest integer that is greater than or equal to stem:[a] |=== If the argument is infinity, it is returned unchanged. @@ -560,9 +572,11 @@ If the argument is infinity, it is returned unchanged. |=== | Operation | `math/round` | Round operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | Value equal to the integer nearest to stem:[a] +| `floatN value` or + + `floatNxN value` | Value equal to the integer nearest to stem:[a] |=== Half-way cases **MUST** be rounded away from zero. Negative values greater than `-0.5` **MUST** be rounded to negative zero. @@ -585,9 +599,11 @@ a < 0 ? -Math.round(-a) : Math.round(a) |=== | Operation | `math/fract` | Fractional operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | stem:[a - floor(a)] +| `floatN value` or + + `floatNxN value` | stem:[a - floor(a)] |=== ===== Negation @@ -596,9 +612,11 @@ a < 0 ? -Math.round(-a) : Math.round(a) |=== | Operation | `math/neg` | Negation operation | Input value sockets -| `floatN a` | Argument +| `floatN a` or + + `floatNxN a` | Argument | Output value sockets -| `floatN value` | stem:[-a] +| `floatN value` or + + `floatNxN value` | stem:[-a] |=== ===== Addition @@ -607,10 +625,13 @@ a < 0 ? -Math.round(-a) : Math.round(a) |=== | Operation | `math/add` | Addition operation .2+| Input value sockets -d| `floatN a` | First addend -d| `floatN b` | Second addend +d| `floatN a` or + + `floatNxN a` | First addend +d| `floatN b` or + + `floatNxN b` | Second addend | Output value sockets -| `floatN value` | Sum, stem:[a + b] +| `floatN value` or + + `floatNxN value` | Sum, stem:[a + b] |=== ===== Subtraction @@ -619,10 +640,13 @@ d| `floatN b` | Second addend |=== | Operation | `math/sub` | Subtraction operation .2+| Input value sockets -d| `floatN a` | Minuend -d| `floatN b` | Subtrahend +d| `floatN a` or + + `floatNxN a` | Minuend +d| `floatN b` or + + `floatNxN b` | Subtrahend | Output value sockets -| `floatN value` | Difference, stem:[a - b] +| `floatN value` or + + `floatNxN value` | Difference, stem:[a - b] |=== ===== Multiplication @@ -631,10 +655,13 @@ d| `floatN b` | Subtrahend |=== | Operation | `math/mul` | Multiplication operation .2+| Input value sockets -d| `floatN a` | First factor -d| `floatN b` | Second factor +d| `floatN a` or + + `floatNxN a` | First factor +d| `floatN b` or + + `floatNxN b` | Second factor | Output value sockets -| `floatN value` | Product, stem:[a * b] +| `floatN value` or + + `floatNxN value` | Product, stem:[a * b] |=== For matrix arguments, this operation performs per-element multiplication. @@ -651,10 +678,13 @@ See `math/matMul` for matrix multiplication. |=== | Operation | `math/div` | Division operation .2+| Input value sockets -d| `floatN a` | Dividend -d| `floatN b` | Divisor +d| `floatN a` or + + `floatNxN a` | Dividend +d| `floatN b` or + + `floatNxN b` | Divisor | Output value sockets -| `floatN value` | Quotient, stem:[a / b] +| `floatN value` or + + `floatNxN value` | Quotient, stem:[a / b] |=== ===== Remainder @@ -663,14 +693,17 @@ d| `floatN b` | Divisor |=== | Operation | `math/rem` | Remainder operation .2+| Input value sockets -d| `floatN a` | Dividend -d| `floatN b` | Divisor +d| `floatN a` or + + `floatNxN a` | Dividend +d| `floatN b` or + + `floatNxN b` | Divisor | Output value sockets -| `floatN value` | latexmath:[\begin{cases} - \mathit{NaN} & \text{if } a = \pm \infty \text{ or } b = \pm 0 \\ - a & \text{if } a \ne \pm \infty \text{ and } b = \pm \infty \\ - a - (b \cdot \operatorname{trunc}(\frac{a}{b})) & \text{otherwise} - \end{cases}] +| `floatN value` or + + `floatNxN value` | latexmath:[\begin{cases} + \mathit{NaN} & \text{if } a = \pm \infty \text{ or } b = \pm 0 \\ + a & \text{if } a \ne \pm \infty \text{ and } b = \pm \infty \\ + a - (b \cdot \operatorname{trunc}(\frac{a}{b})) & \text{otherwise} + \end{cases}] |=== [TIP] @@ -689,10 +722,13 @@ a % b |=== | Operation | `math/min` | Minimum operation .2+| Input value sockets -d| `floatN a` | First argument -d| `floatN b` | Second argument +d| `floatN a` or + + `floatNxN a` | First argument +d| `floatN b` or + + `floatNxN b` | Second argument | Output value sockets -| `floatN value` | Smallest of the arguments +| `floatN value` or + + `floatNxN value` | Smallest of the arguments |=== For the purposes of this node, negative zero is less than positive zero. @@ -713,10 +749,13 @@ Math.min(a, b) |=== | Operation | `math/max` | Maximum operation .2+| Input value sockets -d| `floatN a` | First argument -d| `floatN b` | Second argument +d| `floatN a` or + + `floatNxN a` | First argument +d| `floatN b` or + + `floatNxN b` | Second argument | Output value sockets -| `floatN value` | Largest of the arguments +| `floatN value` or + + `floatNxN value` | Largest of the arguments |=== For the purposes of this node, negative zero is less than positive zero. @@ -737,11 +776,15 @@ Math.max(a, b) |=== | Operation | `math/clamp` | Clamp operation .3+| Input value sockets -d| `floatN a` | Value to clamp -d| `floatN b` | First boundary -d| `floatN c` | Second boundary +d| `floatN a` or + + `floatNxN a` | Value to clamp +d| `floatN b` or + + `floatNxN b` | First boundary +d| `floatN c` or + + `floatNxN c` | Second boundary | Output value sockets -| `floatN value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] +| `floatN value` or + + `floatNxN value` | latexmath:[\min(\max(a, \min(b, c)), \max(b, c))] |=== This node is defined in terms of `math/min` and `math/max` nodes defined above. @@ -758,9 +801,11 @@ This operation correctly handles a case when stem:[b] is greater than stem:[c]. |=== | Operation | `math/saturate` | Saturate operation | Input value sockets -| `floatN a` | Value to saturate +| `floatN a` or + + `floatNxN a` | Value to saturate | Output value sockets -| `floatN value` | latexmath:[\min(\max(a, 0), 1)] +| `floatN value` or + + `floatNxN value` | latexmath:[\min(\max(a, 0), 1)] |=== This node is defined in terms of `math/min` and `math/max` nodes defined above. @@ -771,11 +816,15 @@ This node is defined in terms of `math/min` and `math/max` nodes defined above. |=== | Operation | `math/mix` | Linear interpolation operation .3+| Input value sockets -d| `floatN a` | Interpolated value at stem:[0] -d| `floatN b` | Interpolated value at stem:[1] -d| `floatN c` | Unclamped interpolation coefficient +d| `floatN a` or + + `floatNxN a` | Interpolated value at stem:[0] +d| `floatN b` or + + `floatNxN b` | Interpolated value at stem:[1] +d| `floatN c` or + + `floatNxN c` | Unclamped interpolation coefficient | Output value sockets -| `floatN value` | stem:[(1 - c) * a + c * b] +| `floatN value` or + + `floatNxN value` | stem:[(1 - c) * a + c * b] |=== ==== Comparison Nodes @@ -790,8 +839,10 @@ For the purposes of these nodes, negative zero is equal to positive zero. |=== | Operation | `math/eq` | Equality operation .2+| Input value sockets -d| `floatN a` | First argument -d| `floatN b` | Second argument +d| `floatN a` or + + `floatNxN a` | First argument +d| `floatN b` or + + `floatNxN b` | Second argument | Output value sockets | `bool value` | True if the input arguments are equal, per-component; false otherwise |=== @@ -1410,7 +1461,7 @@ See individual node definitions for handling special floating-point values. |=== | Operation | `math/length` | Vector length | Input value sockets -| `float{2\|3\|4} a` | Vector +| `floatN a` | Vector | Output value sockets | `float value` | Length of stem:[a], e.g., stem:[sqrt(a_x^2 + a_y^2)] for `float2`; see the description for details |=== @@ -1451,9 +1502,9 @@ To get the squared length of stem:[a], use `math/dot` with stem:[a] provided to |=== | Operation | `math/normalize` | Vector normalization | Input value sockets -| `float{2\|3\|4} a` | Vector +| `floatN a` | Vector .2+| Output value sockets -d| `float{2\|3\|4} value` | Vector in the same direction as stem:[a] but with a unit length, e.g., latexmath:[\dfrac{\vec{a}}{\sqrt{a_x^2 + a_y^2}}] for `float2`; see the description for details +d| `floatN value` | Vector in the same direction as stem:[a] but with a unit length, e.g., latexmath:[\dfrac{\vec{a}}{\sqrt{a_x^2 + a_y^2}}] for `float2`; see the description for details d| `bool isValid` | True if the output vector value has a unit length after normalization; false otherwise |=== @@ -1477,8 +1528,8 @@ If the input vector represents a quaternion and the graph expects it to be ident |=== | Operation | `math/dot` | Dot product .2+| Input value sockets -d| `float{2\|3\|4} a` | First vector -d| `float{2\|3\|4} b` | Second vector of the same type as stem:[a] +d| `floatN a` | First vector +d| `floatN b` | Second vector of the same type as stem:[a] | Output value sockets | `float value` | Sum of per-component products of stem:[a] and stem:[b], e.g., stem:[a_x * b_x + a_y * b_y] for `float2` |=== @@ -1615,9 +1666,9 @@ d| `float4x4 b` | Transformation matrix |=== | Operation | `math/transpose` | Transpose operation | Input value sockets -| `float{2x2\|3x3\|4x4} a` | Matrix to transpose +| `floatNxN a` | Matrix to transpose | Output value sockets -| `float{2x2\|3x3\|4x4} value` | Matrix that is the transpose of stem:[a] +| `floatNxN value` | Matrix that is the transpose of stem:[a] |=== The input and output value sockets have the same type. @@ -1630,7 +1681,7 @@ This operation only reorders the matrix elements without inspecting or altering |=== | Operation | `math/determinant` | Dot product | Input value sockets -| `float{2x2\|3x3\|4x4} a` | Matrix +| `floatNxN a` | Matrix | Output value sockets | `float value` | Determinant of stem:[a] |=== @@ -1643,9 +1694,9 @@ Since this operation is a shortcut for the combination of multiplications, subtr |=== | Operation | `math/inverse` | Inverse operation | Input value sockets -| `float{2x2\|3x3\|4x4} a` | Matrix to inverse +| `floatNxN a` | Matrix to inverse .2+| Output value sockets -d| `float{2x2\|3x3\|4x4} value` | Matrix that is the inverse of stem:[a]; see the description for details +d| `floatNxN value` | Matrix that is the inverse of stem:[a]; see the description for details d| `bool isValid` | True if the input matrix is invertible; false otherwise |=== @@ -1665,10 +1716,10 @@ The output values are computed as follows: |=== | Operation | `math/matMul` | Matrix multiplication operation .2+| Input value sockets -d| `float{2x2\|3x3\|4x4} a` | First matrix -d| `float{2x2\|3x3\|4x4} b` | Second matrix +d| `floatNxN a` | First matrix +d| `floatNxN b` | Second matrix | Output value sockets -| `float{2x2\|3x3\|4x4} value` | Matrix product +| `floatNxN value` | Matrix product |=== Both input value sockets **MUST** have the same type. @@ -4431,7 +4482,7 @@ If the `value` property array length does not match the array length for the spe If the variable type is **bool** and the only array element is not a JSON boolean literal, i.e., neither `true` nor `false`, the variable is invalid and the graph **MUST** be rejected. -If the variable type is any of the **floatN** types and any of the array elements is not a JSON number, the variable is invalid and the graph **MUST** be rejected. +If the variable type is any of the **floatN** or **floatNxN** types and any of the array elements is not a JSON number, the variable is invalid and the graph **MUST** be rejected. If the variable type is **int** and the only array element is not a JSON number exactly representable as a 32-bit signed integer, the variable is invalid and the graph **MUST** be rejected. From 2c85ad8344f8fc2101397764123cc660e7f0409d Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 6 Oct 2025 00:00:00 +0000 Subject: [PATCH 75/80] Merge variable/set and variable/setMultiple nodes --- .../KHR_interactivity/Specification.adoc | 46 ++----------------- 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 31650ec66a..e8a30d6989 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -417,7 +417,7 @@ Implementations **MAY** restrict the size and complexity of behavior graphs by i * The number of nodes * The number of graph-defined output flow sockets in operations like `flow/multiGate` or `flow/sequence` * The number of configuration-defined output flow sockets in operations like `flow/switch` -* The number of configuration-defined input value sockets in operations like `pointer/get`, `math/switch`, or `variable/setMultiple` +* The number of configuration-defined input value sockets in operations like `pointer/get`, `math/switch`, or `variable/set` The graph **MUST** be rejected if it exceeds implementation-defined max values for these properties. @@ -3360,45 +3360,7 @@ This node has no internal state. [cols="1h,1,2"] |=== -| Operation | `variable/set` | Set a custom variable value -| Configuration -| `int variable` | The custom variable index -| Input flow sockets -| `in` | The entry flow into this node -| Input value sockets -| `T value` | The new variable value -| Output flow sockets -| `out` | The flow to be activated after the value is set -|=== - -[CAUTION] -==== -This node does not have a default configuration. -==== - -[CAUTION] -==== -The configuration of this node affects its value socket. -==== - -This node sets a custom variable value using the variable index provided by the `variable` configuration value and the `value` input value. - -The type `T` is determined by the referenced variable. The variable index **MUST** be a non-negative integer less than the total number of custom variables, otherwise the node is invalid and the graph **MUST** be rejected. - -This node has no internal state. - -When the `in` input flow is activated: - -1. Evaluate the `value` input value. -2. If the _variable interpolation state dynamic array_ (defined below) contains an entry with the same variable reference, remove it from the array. -3. Set the custom variable with the `variable` configuration value index to the `value` input value. -4. Activate the `out` output flow. - -===== Variable Set Multiple - -[cols="1h,1,2"] -|=== -| Operation | `variable/setMultiple` | Set multiple variables +| Operation | `variable/set` | Set one or more custom variable values | Configuration | `int[] variables` | The array containing variable indices | Input flow sockets @@ -4936,10 +4898,10 @@ The `variable/set` node sets a custom variable with index `0` when the start eve { "declaration": 1, "configuration": { - "variable": { "value": [ 0 ] } + "variables": { "value": [ 0 ] } }, "values": { - "value": { "type": 0, "value": [ 1.5 ] } + "0": { "type": 0, "value": [ 1.5 ] } } } ] From 2f4558f3fe83423cc108e6b4f70b178369446bee Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 20 Oct 2025 00:00:00 +0000 Subject: [PATCH 76/80] Refine math/inverse, math/matDecompose, and math/quatFromDirections --- .../Khronos/KHR_interactivity/Specification.adoc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index e8a30d6989..3e8223fade 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -1694,9 +1694,9 @@ Since this operation is a shortcut for the combination of multiplications, subtr |=== | Operation | `math/inverse` | Inverse operation | Input value sockets -| `floatNxN a` | Matrix to inverse +| `floatNxN a` | Matrix stem:[A] to inverse .2+| Output value sockets -d| `floatNxN value` | Matrix that is the inverse of stem:[a]; see the description for details +d| `floatNxN value` | Matrix that is the inverse of stem:[A]; see the description for details d| `bool isValid` | True if the input matrix is invertible; false otherwise |=== @@ -1704,11 +1704,11 @@ The `value` input value socket and `value` output value socket have the same typ The output values are computed as follows: -1. Let _determinant_ be the output value of the `math/determinant` operation on stem:[a] as defined above. +1. Let _determinant_ be the output value of the `math/determinant` operation on stem:[A] as defined above. -2. If _determinant_ is zero, NaN, or infinity, the `isValid` output value is false and the `value` output value is a matrix of the same type as stem:[a] with all elements set to positive zeros. +2. If _determinant_ is zero, NaN, or infinity, the `isValid` output value is false and the `value` output value is a matrix of the same type as stem:[A] with all elements set to positive zeros. -3. If _determinant_ is a finite number not equal to zero, the `isValid` output value is true and the `value` output value is a matrix that the inverse of stem:[a]. +3. If _determinant_ is a finite number not equal to zero, the `isValid` output value is true and the `value` output value is a matrix that is an implementation-defined approximation of the inverse of stem:[A]. ===== Multiplication @@ -1805,7 +1805,7 @@ d| `bool isValid` | True if the input matrix is decomposable; false otherwise The output values are computed as follows: -1. If the fourth row of stem:[A] is not stem:[(0, 0, 0, 1)] exactly, set the `isValid` output value to false and goto to the step 11. +1. If the first three elements of the fourth row of stem:[A] are not zeros or if the last element of the fourth row of stem:[A] is not close to positive one within an implementation-defined threshold, set the `isValid` output value to false and goto to the step 11. 2. Let stem:[s_x], stem:[s_y], and stem:[s_z] be lengths of the first three columns of stem:[A]. For example, stem:[s_x=sqrt(a_{11}^2+a_{21}^2+a_{31}^2)]. @@ -2027,7 +2027,7 @@ This operation does not implicitly normalize the rotation quaternion. If needed, [cols="1h,1,2"] |=== -| Operation | `math/quatFromDirections` | Create a quaternion from two directional vectors +| Operation | `math/quatFromDirections` | Create a quaternion transforming one directional vector to another .2+| Input value sockets d| `float3 a` | First direction d| `float3 b` | Second direction From 582c2fa64066d05f3b94cc8176cae546484455c2 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 27 Oct 2025 00:00:00 +0000 Subject: [PATCH 77/80] Add math/quatFromUpForward --- .../KHR_interactivity/Specification.adoc | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 3e8223fade..0279b78adb 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2069,6 +2069,47 @@ Since this operation is a shortcut for the combination of arithmetic and exponen This operation implies that both input vectors are normalized. A `math/normalize` node could be added to handle non-normalized inputs. ==== +===== Quaternion From Up and Forward Directional Vectors + +[cols="1h,1,2"] +|=== +| Operation | `math/quatFromUpForward` | Create a quaternion from the specified up and forward directions +.2+| Input value sockets +d| `float3 up` | Up direction +d| `float3 forward` | Forward direction +| Output value sockets +| `float4 value` | Rotation quaternion +|=== + +[CAUTION] +==== +This node assumes that both input directions are unit. +==== + +The `value` output socket value is computed as follows: + +1. Let latexmath:[\vec{y}] be a three-component vector corresponding to the `up` input socket value and latexmath:[\vec{r}] be a three-component vector corresponding to the `forward` input socket value. +2. If latexmath:[\vec{y}] and latexmath:[\vec{r}] are colinear within an implementation-defined threshold, +.. let latexmath:[\vec{s}] be a three-component unit vector representing a direction perpendicular to latexmath:[\vec{r}]. +3. Else +.. let latexmath:[\vec{s}] be the normalized cross product of latexmath:[\vec{y}] and latexmath:[\vec{r}]. +4. Let latexmath:[\vec{t}] be the cross product of latexmath:[\vec{r}] and latexmath:[\vec{s}]. +5. Let latexmath:[M] be a rotation matrix composed as follows: ++ +[stem] ++++++ +M = ((s_x, t_x, r_x), + (s_y, t_y, r_y), + (s_z, t_z, r_z)) ++++++ +6. Set the `value` output value to the unit quaternion corresponding to the rotation matrix stem:[M]. + +[TIP] +.Authoring Tip +==== +This operation implies that both input vectors are normalized. A `math/normalize` node could be added to handle non-normalized inputs. +==== + ==== Swizzle Nodes ===== Combine From 461eb8ddaf1d8d03a6bdea17b1d05a76c7118d1c Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Nov 2025 00:00:00 +0000 Subject: [PATCH 78/80] Add math/quatSlerp --- .../KHR_interactivity/Specification.adoc | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 0279b78adb..b4abcfa7ab 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -2110,6 +2110,56 @@ M = ((s_x, t_x, r_x), This operation implies that both input vectors are normalized. A `math/normalize` node could be added to handle non-normalized inputs. ==== +===== Quaternion Spherical Linear Interpolation + +[cols="1h,1,2"] +|=== +| Operation | `math/quatSlerp` | Spherical linear interpolation operation +.3+| Input value sockets +d| `float4 a` | First quaternion +d| `float4 b` | Second quaternion +d| `float c` | Unclamped interpolation coefficient +| Output value sockets +| `float4 value` | Interpolated value +|=== + +[CAUTION] +==== +This node assumes that both input quaternions are unit. +==== + +The `value` output socket value is computed as follows: + +1. Let +.. stem:[a_x], stem:[a_y], stem:[a_z], and stem:[a_w] be the components of stem:[a]; +.. stem:[b_x], stem:[b_y], stem:[b_z], and stem:[b_w] be the components of stem:[b]. +2. Let latexmath:[d = a_x \cdot b_x + a_y \cdot b_y + a_z \cdot b_z + a_w \cdot b_w]. +3. If latexmath:[d] is negative, +.. set latexmath:[d] to latexmath:[-d]; +.. negate all components of the quaternion latexmath:[b]. +4. If latexmath:[d] is close to positive one within an implementation-defined threshold, +.. let latexmath:[k_a = 1 - c], latexmath:[k_b = c]. +5. Else let: +.. latexmath:[\omega = \arccos(d)]; +.. latexmath:[k_a = \frac{\sin(\omega \cdot (1 - c))}{\sin(\omega)}], latexmath:[k_b = \frac{\sin(\omega \cdot c)}{\sin(\omega)}]. +6. Set the components of the `value` output socket value using the following expressions: ++ +[latexmath] ++++++ +x = a_x \cdot k_a + b_x \cdot k_b \\ +y = a_y \cdot k_a + b_y \cdot k_b \\ +z = a_z \cdot k_a + b_z \cdot k_b \\ +w = a_w \cdot k_a + b_w \cdot k_b ++++++ + +Since this operation is a shortcut for the combination of arithmetic and trigonometry operations, NaN and infinity values are propagated accordingly. + +[TIP] +.Authoring Tip +==== +This operation does not implicitly normalize the input quaternions. If needed, that step could be done explicitly by adding `math/normalize` nodes. +==== + ==== Swizzle Nodes ===== Combine From 248c2e315899ea491602fad111020c5bc570677e Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 24 Nov 2025 00:00:00 +0000 Subject: [PATCH 79/80] Tweak Turing completeness language --- extensions/2.0/Khronos/KHR_interactivity/Specification.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index b4abcfa7ab..3db3c5b77e 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -194,7 +194,7 @@ Behavior graphs and trigger-action lists share common features, such as being sa On the other hand, behavior graphs are a superset of trigger-action lists, meaning that the former can support everything that trigger-action lists can, and more. Behavior graphs support “Queries”, “Logic” and “Control Flow” nodes, making them more expressive and capable of creating more sophisticated behaviors. This makes behavior graphs the preferred method of choice for high-end game engines, as it offers an identical safety model as trigger-action lists while being more expressive. === Turing Completeness -The execution model and node choices for this extension mean that it is Turing-complete. This means that an implementation of this can execute any computation and it is also hard to predict if it will run forever, e.g., halt or not. +The execution model and operation choices for this extension mean that it is Turing-complete. This means that an implementation of this can execute any computation and it is not always possible to predict if it will run forever, e.g., halt or not. While this may present security implications, it is not a major hindrance and can be safely mitigated so that any implementation does not become susceptible to denial of services by badly behaving behavior graphs, whether intentional or not. From cdc399f4deb4693fd41d3e131d4bcc52564025be Mon Sep 17 00:00:00 2001 From: Alexey Knyazev <3479527+lexaknyazev@users.noreply.github.com> Date: Mon, 15 Dec 2025 00:00:00 +0000 Subject: [PATCH 80/80] Add math/Tau --- .../2.0/Khronos/KHR_interactivity/Specification.adoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc index 3db3c5b77e..3e4aae33d0 100644 --- a/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc +++ b/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc @@ -459,6 +459,14 @@ All value sockets of `floatN` or `floatNxN` types have the same type within a no | Output value sockets | `float value` | 3.141592653589793 |=== +===== Tau + +[cols="1h,1,2"] +|=== +| Operation | `math/Tau` | Ratio of a circle's circumference to its radius +| Output value sockets | `float value` | 6.283185307179586 +|=== + ===== Infinity [cols="1h,1,2"]