<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=Generator content="Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:#954F72;
        text-decoration:underline;}
.MsoChpDefault
        {mso-style-type:export-only;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></style></head><body lang=EN-US link=blue vlink="#954F72"><div class=WordSection1><p class=MsoNormal>I am pretty sure I spotted an animations array in glTF.  Discover for yourself, please, I don’t know the context of mine!</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Sent from <a href="https://go.microsoft.com/fwlink/?LinkId=550986">Mail</a> for Windows 10</p><p class=MsoNormal><o:p> </o:p></p><div style='mso-element:para-border-div;border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='border:none;padding:0in'><b>From: </b><a href="mailto:brutzman@nps.edu">Brutzman, Donald (Don) (CIV)</a><br><b>Sent: </b>Saturday, February 2, 2019 11:40 PM<br><b>To: </b><a href="mailto:andreasplesch@gmail.com">Andreas Plesch</a>; <a href="mailto:michalis.kambi@gmail.com">Michalis Kamburelis</a><br><b>Cc: </b><a href="mailto:x3d-public@web3d.org">X3D Graphics public mailing list</a><br><b>Subject: </b>Re: [x3d-public] X3D meeting minutes 1 February 2018: strategy formapping glTF 2.0 to X3Dv4; name tokenization, event-stream mixing</p></div><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Likewise thanks for progressive analysis, really interesting.  Reactions:</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>a. Naming convention for normalizing/demunging ID nametokens is straightforward enough to define, with extra credit if round-tripping is achieved without obscurantism.  Key question: can more than one animation element (i.e. TimeSensor equivalent) exist in a glTF model?</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>b. Michalis your animation cross-fading is really interesting.  Perhaps it can be conceptualized declaratively as type-aware Mixer nodes in the X3D Event Utilities component?  We can currently animate any data type, such functionality would be a super addition for authors if it can fit into event chains.  Perhaps designing a new node prototype is a good way to proceed?</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>                X3D Event Utilities component</p><p class=MsoNormal>                http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/utils.html</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>                X3D Example Archives: X3D for Web Authors, Chapter 09 Event Utilities Scripting</p><p class=MsoNormal>                https://x3dgraphics.com/examples/X3dForWebAuthors/Chapter09EventUtilitiesScripting</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>                X3D Event Utility Nodes: Field Event Diagrams</p><p class=MsoNormal>                https://x3dgraphics.com/examples/X3dForWebAuthors/Chapter09EventUtilitiesScripting/X3dEventUtilityNodeEventDiagrams.pdf</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>On 2/2/2019 1:22 PM, Andreas Plesch wrote:</p><p class=MsoNormal>> On Sat, Feb 2, 2019 at 2:26 PM Michalis Kamburelis</p><p class=MsoNormal>> <michalis.kambi@gmail.com> wrote:</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>>   Andreas Plesch <andreasplesch@gmail.com> wrote:</p><p class=MsoNormal>>>> A few edges cases are:</p><p class=MsoNormal>>>><o:p> </o:p></p><p class=MsoNormal>>>> https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#reference-animation</p><p class=MsoNormal>>>> says that a name is not required. What name should be used then ?</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> Hm, that's a curious case. Can such unnamed animation be played by</p><p class=MsoNormal>>> other glTF APIs/viewers?</p><p class=MsoNormal>> </p><p class=MsoNormal>> Yes, since the name in glTF is considered metadata. The authoritative</p><p class=MsoNormal>> reference to something is its index in an array. All animations in</p><p class=MsoNormal>> glTF are contained in an animations array. So an animation without a</p><p class=MsoNormal>> name may be listed as 'first glTF animation' but it is up to the</p><p class=MsoNormal>> application how to label it.</p><p class=MsoNormal>> </p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> Consistently, we could convert unnamed glTF animation to an unnamed</p><p class=MsoNormal>>> TimeSensor. It would be equally uncomfortable (to access) then, both</p><p class=MsoNormal>>> in X3D and in glTF :)</p><p class=MsoNormal>> </p><p class=MsoNormal>> In glTF, it would accessed as gltf.animations[index] . Actually named</p><p class=MsoNormal>> animations would also be accessed by their index.</p><p class=MsoNormal>> </p><p class=MsoNormal>>>><o:p> </o:p></p><p class=MsoNormal>>>> The name can contain any character including spaces which are not</p><p class=MsoNormal>>>> allowed in DEF names. How to sanitize ?</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> We use a "brutal" solution in CGE right now that replaces everything</p><p class=MsoNormal>>> except ['a'..'z', 'A'..'Z', '0'..'9'] to an underscore,</p><p class=MsoNormal>>> https://github.com/castle-engine/castle-engine/blob/master/src/x3d/x3dloadinternalutils.pas#L60.</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> It's "brutal" in the sense that it knowingly replaces much more than</p><p class=MsoNormal>>> necessary. I don't have a strong opinion whether this is the right</p><p class=MsoNormal>>> solution, I could change it to be consistent with other X3D players.</p><p class=MsoNormal>> </p><p class=MsoNormal>> There is probably a function somewhere which converts a string to a</p><p class=MsoNormal>> NMTOKEN , the DEF XML data type:</p><p class=MsoNormal>> </p><p class=MsoNormal>> NMTOKEN is an XML term for Name Token. NMTOKEN is a special kind of</p><p class=MsoNormal>> CDATA string that must match naming requirements for legal characters,</p><p class=MsoNormal>> with no whitespace characters allowed. Additionally, from XML</p><p class=MsoNormal>> specification: disallowed initial characters for Names include numeric</p><p class=MsoNormal>> digits, diacritics (letter with accent or marking), the "." period</p><p class=MsoNormal>> character (sometimes called full stop) and the "-" hyphen character.</p><p class=MsoNormal>> </p><p class=MsoNormal>> The brutal solution still allows for initial numeric digits, so it may</p><p class=MsoNormal>> have to become really violent ;)</p><p class=MsoNormal>> </p><p class=MsoNormal>>>><o:p> </o:p></p><p class=MsoNormal>>>> I suppose for blending animations, eg. mixing interpolator output by</p><p class=MsoNormal>>>> weight, there are additional processing steps. Do you use an actual</p><p class=MsoNormal>>>> script or just internal computing ?</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> I'm using an internal mechanism. It allows to send an event and say</p><p class=MsoNormal>>> that "the effect of this event should be applied only partially".</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> You can watch the demo movie on</p><p class=MsoNormal>>> https://castle-engine.io/wp/2018/03/21/animation-blending/ , it shows</p><p class=MsoNormal>>> how it works for both animating Transform.translation/rotation</p><p class=MsoNormal>>> (skeletal animation from Spine) and Coordinate.point (animation of</p><p class=MsoNormal>>> mesh coordinates).</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> The reasons for doing it without any new X3D nodes:</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> - I wanted animation blending to work with any existing X3D animation.</p><p class=MsoNormal>>> So I didn't want to require X3D authors to use a new node, or to</p><p class=MsoNormal>>> organize existing TimeSensor/interpolators/routes in any new way.</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> - I wanted it to also allow "fade out" of the animation (when the</p><p class=MsoNormal>>> animation is applied with less and less impact, but no new animation</p><p class=MsoNormal>>> takes it's place).</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> - I wanted it to work for any combination of animations. E.g.</p><p class=MsoNormal>>> Animation1 affects bones A, B, C, and Animation2 affects bones B, C,</p><p class=MsoNormal>>> D. ("Bone" here can mean just a "Transform" node for Spine skeletal</p><p class=MsoNormal>>> animation.) So some bones are controlled by both animations (B, C),</p><p class=MsoNormal>>> but some not. In all cases, fade out of the old Animation1 and fade-in</p><p class=MsoNormal>>> of Animation2 should work as expected.</p><p class=MsoNormal>> </p><p class=MsoNormal>> There may be a way to accomplish goals 2 and 3 with a Mixer node. Fade</p><p class=MsoNormal>> out could be mixing an interpolator with a no-op interpolator (zeroes,</p><p class=MsoNormal>> or ones in the keyValue). Combinations may just require multiple</p><p class=MsoNormal>> mixers but potentially many to cover all combinations.  It may not be</p><p class=MsoNormal>> unreasonable to ask scene author to use a new node since it may turn</p><p class=MsoNormal>> out to be quite similar to defining the blending in the play animation</p><p class=MsoNormal>> call. Instead of saying blend animations A and B with this weight, one</p><p class=MsoNormal>> says generate a new interpolator output by mixing interpolator A and B</p><p class=MsoNormal>> with this weight.</p><p class=MsoNormal>> </p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> <details></p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> (Be warned, this is one of the more complicated algorithms in CGE :)</p><p class=MsoNormal>>> It's not a lot of code lines, but understanding and writing this was</p><p class=MsoNormal>>> hard.)</p><p class=MsoNormal>> </p><p class=MsoNormal>> Thanks for writing this up. It will take me a while to digest :)</p><p class=MsoNormal>> </p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> I track when we should do cross-fading between animations (it has to</p><p class=MsoNormal>>> be initilalized by calling TCastleScene.PlayAnimation), and when we</p><p class=MsoNormal>>> are doing cross-fading, then</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>> </p><p class=MsoNormal>> Ok, one says cross-fade between A and B within two seconds.</p><p class=MsoNormal>> </p><p class=MsoNormal>> This translates to animating the weight/fraction, I believe.</p><p class=MsoNormal>> </p><p class=MsoNormal>>> 1. First the TimeSensor of the old animation sends events in a "fake"</p><p class=MsoNormal>>> way (it sends events despite being active or not), with "partial"</p><p class=MsoNormal>>> factor falling down from 1.0 to 0.0 as the scene time passes. The</p><p class=MsoNormal>>> procedure to "send TimeSensor events in a fake way",</p><p class=MsoNormal>>> TTimeSensorNode.FakeTime , is documented on</p><p class=MsoNormal>>> https://github.com/castle-engine/castle-engine/blob/master/src/x3d/x3dnodes_standard_time.inc#L343</p><p class=MsoNormal>>> .</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>>      The "partial" factor is passed on as one event produces another,</p><p class=MsoNormal>>> e.g. TimeSensor sends fraction_changed, some interpolator receives it</p><p class=MsoNormal>>> and then sends value_changed, and the "partial factor" information is</p><p class=MsoNormal>>> still carried over. Various complicated setups are covered by this</p><p class=MsoNormal>>> approach, in all cases the "event cascade" carries the information</p><p class=MsoNormal>>> that "it should be applied only partially".</p><p class=MsoNormal>> </p><p class=MsoNormal>> </p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>>      The resulting "partial" values (e.g. Transform.translation, or</p><p class=MsoNormal>>> Coordinate.point) are stored at the field, with current accumulated</p><p class=MsoNormal>>> "weight". But they are not immediately applied to the field actual</p><p class=MsoNormal>>> value, they are only stored in an additional record "attached" to the</p><p class=MsoNormal>>> field. Some notes about it are at TPartialReceived record in</p><p class=MsoNormal>>> https://github.com/castle-engine/castle-engine/blob/master/src/x3d/castlefields_x3dfield.inc#L87</p><p class=MsoNormal>>> .</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>>      This entire procedure is done by "PartialSendBegin" method which</p><p class=MsoNormal>>> is in https://github.com/castle-engine/castle-engine/blob/master/src/x3d/castlescenecore.pas</p><p class=MsoNormal>>> .</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> 2. Then the TimeSensor of the new animation sends events in a regular</p><p class=MsoNormal>>> way (without FakeTime), but also with "partial factor" (growing from</p><p class=MsoNormal>>> 0.0 to 1.0).</p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> 3. At the end, for all fields that received some "partial" values in</p><p class=MsoNormal>>> this frame, we calculate the final field value. This is done by</p><p class=MsoNormal>>> PartialSendEnd method in</p><p class=MsoNormal>>> https://github.com/castle-engine/castle-engine/blob/master/src/x3d/castlescenecore.pas</p><p class=MsoNormal>>> that sends TX3DField.InternalAffectedPartial . We do a lerp between</p><p class=MsoNormal>>> "PartialValue" (the sum of all values received in this frame, weighted</p><p class=MsoNormal>>> by their "partial" factor) and a "SettledValue" (last known value when</p><p class=MsoNormal>>> animation blending did not occur).</p><p class=MsoNormal>> </p><p class=MsoNormal>> It sounds like the SettledValue is the old value and PartialValue is</p><p class=MsoNormal>> the current value, becoming the new value. The settledValue probably</p><p class=MsoNormal>> is weighted by 1 - sum of partial factor or so.</p><p class=MsoNormal>> </p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>>      This way the mechanism works e.g. when cross-fading two animations</p><p class=MsoNormal>>> (old animation has partial = 0.7, new one has 0.3, their final weight</p><p class=MsoNormal>>> is 1.0 and then the "SettledValue" doesn't matter) or when old</p><p class=MsoNormal>>> animation fades away (so the "partial" value has decreasing weight,</p><p class=MsoNormal>>> and is mixed with unchanging "SettledValue").</p><p class=MsoNormal>> </p><p class=MsoNormal>> Ok, there is something else to understand.</p><p class=MsoNormal>> </p><p class=MsoNormal>>><o:p> </o:p></p><p class=MsoNormal>>> </details></p><p class=MsoNormal>> </p><p class=MsoNormal>> Thanks for the detailed description. It would be probably insightful</p><p class=MsoNormal>> to try do this with a script to then better understand how blending</p><p class=MsoNormal>> animations could be better supported by X3D. Such blending would be</p><p class=MsoNormal>> important for HAnim. HAnim examples would be good targets for such an</p><p class=MsoNormal>> attempt. Currently, they just stop one animation and start another one</p><p class=MsoNormal>> when a button is clicked.</p><p class=MsoNormal>> </p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>all the best, Don</p><p class=MsoNormal>-- </p><p class=MsoNormal>Don Brutzman  Naval Postgraduate School, Code USW/Br       brutzman@nps.edu</p><p class=MsoNormal>Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA   +1.831.656.2149</p><p class=MsoNormal>X3D graphics, virtual worlds, navy robotics http://faculty.nps.edu/brutzman</p><p class=MsoNormal>_______________________________________________</p><p class=MsoNormal>x3d-public mailing list</p><p class=MsoNormal>x3d-public@web3d.org</p><p class=MsoNormal>http://web3d.org/mailman/listinfo/x3d-public_web3d.org</p><p class=MsoNormal><o:p> </o:p></p></div></body></html>