From 3c5227a8666e44b8894d34edc692a05bfd64849b Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Fri, 31 May 2019 14:40:46 -0400 Subject: [PATCH 1/4] Started work on CZML articulations demo. --- .../gallery/CZML Model Articulations.html | 107 ++++++++++++++++++ Source/DataSources/CzmlDataSource.js | 54 ++++++++- Source/DataSources/ModelGraphics.js | 30 +++++ Source/DataSources/ModelVisualizer.js | 28 ++++- 4 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 Apps/Sandcastle/gallery/CZML Model Articulations.html diff --git a/Apps/Sandcastle/gallery/CZML Model Articulations.html b/Apps/Sandcastle/gallery/CZML Model Articulations.html new file mode 100644 index 000000000000..f5b81c38baea --- /dev/null +++ b/Apps/Sandcastle/gallery/CZML Model Articulations.html @@ -0,0 +1,107 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + + diff --git a/Source/DataSources/CzmlDataSource.js b/Source/DataSources/CzmlDataSource.js index a61b8b68662b..2132ef20cf00 100644 --- a/Source/DataSources/CzmlDataSource.js +++ b/Source/DataSources/CzmlDataSource.js @@ -1713,16 +1713,28 @@ define([ processPacketData(Number, model, 'colorBlendAmount', modelData.colorBlendAmount, interval, sourceUri, entityCollection); processPacketData(DistanceDisplayCondition, model, 'distanceDisplayCondition', modelData.distanceDisplayCondition, interval, sourceUri, entityCollection); + var i, len; var nodeTransformationsData = modelData.nodeTransformations; if (defined(nodeTransformationsData)) { if (isArray(nodeTransformationsData)) { - for (var i = 0, len = nodeTransformationsData.length; i < len; i++) { + for (i = 0, len = nodeTransformationsData.length; i < len; i++) { processNodeTransformations(model, nodeTransformationsData[i], interval, sourceUri, entityCollection); } } else { processNodeTransformations(model, nodeTransformationsData, interval, sourceUri, entityCollection); } } + + var articulationsData = modelData.articulations; + if (defined(articulationsData)) { + if (isArray(articulationsData)) { + for (i = 0, len = articulationsData.length; i < len; i++) { + processArticulations(model, articulationsData[i], interval, sourceUri, entityCollection); + } + } else { + processArticulations(model, articulationsData, interval, sourceUri, entityCollection); + } + } } function processNodeTransformations(model, nodeTransformationsData, constrainedInterval, sourceUri, entityCollection) { @@ -1772,6 +1784,46 @@ define([ } } + function processArticulations(model, articulationsData, constrainedInterval, sourceUri, entityCollection) { + var combinedInterval; + var packetInterval = articulationsData.interval; + if (defined(packetInterval)) { + iso8601Scratch.iso8601 = packetInterval; + combinedInterval = TimeInterval.fromIso8601(iso8601Scratch); + if (defined(constrainedInterval)) { + combinedInterval = TimeInterval.intersect(combinedInterval, constrainedInterval, scratchTimeInterval); + } + } else if (defined(constrainedInterval)) { + combinedInterval = constrainedInterval; + } + + var articulations = model.articulations; + var keys = Object.keys(articulationsData); + for (var i = 0, len = keys.length; i < len; ++i) { + var key = keys[i]; + + if (key === 'interval') { + continue; + } + + var articulationStageData = articulationsData[key]; + + if (!defined(articulationStageData)) { + continue; + } + + if (!defined(articulations)) { + model.articulations = articulations = new PropertyBag(); + } + + if (!articulations.hasProperty(key)) { + articulations.addProperty(key); + } + + processPacketData(Number, articulations, key, articulationStageData, combinedInterval, sourceUri, entityCollection); + } + } + function processPath(entity, packet, entityCollection, sourceUri) { var pathData = packet.path; if (!defined(pathData)) { diff --git a/Source/DataSources/ModelGraphics.js b/Source/DataSources/ModelGraphics.js index 0e357b380c86..fb1e83c9b2ca 100644 --- a/Source/DataSources/ModelGraphics.js +++ b/Source/DataSources/ModelGraphics.js @@ -26,6 +26,14 @@ define([ return new PropertyBag(value, createNodeTransformationProperty); } + function createArticulationStageProperty(value) { + return createPropertyDescriptor('value'); + } + + function createArticulationStagePropertyBag(value) { + return new PropertyBag(value, createArticulationStageProperty); + } + /** * A 3D model based on {@link https://github.com/KhronosGroup/glTF|glTF}, the runtime asset format for WebGL, OpenGL ES, and OpenGL. * The position and orientation of the model is determined by the containing {@link Entity}. @@ -47,6 +55,7 @@ define([ * @param {Property} [options.runAnimations=true] A boolean Property specifying if glTF animations specified in the model should be started. * @param {Property} [options.clampAnimations=true] A boolean Property specifying if glTF animations should hold the last pose for time durations with no keyframes. * @param {PropertyBag} [options.nodeTransformations] An object, where keys are names of nodes, and values are {@link TranslationRotationScale} Properties describing the transformation to apply to that node. + * @param {PropertyBag} [options.articulations] An object, where keys are composed of an articulation name, a single space, and a stage name, and the values are numeric properties. * @param {Property} [options.shadows=ShadowMode.ENABLED] An enum Property specifying whether the model casts or receives shadows from each light source. * @param {Property} [options.heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @param {Property} [options.distanceDisplayCondition] A Property specifying at what distance from the camera that this model will be displayed. @@ -82,6 +91,8 @@ define([ this._runAnimationsSubscription = undefined; this._nodeTransformations = undefined; this._nodeTransformationsSubscription = undefined; + this._articulations = undefined; + this._articulationsSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._distanceDisplayCondition = undefined; @@ -206,6 +217,14 @@ define([ */ nodeTransformations : createPropertyDescriptor('nodeTransformations', undefined, createNodeTransformationPropertyBag), + /** + * Gets or sets the set of articulation values to apply to this model. This is represented as an {@link PropertyBag}, where keys are + * composed as the name of the articulation, a single space, and the name of the stage. + * @memberof ModelGraphics.prototype + * @type {PropertyBag} + */ + articulations : createPropertyDescriptor('articulations', undefined, createArticulationStagePropertyBag), + /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof ModelGraphics.prototype @@ -305,6 +324,7 @@ define([ result.runAnimations = this.runAnimations; result.clampAnimations = this.clampAnimations; result.nodeTransformations = this.nodeTransformations; + result.articulations = this.articulations; result.heightReference = this._heightReference; result.distanceDisplayCondition = this.distanceDisplayCondition; result.silhouetteColor = this.silhouetteColor; @@ -361,6 +381,16 @@ define([ this.nodeTransformations = new PropertyBag(sourceNodeTransformations, createNodeTransformationProperty); } } + + var sourceArticulations = source.articulations; + if (defined(sourceArticulations)) { + var targetArticulations = this.articulations; + if (defined(targetArticulations)) { + targetArticulations.merge(sourceArticulations); + } else { + this.articulations = new PropertyBag(sourceArticulations, createArticulationStageProperty); + } + } }; return ModelGraphics; diff --git a/Source/DataSources/ModelVisualizer.js b/Source/DataSources/ModelVisualizer.js index 06469ee82796..0c0132632161 100644 --- a/Source/DataSources/ModelVisualizer.js +++ b/Source/DataSources/ModelVisualizer.js @@ -137,6 +137,7 @@ define([ url : resource.url, animationsRunning : false, nodeTransformationsScratch : {}, + articulationsScratch : {}, loadFail : false }; modelHash[entity.id] = modelData; @@ -196,6 +197,28 @@ define([ modelNode.matrix = Matrix4.multiply(modelNode.originalMatrix, transformationMatrix, transformationMatrix); } } + + // Apply articulations + var anyArticulationUpdated = false; + var articulations = Property.getValueOrUndefined(modelGraphics._articulations, time, modelData.articulationsScratch); + if (defined(articulations)) { + var articulationStageKeys = Object.keys(articulations); + for (var s = 0, numKeys = articulationStageKeys.length; s < numKeys; ++s) { + var key = articulationStageKeys[s]; + + var articulationStageValue = articulations[key]; + if (!defined(articulationStageValue)) { + continue; + } + + anyArticulationUpdated = true; + model.setArticulationStage(key, articulationStageValue); + } + } + + if (anyArticulationUpdated) { + model.applyArticulations(); + } } } @@ -291,7 +314,7 @@ define([ for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; if (defined(entity._model) && defined(entity._position)) { - clearNodeTransformationsScratch(entity, modelHash); + clearNodeTransformationsArticulationsScratch(entity, modelHash); entities.set(entity.id, entity); } else { removeModel(this, entity, modelHash, primitives); @@ -314,10 +337,11 @@ define([ } } - function clearNodeTransformationsScratch(entity, modelHash) { + function clearNodeTransformationsArticulationsScratch(entity, modelHash) { var modelData = modelHash[entity.id]; if (defined(modelData)) { modelData.nodeTransformationsScratch = {}; + modelData.articulationsScratch = {}; } } From c6b9e52a585a8ee0e6d84f438758481fa3aa221b Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 5 Jun 2019 11:57:28 -0400 Subject: [PATCH 2/4] Add tests for Entity-level articulations --- .../gallery/CZML Model Articulations.jpg | Bin 0 -> 9318 bytes Source/DataSources/ModelGraphics.js | 8 +-- Specs/DataSources/ModelGraphicsSpec.js | 32 ++++++++++ Specs/DataSources/ModelVisualizerSpec.js | 56 ++++++++++++++++++ 4 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 Apps/Sandcastle/gallery/CZML Model Articulations.jpg diff --git a/Apps/Sandcastle/gallery/CZML Model Articulations.jpg b/Apps/Sandcastle/gallery/CZML Model Articulations.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c85729c2e9073aa9b82ee6f6fc8ec3f8220ba7b8 GIT binary patch literal 9318 zcmb8U1z1#18#aD+fu&(dK|tw}?vn0i0ZAp7uBAa*LQ2x5lx~oakQR_eq*J6NB&0-8 z;#>U1`~I)%|9;o?J=bC8o+oC`JbRxxXNH@(n-u`6s;Hs}fKb8#;sAh~Yk)+~9d6+X zx2E;6_q3x`Qqj=9Sp(Oo$jO;$!?YAt)D=+P007Q)vU2u7=K%m`7f*MXk}R#Ekufdi zIsgGM078HRpt7*?aMRFL)C2yfoXr5Jo&aEy^R}=5>GuC`z_*5bSOEZt7G;*Ua&z}Y zVRIA~^6_-L#px(aYGr3(jlz{E%;Ao@K@^_5)m#3B_inN69}Gr8fYe09QJG-Fz2OIu{t#5I_Ep~SDLfzXRxt$>%+(lm(RWqXgr~xHF z1<(Mr0b0Nk@B-`sC%_ZnMAa@RjRycj*=7Gn{M|qC+NdZ?RFpkng^G{|Tmfgm;!iwq z`x~HqQ1YMLdf4#u{ZWDNWB~wU>E`By2>>ut0N^U|=H_?a&COLl0H7@bK&Q)p;$3qA zK;#EXpZFggV-5fih66xj&wq55X#nsp3IHhP-7MTK{)~f$D#1230B~Fa0Qg1#Kr#jZ zI3|DP4Q0Ev0|m1Hpohwp`Un7IWC8%QEvj$R|1obYRKUM^`@j19oxhvUfE<7ZzLnb& z4JGIh^jpD#KrkTKSU5P?SlHM&c!YR3xCFS^*!V>F1cXp15flfHn1mQgg2K?-PC&Pj zXy{m|KqxLYE=v3VC^y{z5f*?1R?$F20GJ4bMg+R)0jO@DAut-~HWPmf2pt2A2Ejxb zDQ}-fl>B2s)gUk$I>yZcfQJeJL(!lp%?07~k+ih53f8l+#5C=(K;UjLKo8(Ix(p*PueX#;wPE48 zTiV3v=%}uMg9PUf#qSd|4Aj3}N%&4<%gAVYVM!|kSOCP^wd~mEWO(#Fr?8WJZf<_5 zxH5v476<{9E4D>EC%w1g&2#HnS=km6LoKkd09pVHz{0{(X=Y@h@^qwFAdoT$2S-~k zm-4PGGzm-2KDMr}?FLZ)#U+0F>Rog5aPPoYb;hse3qmEn5LH|~Qc7}J4!Y%gTU$R* z4eMTT|a zuC4@CbD1Gn00x_kjCy!DI$c)mmwsOw#B_6CE`11U4a+UU;B!jCQV2&e12}E-YTJ54 zXnOIf8Nk88!5dDmS(8O2h1L+S%Ex14w{6RNA+uxt`0A*5hm{=%$EL7U78*|9DJ>0} zk4^ayxahN-)!!(1uS1oTlsU*1E!qhh&Lj&4q)9_SGBWb!mp8zZG^&C4*@~!|oz4B% zwL3N??>RaiV-k~=0Ej9bd*aeinX1LLY=f<*k|}+^$t49lvis*Z7v|F9U&rReTT=-6 zsEym=42^78vEj}8QM32!d&JwE6fX&~=xkjEz21r%t=SqL3MBM-Gxa@9%VvnL#kN9h zJ?l*!ue(+Bn>tQEN*0QV`MR{WHH-Nfo2oQIK|vJXSWtAr1VHGh=j0#G{oxxC8HAWb znv@nwPS3!ojbb0_2LwPE;2#CLg6yruI}D5OA?<0l@4*kq`F6AH4ZhHrnCb53Iogfw z#)pfnI|?u9zEJiX+q~8=r0$cWuh59Gb6O4s}f0CVBGG@ zo5H2`Giikcq1W(`vydnwr(r8>ShHjI1~}snv|+C-V)?OSuZCFm+rD}*MGWhg85T|% zKjqDIYJm}Fev2gD(#^*lJ+{n@8^YpQ8-Z1>9}oxy|h)SA|ePs;8d z72K@H!NS?zvR8O`#2TBG-}0H78_^@Kdo1R7UlAfk3Wiqr1@GrQg9Os61>sh_eYK{y z9)nrTW8U=)f=BUJ^=sv}fhuz{S;v+Gm$Jf-RCaa^*CBY6KSn?O<8|vC8kO4Bz$+8$ zlZEpdf>16h4^S!1qNw@r&i?L`f`@n}F|Gf#1vwuT!Jvcm-@{4JDyIDF@&64|P2U9y zRFEYO@S1jSl9!1Es+fBHN&4}=U&@{9FX0(SLQi{)37AJU%LhWIjEUUN3Zm<%HPZDT z$H+_@C9ZK|(J15*un!=F{PoErZFOy=zd4~A7RoR}w4vjNTAG7>?s)b}j`zES^3q{+ zWP!XXFQ@hsLe&7r4f5MUJ#hmI(exP_@)UVAQS6}2LG(qFs zsa4pYT3^Vf_TBZZ(@TMunl*07U--eyBFVFMv2+os(Y4yUX4@I$p63WZ6Bu!aUcYH= zErPYa#^lwTT=$9b9yz^I0{Xte2KPsd$&K}Nyz@vL^@|i z)z=5&B$%!amZoKA*a^%|b|dri^-qF&eLn`H=*@vzcfnu)0zw00{nYS@QTBX2q?2rmzoqo@}y45uV_H@U(;%VjtD> zx0{xpGQtT=ReFW#N-r#5F|6cFNujNcTu`}IyJTY{;KE3w>p`;Q2s7_&494sGk_9o; z#s?{OrW#1~AttkV56XV<3p|q53;F7V6;dJH-!DYu;VugB{N!;XyUO}U#%!Eq$QU)0 z;_csx2b`qHnBiN+>_;Lt^SH_1DxQP-*+mR*ljfE8%I7C9;)0ZF_nzMClVZ4^gsS^* z8~wE$RdF9<22+zDftLM*m(^#1TuePLJ>Tz0>X2~2$>mR&o=2~GaKsfFkt@U%ql#Ec zg(PdkAsZd|Q3@*Ie5izjzz`4!f`8XgpOB7T83U0W*L?TwK!zp;p5jf zcduRjlXmcJ+K=KD+idL~zm0~~z2TGpV9DNU6lWYC%%EbEnNi#(r+`&UWT&gqndu-Q z+vOMePL^Tw*f-u}6&VxW$=RRS&)R0g{SnM5x$=3iVKb=>=gx1DmKW-RGd6%}U(du7 zV=h7J#QeMaJNQbHwZ)Y4eb+>^xao6|4%vfq!K1UgHf2W?2-5R5Ck2_y2A(vlwjmqh zA?2{F9A#F~XN2?5LouSlFJ&|iyu)6zJDbY^Jjo)Qm3H_44YFCX*CL2pgGb>VG z5>cXVWM2Gi!gzUXS5&8-;>AjN)NpA=ZvhN`iAJ`@=`I?pZ!%>4XjAC^YGg`+N>WSe z52gDp@6&9Q+HBiK*dBHC>G$Y-n41xT8hqqJpT5kWYHldNYaLNIaNzdPd72r!IQ3*R z-$(FHb7}j>-G3Xe|FQi1W4RW*i)apgh9Jq3-gjXx`0+EW;J!-f4M4bLYjtq7y8I>W zT{Gx(7}9?Zzcvi+Gn-|6Uu+bNH2eDMck&Iu_={y+GUHUC_A5Nufpx*5#65qHne)AE z@WmgW}p)h(_xU%^FkQ-q-Asvp`>K|g0dD#DS2JvaAtb3NQwEk1H%ZZ@xrIOm_dX=@w*?3{E1WaUmx z@#7w6d^)zm;Apr3M2Xy8KP5X=Y6(uQYqgB`3CW((pq=)a&OgZxds>i%+3EJ%M=mon zQ_MZ<lMJSZVpi#! zor>(yFt1!-t+;e!h6-u;<22nqRbd3VJXea;x#n0iK5s7;6@93oVi`OknJB@aq+09L zlL@%*e5~}9C=;vYw#9f8HElQVD6`-w7VF)`DH2R-%hT|y0NU}SX-32G_2MAf$n=|& zr%$@cCl&&F8gjsDmv!g8^+6#Y*iJf%in;q)r}Ye!3$;G8>K_ ziJ6}?p0`ZIyz0;yI#ZZS8ToO#AF=o7>;3d%9Y`o!Y@-Be9AbW!9pmE~L zyjg}9vxH+Y&~N&gqUDz)FW&}xla*D{nFs@1E9&gvSpNhl# zKit)s9_sbFcT!dPKD8K;TbKu-<4ogvbSxVhq?vF=pK6+k*c?TOTimG;T`uP-{!G*5 z!OXd~Q0+dxIfN=)@8~UFmlZs`50mm7_b6~^A6B?D(mybk_@aA|De*=BAXBJ5P`Sfs zh&TI1*-s71?S-oM^yAs_<@cNIPQ0{Q?B2yXRR1QA#iv3A@sCf_FPGbmDo*27Z5=*&9$@8|LsFph)8W+L(3vP>=+j<-XT%2!k#VS-X z{aS0HnW~o9^JVuTZ=@^EH;$~xa*K-(ysHX&jqPtj zPtLnlubcF6U+6NF-kXU>tXl6P8xAG8Kj8}FIAngVW;;3Wqji@_p zJX#sW!^C$urhIH_b|ig$@yPWZvyE8{K+Rv?c&$EKDJF%>-W@;MXZyu>y~#{BDAYd3 zkWj5#H72F?Y4s<-e<2e1>KDcOIm?@+q_F>`Vd95zSV?o7s*&gUJ=4Y+YJ)!aXpcAR6GUk*0|B+fb5C8Z$PGWqQs&d&kA90B7TUv4#AVk6b0?p9oViV^ zB*kst(($dsg&cDrV%y;>=6kaX%F{LwkXzs`>>;#^nj^gjFf zb^>b6W`m+O?0??p82@ZZK}2-CGCCGeT1044-l24D*ZArY&p-PEDY|U?jck>5iPD5G zX}{S9@@6fw_+x!lW6V)UN@Wn5pv?&HhVqwnQhI73Zu=4}afjLbgA#RpzFbA3q2=B% z9j0$(!_PKyVCl2c5ECxv5oU~jIKNgLlN1^k9qyAo zADrr*>>6J4T$ZE{8qN~Of`o=st+T-RJ>dlk!;ui`;zb>IHEgBGq4*)e!ZWJC&l@)G z3kJe137@~$d@BC-g-*gY4mB8RM2W<3zPIsnRAmMPZ5kpiZW4M({Uw6a(~}Tt0G-e_!AgsJw!)yuiWTKJTw_Jzi*2)W@?xa ziK}y=C+~|cze-re;|#J6IBlA}dizeZ;NZulOf6IRc22FGW;Nr#lh2mHlIdj~6iRU4 zEhz|8#+o^9+}&8%7I$!Ya~%Sp(_wFcQ5nRN$@A(Th*eBqkxWj0U3Sb`U_sUEIXUfz zO6#F)PzZo|l;tIUYUTP3aRU^PX41iJGJ6fv2yKigQ$OK*+GdVyYk^-j=RY>S(#AXo zc@fKJ!}zu?eTi!jOL!I63iF`W@)Q5x@s9I|;6t?jcJ;1ba#vnslyjKEk+kk6=BwsBGJF^DT5q_Kk6{H7hUs8Kx;y z&WagZ-QjrXL@fU=%EF6po4+VtG9i};^LQB@{DU{-{DL{}U2Z*HiQLP-`^!#6=aH>X z=R{dy^MFIapeE_>(y{Ed!sPgz zg$(iYs$ES$O1#B#)o!rIVZl_dVlEUWUb1FWSAp)gk2UGc_U<6x*;36PtA6i6;i6TP zfqWN+W^|aT9^otgS87^W50+Ejo$ZiCEj9mo=1Ed(f95>BM?Y6M&i9sDY%nigEc_)p z{5K0PwN3P>`$?DW!N+Psx&w)ogZm!@(Qivm?!2N+0wk23ewq=Lp#j0wUnH1bhI>NWuMP& z5x@5k@_3_&tZ}9tdjmT>rmWfuuO)qDDcFiu)OUw~5$y9VnBmwVTlbFSKpfiL1Wv=^ zsbhMjw2t?A%lA=h-k%e>KS!ESBMbS%Ia(;_VMP0~34>DP7!dip%D&^X8V>FafIOw* zz5$9lei?CMCQh|}e}5%)b_47mr+@F*VGEjPp3qXfz9{Kf-VwS1T(4Cc)0T&W9bK7P zFB*SexZD5$a`3Qq1fOY{Inycmhmwm}G;{WdkhI1FECj8IbFK_}B6h5G24k|EMi};Q z6D(T5Fp(IGz6YBn@VaJq@_4jko-tV-Dvfc3PW$Xlt8qRTh5T6dh*VM6+jSs)yz5s|&(*uFgA!vV{+}<8XqFw+=P#KFnS{`W~ z#9?UdxOUfHpXIlkXXv3WUIaH@+OQ$}-3`in5F!D_(hFbLyD&-xK{~b7HUUxkdx*@; zx)n3_bZb6XI&W7CkI0}!h-G$#EAd!@o8r!8C{IM{nOF+NhRr%xmTrD2HSS?QywaksMqsiY)`V`kfy<) zSJUjFjsqWT>;3ZbA*}52?>WaCVCNUbO!Z{@H_@(ZbEi#{GF3DB)S{!MJ0Eqe?FZwZ z#2gl}1>9Kzp&O$a`^p{_o$RRE#hrmSUzAOACEyPKkiMG z_Q%Vfl$Mi-jl3wZs+`*u`Wt)`Vj8KPaq`NdvFSIu1yp|nL7sC?tIyyQ7Y1#XLmPaj z!%P0feJg{@9Ait9vNKCBU8i2ttG@h(y6XgXq4^|9=k$ur`IuK#8yqtq?VmPUs#G?9 zG=w!;8sxk`y==7oTF1BOyqLYeX#J?Y@5)3lahv~vnWv&xn>TG^mk;YxMkZG2`J`uj z16PBw%-Y5nJ+%HG63xS*jA9-l#1G9=OF)8HQYebB6QPc!|4;<llGHg`tcm88nZ{*Lw(^tl6>pzb!` zB@`!)h84MqO>1pbH!TePxO|CGX-l<&T&#Ufmw8k%SWSOM+}>8y2F19K@a!bcO_iDQ(1FKYOBS8j#n(6Owu*ze*x?UMDc>(^CTW6rxX;g#3Z^|`yF+EL zHqCl^%Od&82Nr{wqR-*AmUU@geuvoG<_5^hXnZ^7*!iq}>T!>oZ^URyvAz1h979l5 zgo(SS4A!KF3eM#k-X}h|W1*rzp+D@CYZ=p9dbg^{^m_R<6V8)LC;9D}UuM_YIlHXA zZ`#YRJ|)j2m|iXx(0cc;$43^4If`609=sEfIRRKNjZZ?C(idAbpPDtp-h-PA5}Jz( z&2}T(%OZt&t+av&V~Q@O+4aVC%a{0HXS|*nW5V;fJ`bIZ@O6s0+u0&XmDc_B;wN{+ zTgwL~$Rv<%q?rX#+O{oDa)G}EH&jEYnr@Ut1Qz?;P%4$ixZa}LfQX!g5%n|vB} zC(K=ZjojE1W`Vcuzk8j1Qn%!7lJXX%J5*wUc+B$3SkI*0DPt4#PdSgi0i0P*i3BBk zx>Obucw7S21bifmOU0A>x0bivKzd040PI<{8B7`r9*zEaIHXc-7vyx>Jmyo`(=XF zI8hLO`Kw=@g4c372>+VNnxu|?s>n$1a>%M%mv!t_CXpb_;V^Z_fHgJbE_JS6TZwKG zT->u$L%~sbPO!tOih;S+HXy7D@Km-k7 zO@|E+|4%1xZ|zePD~ z4uC^M06KJPd{TRRX_h_ynMvR3Ls$s=BVgfXDNJG`pnCUcIV_TGj3c zE_$Jp@&kuN|LDY#Uc{qRZ7F@ekRq6J!KmUB5lRm%g|JuZ>#H9ha$j&|rdG#lSE!CQ21Mr6qcIDyfA7IG z^~t;^#8YQ;!i1#q(fD~+bYKiW1Q2$E>krk99{XKuNeymY;Pa~|YY3rkggSZH%Dm{? zx6USQJ)*ftAKU>-&MR19GHJQ<0KG2gzMv{zaqJetB-#rJxDGNsdMCo|{F4wX7Bc4( zyUZS2dqH~xu#z)df%J8rahzOc2dDfs#?c_-E;eII<;i6n&*|jmLwM5+fk3GfGKE!0 zXJ3*tf<)A~b?K1)h;?r(b@(EBR@zyB6#k88+c%pfdhNDYmw|*b=j3eo_gA7Wiv-@Z zO85@Nk;DzalC$x2PxayXlmUBx^4s*IPMxY@;ZHO;I6p-3h?Mi6NTykBCXn={R0>== zwp-0S{B!xmwOettTS?kJhh<8;%7VN5ch}Ne!yC@f=!^j$zo$ul6!dSC`KZZApE=gH z4uhW7XqJ*yu$E`pAt0X~lcj1yr8-mH2g7-wS1wb4_D%!{C=pEPoqr<`^6F~PXa`wB{ zYe|d3(^Mzh?fZMOBc0X94;dzIFDq`9HB{|8j>?AA`uX@84c(0Ihg;`%WvYKpanC4Z#kF*|7c14(;1$J_ z5OCG33;jt52H8SUA>sP|#@^+#Gli%Ck&sm5)t5z^W9wxR5>2=QGzTJ0!~p7a7*R76 zk~W-qd|JUleR!Rb7K00Iblimabf)}bd~~Nb$RMr`JcXula`jpD!F{r1y<`fErWZ=G zuvzM7N8r&RU9p;nfn97YBL&uugvs4g6&taX7U%|SKhMDQ4j)_+fl5M3cqXrW;~{-q zvt?*lR{8_Aw<)}g%^5U9hLumBwkQ%156IjU;J)>8P_SydpeLAJbO&~Zw~fX6|}vB}=@ zHM#Z_pH4V|=UHmG z-gGl=x$42!398v`Vc$=OcPHM7amm~OZ%Zv1Dfn>Rq;$PT^U-LX=-W53`g+y(ZQklj zb_(=T9}h|woPl*-cj*B*)rX*Cp(V9PXbo{+uIamW3}yOwlnCL#8yARo zFx>ws+$0v>Y_hpBX-ZQuROX|3`C*~<>;As$=q8r%sbFKDaE7h+D(TST>>bSA5XuPs z2Nt|zq+is?I!J$i?2*kYn!EV@BXR-s(6{Q;$*}Yd@ W(TQs9@oQxxbvJ;~4X|-D|9=3CemgV( literal 0 HcmV?d00001 diff --git a/Source/DataSources/ModelGraphics.js b/Source/DataSources/ModelGraphics.js index fb1e83c9b2ca..d1ec883a1f48 100644 --- a/Source/DataSources/ModelGraphics.js +++ b/Source/DataSources/ModelGraphics.js @@ -26,12 +26,8 @@ define([ return new PropertyBag(value, createNodeTransformationProperty); } - function createArticulationStageProperty(value) { - return createPropertyDescriptor('value'); - } - function createArticulationStagePropertyBag(value) { - return new PropertyBag(value, createArticulationStageProperty); + return new PropertyBag(value); } /** @@ -388,7 +384,7 @@ define([ if (defined(targetArticulations)) { targetArticulations.merge(sourceArticulations); } else { - this.articulations = new PropertyBag(sourceArticulations, createArticulationStageProperty); + this.articulations = new PropertyBag(sourceArticulations); } } }; diff --git a/Specs/DataSources/ModelGraphicsSpec.js b/Specs/DataSources/ModelGraphicsSpec.js index 9d521fc14600..9e194bb108a7 100644 --- a/Specs/DataSources/ModelGraphicsSpec.js +++ b/Specs/DataSources/ModelGraphicsSpec.js @@ -57,6 +57,9 @@ defineSuite([ rotation : new Quaternion(0.5, 0.5, 0.5, 0.5), scale : Cartesian3.UNIT_X } + }, + articulations : { + 'articulation1 stage1' : 45 } }; @@ -82,6 +85,7 @@ defineSuite([ expect(model.clampAnimations).toBeInstanceOf(ConstantProperty); expect(model.nodeTransformations).toBeInstanceOf(PropertyBag); + expect(model.articulations).toBeInstanceOf(PropertyBag); expect(model.uri.getValue()).toEqual(options.uri); expect(model.scale.getValue()).toEqual(options.scale); @@ -110,6 +114,14 @@ defineSuite([ actualNodeTransformations = JSON.parse(JSON.stringify(actualNodeTransformations)); expectedNodeTransformations = JSON.parse(JSON.stringify(expectedNodeTransformations)); expect(actualNodeTransformations).toEqual(expectedNodeTransformations); + + var actualArticulations = model.articulations.getValue(new JulianDate()); + var expectedArticulations = options.articulations; + + // by default toEqual requires constructors to match. for the purposes of this test, we only care about the structure. + actualArticulations = JSON.parse(JSON.stringify(actualArticulations)); + expectedArticulations = JSON.parse(JSON.stringify(expectedArticulations)); + expect(actualArticulations).toEqual(expectedArticulations); }); it('merge assigns unassigned properties', function() { @@ -143,6 +155,10 @@ defineSuite([ scale : Cartesian3.UNIT_Z }) }; + source.articulations = { + 'a1 s1' : 10, + 'a2 s2' : 20 + }; var target = new ModelGraphics(); target.merge(source); @@ -167,6 +183,7 @@ defineSuite([ expect(target.runAnimations).toBe(source.runAnimations); expect(target.clampAnimations).toBe(source.clampAnimations); expect(target.nodeTransformations).toEqual(source.nodeTransformations); + expect(target.articulations).toEqual(source.articulations); }); it('merge does not assign assigned properties', function() { @@ -193,6 +210,10 @@ defineSuite([ source.nodeTransformations = { transform : new NodeTransformationProperty() }; + source.articulations = { + 'a1 s1' : 10, + 'a2 s2' : 20 + }; var uri = new ConstantProperty(''); var show = new ConstantProperty(true); @@ -216,6 +237,10 @@ defineSuite([ var nodeTransformations = new PropertyBag({ transform : new NodeTransformationProperty() }); + var articulations = new PropertyBag({ + 'a1 s1' : 10, + 'a2 s2' : 20 + }); var target = new ModelGraphics(); target.uri = uri; @@ -238,6 +263,7 @@ defineSuite([ target.runAnimations = runAnimations; target.clampAnimations = clampAnimations; target.nodeTransformations = nodeTransformations; + target.articulations = articulations; target.merge(source); @@ -261,6 +287,7 @@ defineSuite([ expect(target.runAnimations).toBe(runAnimations); expect(target.clampAnimations).toBe(clampAnimations); expect(target.nodeTransformations).toBe(nodeTransformations); + expect(target.articulations).toBe(articulations); }); it('clone works', function() { @@ -288,6 +315,10 @@ defineSuite([ node1 : new NodeTransformationProperty(), node2 : new NodeTransformationProperty() }; + source.articulations = { + 'a1 s1' : 10, + 'a2 s2' : 20 + }; var result = source.clone(); expect(result.uri).toBe(source.uri); @@ -310,6 +341,7 @@ defineSuite([ expect(result.runAnimations).toBe(source.runAnimations); expect(result.clampAnimations).toBe(source.clampAnimations); expect(result.nodeTransformations).toEqual(source.nodeTransformations); + expect(result.articulations).toEqual(source.articulations); }); it('merge throws if source undefined', function() { diff --git a/Specs/DataSources/ModelVisualizerSpec.js b/Specs/DataSources/ModelVisualizerSpec.js index 34034c24f0d6..e3cf35d5a183 100644 --- a/Specs/DataSources/ModelVisualizerSpec.js +++ b/Specs/DataSources/ModelVisualizerSpec.js @@ -7,6 +7,7 @@ defineSuite([ 'Core/defined', 'Core/DistanceDisplayCondition', 'Core/JulianDate', + 'Core/Math', 'Core/Matrix4', 'Core/Quaternion', 'Core/Resource', @@ -31,6 +32,7 @@ defineSuite([ defined, DistanceDisplayCondition, JulianDate, + CesiumMath, Matrix4, Quaternion, Resource, @@ -49,6 +51,7 @@ defineSuite([ 'use strict'; var boxUrl = './Data/Models/Box/CesiumBoxTest.gltf'; + var boxArticulationsUrl = './Data/Models/Box-Articulations/Box-Articulations.gltf'; var scene; var visualizer; @@ -192,6 +195,59 @@ defineSuite([ }); }); + it('can apply model articulations', function() { + var time = JulianDate.now(); + var entityCollection = new EntityCollection(); + visualizer = new ModelVisualizer(scene, entityCollection); + + var model = new ModelGraphics(); + model.uri = new ConstantProperty(boxArticulationsUrl); + + var articulations = { + 'SampleArticulation MoveX' : 1.0, + 'SampleArticulation MoveY' : 2.0, + 'SampleArticulation MoveZ' : 3.0, + 'SampleArticulation Yaw' : 4.0, + 'SampleArticulation Pitch' : 5.0, + 'SampleArticulation Roll' : 6.0, + 'SampleArticulation Size' : 0.9, + 'SampleArticulation SizeX' : 0.8, + 'SampleArticulation SizeY' : 0.7, + 'SampleArticulation SizeZ' : 0.6 + }; + model.articulations = articulations; + + var testObject = entityCollection.getOrCreateEntity('test'); + testObject.position = new ConstantPositionProperty(Cartesian3.fromDegrees(1, 2, 3)); + testObject.model = model; + + visualizer.update(time); + + expect(scene.primitives.length).toEqual(1); + + var primitive = scene.primitives.get(0); + + // wait till the model is loaded before we can check articulations + return pollToPromise(function() { + scene.render(); + return primitive.ready; + }).then(function() { + visualizer.update(time); + + var node = primitive.getNode('Root'); + expect(node.useMatrix).toBe(true); + + var expected = [ + 0.7147690483240505, -0.04340611926232735, -0.0749741046529782, 0, + -0.06188330295778636, 0.05906797312763484, -0.6241645867602773, 0, + 0.03752515582279579, 0.5366347296529127, 0.04706410108373541, 0, + 1, 3, -2, 1 + ]; + + expect(node.matrix).toEqualEpsilon(expected, CesiumMath.EPSILON14); + }); + }); + it('A ModelGraphics with a Resource causes a primitive to be created.', function() { var time = JulianDate.now(); var entityCollection = new EntityCollection(); From 05874e715563db13eba407f0b7c02cbae4d7b14f Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 5 Jun 2019 12:14:17 -0400 Subject: [PATCH 3/4] CHANGES.md --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 251e2d03e0b1..b25b2d383d1b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ Change Log ========== +### 1.59 - 2019-07-01 + +##### Additions :tada: +* Added support for the [AGI_articulations](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations) vendor extension of glTF 2.0 to the Entity API and CZML. [#7907](https://github.com/AnalyticalGraphicsInc/cesium/pull/7907) + ### 1.58.1 - 2018-06-03 _This is an npm-only release to fix a publishing issue__ From 6e8789ba1a4cd614964714181651decbeb3a79a9 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 11 Jun 2019 15:50:07 -0400 Subject: [PATCH 4/4] Add articulation tests to CzmlDataSourceSpec.js --- .../Box-Articulations/Box-Articulations.gltf | 1 + Specs/DataSources/CzmlDataSourceSpec.js | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Specs/Data/Models/Box-Articulations/Box-Articulations.gltf b/Specs/Data/Models/Box-Articulations/Box-Articulations.gltf index d5f906c215d0..b727cf9e9395 100644 --- a/Specs/Data/Models/Box-Articulations/Box-Articulations.gltf +++ b/Specs/Data/Models/Box-Articulations/Box-Articulations.gltf @@ -42,6 +42,7 @@ } }, { + "name": "Mesh", "mesh": 0 } ], diff --git a/Specs/DataSources/CzmlDataSourceSpec.js b/Specs/DataSources/CzmlDataSourceSpec.js index eff0e7abc1d3..5b11f38ea90a 100644 --- a/Specs/DataSources/CzmlDataSourceSpec.js +++ b/Specs/DataSources/CzmlDataSourceSpec.js @@ -3196,7 +3196,7 @@ defineSuite([ scale : 3.0, minimumPixelSize : 5.0, maximumScale : 4.0, - gltf : './Data/Models/Box/CesiumBoxTest.gltf', + gltf : './Data/Models/Box-Articulations/Box-Articulations.gltf', incrementallyLoadTextures : true, shadows : 'ENABLED', heightReference : 'CLAMP_TO_GROUND', @@ -3221,6 +3221,11 @@ defineSuite([ unitQuaternion : [0.0, 0.707, 0.0, 0.707] } } + }, + articulations : { + 'SampleArticulation Yaw' : 30, + 'SampleArticulation Pitch' : 45, + 'SampleArticulation Roll' : 60 } } }; @@ -3234,7 +3239,7 @@ defineSuite([ expect(entity.model.scale.getValue(Iso8601.MINIMUM_VALUE)).toEqual(3.0); expect(entity.model.minimumPixelSize.getValue(Iso8601.MINIMUM_VALUE)).toEqual(5.0); expect(entity.model.maximumScale.getValue(Iso8601.MINIMUM_VALUE)).toEqual(4.0); - expect(entity.model.uri.getValue(Iso8601.MINIMUM_VALUE).url).toEqual('./Data/Models/Box/CesiumBoxTest.gltf'); + expect(entity.model.uri.getValue(Iso8601.MINIMUM_VALUE).url).toEqual('./Data/Models/Box-Articulations/Box-Articulations.gltf'); expect(entity.model.incrementallyLoadTextures.getValue(Iso8601.MINIMUM_VALUE)).toEqual(true); expect(entity.model.shadows.getValue(Iso8601.MINIMUM_VALUE)).toEqual(ShadowMode.ENABLED); expect(entity.model.heightReference.getValue(Iso8601.MINIMUM_VALUE)).toEqual(HeightReference.CLAMP_TO_GROUND); @@ -3256,6 +3261,12 @@ defineSuite([ expect(entity.model.nodeTransformations.Mesh.scale.getValue(Iso8601.MINIMUM_VALUE)).toEqual(new Cartesian3(1.0, 2.0, 3.0)); expect(entity.model.nodeTransformations.Mesh.translation.getValue(Iso8601.MINIMUM_VALUE)).toEqual(new Cartesian3(4.0, 5.0, 6.0)); expect(entity.model.nodeTransformations.Mesh.rotation.getValue(Iso8601.MINIMUM_VALUE)).toEqual(expectedRotation); + + var articulations = entity.model.articulations.getValue(Iso8601.MINIMUM_VALUE); + expect(articulations).toBeDefined(); + expect(articulations['SampleArticulation Yaw']).toEqual(30); + expect(articulations['SampleArticulation Pitch']).toEqual(45); + expect(articulations['SampleArticulation Roll']).toEqual(60); }); }); @@ -3266,7 +3277,7 @@ defineSuite([ show : true, scale : 3.0, minimumPixelSize : 5.0, - gltf : './Data/Models/Box/CesiumBoxTest.gltf', + gltf : './Data/Models/Box-Articulations/Box-Articulations.gltf', incrementallyLoadTextures : true, shadows : 'ENABLED', heightReference: 'CLAMP_TO_GROUND', @@ -3291,6 +3302,11 @@ defineSuite([ unitQuaternion : [0.0, 0.707, 0.0, 0.707] } } + }, + articulations : { + 'SampleArticulation Yaw' : 30, + 'SampleArticulation Pitch' : 45, + 'SampleArticulation Roll' : 60 } } }; @@ -3308,7 +3324,7 @@ defineSuite([ expect(entity.model.show.getValue(validTime)).toEqual(true); expect(entity.model.scale.getValue(validTime)).toEqual(3.0); expect(entity.model.minimumPixelSize.getValue(validTime)).toEqual(5.0); - expect(entity.model.uri.getValue(validTime).url).toEqual('./Data/Models/Box/CesiumBoxTest.gltf'); + expect(entity.model.uri.getValue(validTime).url).toEqual('./Data/Models/Box-Articulations/Box-Articulations.gltf'); expect(entity.model.incrementallyLoadTextures.getValue(validTime)).toEqual(true); expect(entity.model.shadows.getValue(validTime)).toEqual(ShadowMode.ENABLED); expect(entity.model.heightReference.getValue(validTime)).toEqual(HeightReference.CLAMP_TO_GROUND); @@ -3331,6 +3347,12 @@ defineSuite([ expect(entity.model.nodeTransformations.Mesh.translation.getValue(validTime)).toEqual(new Cartesian3(4.0, 5.0, 6.0)); expect(entity.model.nodeTransformations.Mesh.rotation.getValue(validTime)).toEqual(expectedRotation); + var articulations = entity.model.articulations.getValue(validTime); + expect(articulations).toBeDefined(); + expect(articulations['SampleArticulation Yaw']).toEqual(30); + expect(articulations['SampleArticulation Pitch']).toEqual(45); + expect(articulations['SampleArticulation Roll']).toEqual(60); + expect(entity.model.show.getValue(invalidTime)).toBeUndefined(); expect(entity.model.scale.getValue(invalidTime)).toBeUndefined(); expect(entity.model.minimumPixelSize.getValue(invalidTime)).toBeUndefined(); @@ -3348,6 +3370,12 @@ defineSuite([ expect(entity.model.nodeTransformations.Mesh.scale.getValue(invalidTime)).toBeUndefined(); expect(entity.model.nodeTransformations.Mesh.translation.getValue(invalidTime)).toBeUndefined(); expect(entity.model.nodeTransformations.Mesh.rotation.getValue(invalidTime)).toBeUndefined(); + + var invalidArticulations = entity.model.articulations.getValue(invalidTime); + expect(invalidArticulations).toBeDefined(); + expect(invalidArticulations['SampleArticulation Yaw']).toBeUndefined(); + expect(invalidArticulations['SampleArticulation Pitch']).toBeUndefined(); + expect(invalidArticulations['SampleArticulation Roll']).toBeUndefined(); }); });