From 567ddcf55bdcc071ac9bdeb76e5f6694fcd1bc9e Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Mon, 22 Aug 2016 11:48:45 -0400 Subject: [PATCH 01/34] Added feature table spec --- TileFormats/FeatureTable/README.md | 64 ++++++++++++++++++ .../figures/feature-table-binary-index.png | Bin 0 -> 7810 bytes .../figures/feature-table-binary-index.pptx | Bin 0 -> 36501 bytes .../figures/feature-table-layout.png | Bin 0 -> 4033 bytes .../figures/feature-table-layout.pptx | Bin 0 -> 35866 bytes 5 files changed, 64 insertions(+) create mode 100644 TileFormats/FeatureTable/README.md create mode 100644 TileFormats/FeatureTable/figures/feature-table-binary-index.png create mode 100644 TileFormats/FeatureTable/figures/feature-table-binary-index.pptx create mode 100644 TileFormats/FeatureTable/figures/feature-table-layout.png create mode 100644 TileFormats/FeatureTable/figures/feature-table-layout.pptx diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md new file mode 100644 index 000000000..0e433ff05 --- /dev/null +++ b/TileFormats/FeatureTable/README.md @@ -0,0 +1,64 @@ +# Feature Table + +## Contributors + +* Sean Lilley, [@lilleyse](https://twitter.com/lilleyse) +* Patrick Cozzi, [@pjcozzi](https://twitter.com/pjcozzi) +* Rob Taglang, [@lasalvavida](https://github.com/lasalvavida) + +## Overview + +The _Feature Table_ is used by the [Instanced 3D Model]('../Instanced3DModel') and [Point Cloud]('../Points) tile formats to define special behavior for each feature in the tile. + +For Instanced 3D Models, each instance is a feature, and for Point Clouds, each point is a feature. The features are defined through the use of Feature Table semantics which can be found in the tile format specification. + +## Layout + +The Feature Table is composed of two parts: a JSON header and a binary body. The JSON keys are tile format semantics, and the values can either be defined directly in the JSON, or refer to locations in the binary. +The binary body is a tightly packed binary buffer containing data used by the header. It is more efficient to store long arrays of data in the binary. + +**Figure 1**: Feature Table layout + +![feature table layout](figures/feature-table-layout.png) + +Code for reading the Feature Table can be found in [Cesium3DTileFeatureTableResources](https://github.com/AnalyticalGraphicsInc/cesium/blob/3d-tiles/Source/Scene/Cesium3DTileFeatureTableResources.js) in the Cesium implementation of 3D tiles. + +## JSON Header + +Feature table values can be defined in the JSON header in three different ways. + +1. A single JSON value. (e.g. `INSTANCES_LENGTH` : `4`) + * This is common for global semantics like `INSTANCES_LENGTH` and `POINTS_LENGTH`. +2. A JSON array of values. (e.g. `POSITION` : `[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]`) + * `POSITION` refers to a `float32[3]` data type. This example shows three features: `POSITION(0)`=`[1.0, 0.0, 0.0]`, `POSITION(1)`=`[0.0, 1.0, 0.0]`, `POSITION(2)`=`[0.0, 0.0, 1.0]`. + * Feature values are always stored as a single, flat array, not an array of arrays. +3. A reference to the binary. (e.g. `SCALE` : { `byteOffset` : `24` } ) + * `byteOffset` is always relative to the start of the binary body. + +## Binary Body + +When the JSON header includes a reference to the binary, the provided `byteOffset` is used to index into the data. + +**Figure 2**: Indexing into the Feature Table binary + +![feature table binary index](figures/feature-table-binary-index.png) + +The value can be retrieved using knowledge of the number of features: `featuresLength`, the desired feature id `featureId`, and the data type for the feature semantic. + +For example, using the `POSITION` semantic, which has a `float32[3]` data type: + +```javascript +var byteOffset = featureTableJSON.POSTION.byteOffset; + +var positionArray = new Float32Array(featureTableBinary.buffer, byteOffset, featuresLength * 3); // There are three components for each POSITION feature. +var position = positionArray.subarray(featureId * 3, featureId * 3 + 3); // Using subarray creates a view into the array data, and not a new array, which is better for performance. +``` + +## Implementation Notes + +This may vary between implementations, but in javascript, a `TypedArray` cannot be created on data unless it is byte-aligned. +This means that a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. +The data types used in 3D Tiles have a maximum length of four bytes, so padding to a multiple of four will work for all cases, since smaller types with lengths of one and two will also be byte-aligned. + +If the string generated from the JSON header has a length that is not a multiple of four, it can be padded with space characters in order to ensure that the binary body is byte-aligned. +The binary body should also be padded to a multiple of four when there is data following the Feature Table. \ No newline at end of file diff --git a/TileFormats/FeatureTable/figures/feature-table-binary-index.png b/TileFormats/FeatureTable/figures/feature-table-binary-index.png new file mode 100644 index 0000000000000000000000000000000000000000..fbebdb7881e6c2d4bd078660cda9c45b7d2ab8ba GIT binary patch literal 7810 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

khu4EF=9BT4!2 z>j^7Zm}orW3t&WNrLB6Vfh&w1nDqf$>R7M2K4+03L8>)0dIt++B}t6k8tbg_DD+8k zW5B>uq0Eop%|OhXFmD><5^u5yJ#Qktvwc zf<=n5j$Wy#b&NzrdA2#z5g|9^B<|@4!uBvE5=y1+FK3Lt)?0~(gM`(pY~Tlr$TQ@c z4;Ms|)|}_i_@iI1rQV2tS+@lpdLvI=RCuIX9F5o+?2wLnjezUX%=(^uNR~_AmlAn} zm!wXsi?v{Aim0 zUjY?TZjj&}qiF_F2@l+dze1;;EZhnT2_&3`k7)5P=izir$mt@SprP(5y1mL#EM%Qt zU*_BF=}D#g|vDBweSZ^>( z*t69XKNJI2d1Bf9l^=l0t0?e^ntWpt(cd9dZV3I?lQJfD*#Y?bIsqEPd9L zCxwG=zxReY^=?bx6_c&vBhd+~`L(@y8(9RanG_0bvFXncFt22d$(%*~ zBbr03T}xPMJ*_HI_g+$Vl=X2emRFES5<56M34 zc5Z@0Z~v)NZ+(OY&L@z+a6krQj9;6nj4{<&>!#FuUjH!m@yiaw*^&rD&d*G zjVOkODZjEf&EW|#4vB-a^K)u}3oABhlkdYUG@QcE#BU#t7+;w~DGw$U={>%Rz3|(uFUy zK`)>2eqKE^V1H7-iadHa0>m%x{g&pAk76ar>Kf1;-x?hLz-8kCPd|mCYJ?tW9yv^@ z8_Bs;*kuem^@1Aly&le}5liwAt=hUlMxZ%$Kp^5wn?eTv2m4rUHjIvOrU2XSxqxLf z{n7~MQ)DJEBgIgwsLnRs3}T)?qMTTvFV@Z+d{^ECjLgJCEb(N0#MwE>W*kp-ER5?o z6_XBb%mcXHTOyG3m1L1G92hL|PI!xQ1v2ZP#C^2jQlopGSG8<_9yhPZt2Z{e`<;*- zQ;nb3q0vw)1xn(fj#s5;$_cU?k^KyC$Quuft}`$t4>z>cYyJ^eFnrt2CxFTQXvj*e+e6q>A@c|+dr+N??=3^ zNq!fT5Mr7A?-a`K(|*_ZzGDFZ_>heL8^!PYu>1PXcQ_M>joJ^`eYNNNg!^*1cLaCT z-!k{p)zD9S>idZMmgskgmk`bA|0*n8|31yXNYVWjn&FNM`DBRychGn8ZhuAoJ<;X_e~0{sbj?4# zdj56mu!Go4{*ChZaV`IcWEY~pL%x&t`m5sjy`a4y{&N0(n(ySY{ysSXvV-B@lkX&` z?vw8eIo-97(1*W}|8&Ut%T@S`Th4!4&i6Hp?#SDad&>Wo=3ji0{n0G$r}VxC${p{L z^55`&Dx%!y-B(_?P&?mLvY!_hqdYuwL)WOy~CNv z{Wb3AV00h%ulwC+O8*4+OMmc3W#8=iUmd{Jso!A8>zh=?-A*_6Oj%rKbDQe_z|U yiwpqJdHhlaf39@gkN*2T=daO+ync!Pk2_FVX;{d#8UUb%{Jexrep&r~eEVM&;jZ}r literal 0 HcmV?d00001 diff --git a/TileFormats/FeatureTable/figures/feature-table-layout.png b/TileFormats/FeatureTable/figures/feature-table-layout.png new file mode 100644 index 0000000000000000000000000000000000000000..e887230d273b7ed188b4c0daf852a38d7e1c72d4 GIT binary patch literal 4033 zcmc&%doy+HZ$Zh1BG)?5Pm2nviJ7tG5TjS0c$tB}5 z8jKp3QHpYnahn-q6uD2tFwB^7?B6-(kF(bA-*e6%@B2Q_yWaJz&%2&yeLm}x?C4-6 zv;X*h003lctj(PO02H+wXYG^N-G@rBle-7#zLS+1P}BFz{BE)baoPSd0KCFTZ~N`t z&87aZzJ4D7l#>2gAn)QMe*%E4n~nKpmna_&3mT*Zlk5Bu=Irr|Fce|$VzyVe;?N=Y z$vtXCAt^smZY1t$cznp)QSGGlSx09B#Zw+7XWSmRCnT72)YT6k3{P}VklrWfUew_t z6yLW1Sd`k!8ECrNr84rip3WNMz506uSY9EbhN?2p@5LMGM9z5Q4V0<5`lJF|02p|q z9={hbPXc6t^k3V=FO}?D0S>8&83X1i|0h?WmjoLeJ4;K;k&KFpie}4Z2b`hn>t*%X zK3wB`F>D?3or#&UXAZ$d1M?`F_u|dp6&_*CqS2K&n*nFEPIa<{&diPluFT<32;VmGX zxd^sSg@6Ls!6vQ(#5=tVI9FcFr{^y>`gsz(DFE_WW*2SgWZ8)qfTGi-n59>p9`u%l zhnGJhcJ_xHt8ENr7xqf;nAKLn=+)YuFZ_bL5fr}k7256Fw@F(bK2sRuRck#+5WXXF zX)L{WG07ni-n9V@U*gKAG+ag^oz(fMrDqahF%1~9sCY`6D-J>@I{o2%W53pKXPR72 zuv%gw!R;B?u?r^vaIZnro@>;jW;} z3jTp%?fb*m^b{rD5rW(}VCHT86!P!kS@pMFNdVvcNF*#dJVxS^TZu%&=C5-5hbbNB1?#psgrh)bxl099tG544>D}CXU zqgeC73^75j4ua&_%<+Q)j;i~YD5=pqA|YKSYX63-K5LkDNxWG!J`l`SN7X#mj%>Q( zm=`+Q88@P6H@QHFqeSLj~cPK9#{*eLF z{S)f6{y|55|M!#t`(%IdH|8&JqgE$nl?*dXDm}CBF5Q?PmR;)t6PriA4a*OlRuI;? z`hG61^4_sKn(rGaE>vCQG_ni5sZMR9mj@Lbb!)`m?>=7jD2K@nye1(}mv@cHH3j)# zgZP%}Dy=so{+@0a^|8|nohZm;hBRrsS*&S#Mk|M^@>r3+r2DgdRgY%`;++?#p6G-m zo!4ZNgS&5L-OOoKcT&N)^bA6-uL~s!v)Sv5FE4l}s_Zdgdcghsp1bPSAOgz~9o>a- zN~V(eVI3wN3Ov4vre1JJh-NAj)k-aU^rPF>`2aV1#ZVDt+8)}YaHuWmi@)2?#O)~O z1K*?}?=CGLo�sOHaUG5h}t0U$WtoSM|~Tm_#%VN^z_3x-PQAxU7zs@6Jf~`0OfPZGW>L+RrLVgQQv+6d+Xm* za0OC!#VYNWPa<&ew6!Ra$yKAnC^aae|FX5+ft6p@H*2`=u-fRH&swk39ar- ziL2~wjT3m#bd4`u$h6lNwJ;m*!c37!N<(+!@TlUcIB;vQ^CNTv>Z-&rv5Nt#H)$vx zMW<@c`9Bj5^{TJie~aOL@9pG6BiE}t^^5xZ`ZS?XD54!T{Q9K{pSDMBuhIA*YXeBv2<@BPjob>79{#zHJ{Rrd^N~;}R8g@k483NtDr|w4`)E!oEqposj=QS5# zD3v8)bs%-FQoB7kW4$stIXU)@YtxjxlTjRdP4El3cZMET_m$Vc61gh*vLl#?N?Hh{ znKb2U7hSZRAL4dBGvJXzP|nG~!v5lVWxRp4tAuYxz{A|yvVi!YrsIw9cN+nyYh_<` zG>G1U3pK$^Jr{l9H?KMf15DN2;i$1O%!pW#!vRcn2OU<$Sf6WeNv2|Eaou z7xMS|I1lQ2zHZTNj#S+QX(Kgj6#ckgREiZ!V~D~DM(svBnP_MzL2hyI3c&wd<{=TK z^am2Pc&c?w@j2wfP;?LqRn}j|BjoG9#%|=2*2#CNS+oel!=hgU4Ul)(nqWBR$K{&G z!UuEmSK1MM1)n-hklQQrgN)UccG+3y5$JH(`CX6}$yWxXNcp_rpM~l{ky=7q;pN~n z3%(YeML35EJc<1C-ksds+#q-2@#Q4O=c5h6oRWzWQV$eof$M#c+)=rZ@&MDfX?nj_ zr+MKFSgotR%cY?o?C}>_-_X#5j@Bq&8r{`7cQXr+ImamMtGOvTD~+8fYnmf-mqj@? zAOEAh$_`l{Tn)3pjYo!S5SJW4CrX4DQQSMvL<$Aa>k!w@QY`PookH#)aVV*nca^}H z&0MdHpvtqjmA~T1{WN|YTN~clwkDu|5+!F<7)IZT)~FN_)&uaxnT2}=1e$A^l6vK0 zYFV@ea=XC*V#2AXS9;&Ya<9f;SBl5kx6dmw^U4>;Q1<;Z|D~njG*bw)UK7&a=o`F3 ztGW|^^_swCTaUm{MWbJb26R$`4CJVBuObMWi?wKq5>&%8T;q*W^$SG{K~;dyaF`T3 zoBJ@cg+JvyOkBz%_bNtE_s)FfRjz=;($~6@(EQE-0UOsz>L~(kVhT z(zg4;UMSXlmm|;Z<99Fq=v09oR52}p&U<0`w}Q0>fJ;=m)j4GJkBOM&UQOEjCkK=p zEaS~sf4>z6MwE8NoN~^WF4WYmxA8YGhsc?EO{aKXz=7B1iIe~ryi3AzIjHL1TzIx;6mRMZrnPpv zan`rD(`|fbD*hIc8hbBn%Zf_zcseMfnF$o5ZJRR zMj21Py|OiylI;_xqe5_VUCtb3+m^Ko%Lls+T*H5go*R&+uc|`?Q#Th&ZgM9}N=r-U zG-<$`49K)-(uZ5psz{_7su0Q!wDurJeKKQtc8$OLPVj1`7mU00eYgAYo^P4i@@r>> zTKz3~v7#fMSUNH83>Yt(i(2iG-szo*-oMI_1WvR`cGxN_@jLRfcbq?ng}(HbAa-ku zo~0QGhh@nR8np;o@li97PvjqW>-+||TU2TR2Gr)U`vHr~KKKHIwI=zsSEr&EeD6XI z|JF6websA+Jb2`akv=;r_}=qGcxDSYRT41${rV-9^dZREb}izHigPo3k%`UKs*-RL zPU$w2MQv|+s>@@fdjYE3InzStWjoWA?;2a~h}P9MUu>!tH63%B{!O`i<#Y$ecJ;i? z&{I=GRtEdLSS+X|nt$qPE4JR|`PANTUXQpy65>n4ZuMvlhq#4^(=az?d3AKe_$F&4TJQ=#Pi2wm1UDl#RMr@4`#%^lvqBxnj{dj8gz2N zkM?_fYFT8BA>-b_IK)^gjTt~zX1#|p9J^pcJrWjoku7DzHKLNKaDk9fVU^4XZm_8! zTe!e7ka>re^^}Gr}hT=#n64)@A zRmQUC+X18e5VIz6S%L}?^({8f-b$unPb$2osds?Vqkr`56_`Ig%f-(E4bcV{6`bng zQOmw@IT{7kuE}o_D!fvP$n>cNJK~sy5u?k53q2Zs99b#OocG~E=TpY88$3*jtV7#N zVljYLF@R^p^RLJzqzEoAydVRc_Q>vSo-YH=ACkk?jV{KQAbRU6-96$R1|72}p2o^- z4*S@&{=4P5bxVvc8o3xh*s)bW|wKCd5QO4RO=PP#hxC?{R@cptH! z`1;ObQcGyCBfB{}-38KIMNT^);g+8{{ES6yQ$N7#yFPvS;{y~(;eU9!`IA!)=;xE2 zPbY@`^m2U%6Dvms`aeGZ?cM)}UHZRGy*zGJ3X};k_!4}RXv~{qm6~|Q%2wnkWB3K= z8*>}ZI<7?FOlL>VmZ2e?f5+IE0Kq1yrtO0b>SaPLoL*0DsZh*fkH#jAn+*Ya6CraR z2PcF)419v&td3rJmbnPMuzNLA_}6RAo^IUB{4o#0kQSI&uVW`7u002tj)>yTYKoT9 zQ=FeZ%-KH~2NozLv>m~7`9xj_w7HE~hbf&U>VK5TP&ArgOdFkqqNQXLNOKXOs1gOF zV`ycl{|L$Oyfz=gO03em$1eGhHGg)^nQ~T}^`7Dp&pd1|47Dx7)EpFPL5)6E=5X?s zBU)i`7e2VlGmOX*bGTtiL-;hq-}}rr9g_O)&nFz8ena^A*Urv~!O;p}Y~sl9H~0DT zg6WSP@=r&KnC!6VV?>0!;+t|sY*Me~2}Gndp#m2!isA1j%CJ_{M&@qmwB_HK%PRq5%JT4<)=5 zY=JQ`ZbGUi!(1qdNwGMd=m1=r?zR|tMITqvn{`}q-wbqY0<~i{#&glX**a9OW|z@T zI%~QTHt0q^aA%q4Wst72pf*LE8avV@2rR=Z8 zavV@4u%uwyK;%D3(W9*0Wr&)FEOJOy(VS!VKOWxPQ)|>uFXig!Jd_rohT~8%Q)kph zSV1>`t{BTdZS3bssi#>*b1R`2S53%(YZp(t9suoggo86Cqvxhft~$Z z&>b8EFeFVVpj%#;_8^e3g8B_|>6bo7n*XqAh+7=$6hY3}_;jRX`F^^FP^YCAT*qKf z7{meU@Oe35jZ1ZY##&l@U-Qp+!?LT>qXztP0Vw{LkmtW%Q0z-x$=jdX4>>##5bA%p z)Bj#dXUUtEtBi<4n~<*r5qjb$n^8DB3HcdA*#T4azTYiqLQ80|B<13x-Y97#&77^e#th8wVyLLzpkkv!v+iADPo}1i z*xwsABE9A@=l9EfOXF#YcqmTi5PrMeKTV6kMC~!t{J2%^;+RnatGA9UgDeJ{iECwf z#82ww`b74P!!1kke<@neg~kowp0?a;)i3mEX`r5t3NP&ezPWIG$$>mkLsT}huj*8M3v~r(~DBz&4ujfZ#$T^eeJq z0v74T#!~h?IUU_yOThx-#WY5nx*Rlkey)ZqDx9*ebh~i?tNK-~7Fv}Xc@nDA4snf> z*?oK1IC@uTw|bISi&ZbWFo5x9T0x87XR@dzi6jd`YLw#Wx%u~8q?jOHBDw-65UOX* z@;j7-G5}bVk8m=af?9TjB66_!WocOe4w!2_JLzx9Pi1I-3X6bj@Bz5l zE?w($)pS~K;D5El015RdlYjvM{YC-;Li-Pw_>UdtzxI|h4Nco!c0?bBsSm-U5^p=F z(b}2`VznL7a2w?EI)U#rij6>Fs1h$0(;pi|RL^qk8k~aYN8zGyhfk)QA!7&xi)^CO z#Kr^5!`D^3kVr|lxkh!dxA%YYixJDBvm;Z_s(KyWwlU3 zR2o70Lsb$U>v79^Hh&3)Z}|v>x&da-3Ft3WX%s>~^lJ7gre)H^4I3|$qd`=``$vVo zEFz*0S#xNy-;N}iZsUhewvypjN-LgHJOwY1K>Bx0cU2a~HhgU%JK(Y4_vP$Mo4lN` zYs&z3oh*JB@#oo<>$*LJE{}f3e*U$BOGWqBsYfTf z6A0WyE7L%-BGnwM@$d=sI7PptlqlQ=cD+_eok}$`-bNpGVZ2zQy#1~ol&|&3n9MOk z4V)5`srIGOeVFDp=rDE-c641rl=CXqluULwx1eE+8A6f=QcDV0a==bu&eq5auiQZ^ zZ#X))^=6`E30&?Xfs7?omWhh~nr8ZoFv1qIG02B@!&v%1!l0bw)vU399lGj%({U_0 zjWcLK+=KSb+nYS@>)2398o;Hd!LRAo*9*9x!E|l3meL{5wMGB68TIvXH%3oxzq?m& zF3a5z5L$$`0-`zBT7u~?7wf#?sufkX9?biEs9So18|_V(KAUKw@E zmeyF1e!Oqb2C1k%u1sdfmPX@50;Zo6!2&Vg2M*I~?cPK!cj6&`|JXI1O9(%chcImr zGpLA}`P$7(Ww8vhPJE|NeLcExpT4ydg?3;d2EPzn8%3!z&4N>OPw$+gWuPwsZI<29 zWCxG5#Tz1kga^}C(IO>Si)QuOEA3C$1?{5wwY{;I-HHLmmMp?#!ll=FpqaDC0HZl+ z)5EsI2QVifQy| zQ6{b}I}^E!5g`ln#>Zp4yVJ)dQ4=BC3L1zr$zj&DTMU>c_eJEp`AoPCb3+CN~|`~A00ov&=3o01Vmd$5)o;c%Kk#wFWY<m@7gnNC#9zsh7&Cz_2_@=C<9MD*r==^! zch-VDECatRBq+24nmb9}zlBsf2E+I8lHWXw2kuO3a+8=z1r5w8FA(m#`ZlVJA_JFN z&z-G5d=l#aA6DS_w-r8@2iL#273lsck6>c_7dOK{7Z^DI#jWs9E{6Y+v9 zAKNGMzqm{MbAf^7-&Xis3#|WwRsiJc6QO;Q;}GQkqDNUnM<)}9|3Qy`yY*jjp?_ZX z4+Ru_%R9%Fu`Zg7Cp11`cscNEc3B+0pvEqNg|wKGl=o{`($_DB=0w?%-EsE^yVmsr zUhv*GehbcGu)|95&?h6GiW$hp#AG-f>4bEJu`Mf1%2ZE|m)BC45rcR0-inQrq{OtG z;OYBQe@dAaZoc&R5AO82LHL;v`PFJgwaZ$eBu70)&kW#wS{5!U;D`L)w_Da7{aRUF zS2TQCXDo$R9Fmk%o*)JXW()$O0_!PQiP_WlW%*Us2p|{j#t15HrCM2;<={AzXFdS` z+5pyx=b_m+<&ZAchczVyP8byl4~@|XOl@6rL2O0Ex0wZFOI{1h^x6ue8MOo}4CThx z#G2(>ZJ$pg{ymna6VRmmA*oh>mRpJc3Pt`*{QrjKOn+LoUVS}kg%h(wAF$aoqGxtP z079)Sgjt$cm@)nJaYJ^@U_MlLRl!`fq-x$fo&T4Q3-S?TJuLPE1jWSR?rp~IBs+ti z4R!E>2=&Nm4vNk*Jh5|+w3fh^jHcU?G%g8%PIAh1>X!^U#fj0=OMZWw(}K20uqQ zWKsWWJk##?xl-W`6;2E0P!x(O=%J7fQ4L6z1JML zx~lsXemBTIf>v?fC0b(>Ed@yHVEB|~wyw-$P~>8B?r?A0nsy#!ZCcwX5zlq74TSjA za%>0rS?D$=coArqW9RQ=y z7J6dFz1{hTaRbBF6N{!-XXtKma+HHB?tV^Mf&E_8Yu+$kfwutk3}=>19>aQx*4NTh zHDf6PwYas4F(UU=nx7cKc2bbX8z|X!?&@(9iByM$@V<8gmHCG+wNtfB2YoQoDLJ9}l;vXgZ`dKUukN_x8@u%o=0rSrqyQI|FO_t?u<8{EL0G~?_3 z@f$+BIz8r-b>mrDsoC{fRq|WxrHodsF5mZup~tEZ9fOLM!ZzXFpLS(VuYqcDsUn2& zjFW>GoIi?ec#j{~rdegz0LTOO{1amCPPYSVq5*CS5=IQ|zDetsg}MoobCPU<$B23yV9n_%kUfS6hu*Wk>X8nDQl@ zGpndfG9lUMPEr^3lXX$#@irs%kPSrEjB`0d2foohKire%&KKeFgf?(}C)Q)-O?Nbv zm`H1C3(ld74ZCsK5mnC#kG3bsYMJPY{asKH{F^v7yT|-ciciSPcC8vZJ$o9pHroyGn&JE}+1bju z4>M;&@iOERj=D#l%lhf6_^IX`w!R&?-oc~enazed6M$G}d$e5?o#SR456rY-RXv)< zM^CLt@|sF9++`y1ARc^MkJ)1ABkz+u_05L&@g4H%5!K+La6@y6+AI&SBVVV;`J=WzCHWV| zTGbrogvAu^+8_pB2hAg9ZbC%N}fS5on|i;2zqL=!vkC&0*o}ts>o5PQF=yS zo9aM39jo7y9d`jsI%=_FRJ8{6x2l(0pHm@fd- zq2Gk^hdz2Du~`U^v)i(I2a-v6Giq1|bZ)K|jXtk0lMI`kzdikQ8Y0UYwq%=CTQ74W z^Mj!nL&LoeCUfiWW}nTEi&LShKc{ zVtvS=IZOE7+ae#E7n*YcbA9^PGTh9tBzWMbxUcgm?i2lko&Hap9!oj3S!GB4{(-Q> z1GQo+%5h?pCuul(%}yxm&+QE)mTR4pP^?&QX-he^xPo6KU+YNu%VDP88K-ZUAUqcy zq05m7;aI(j1Ds<)W}$_0Quc9-2~Mq$cyx52<@v(KVxUlwM$0rIJT4}M!RLKn zm95;}MYPGN;LDWag0P1TePU^Fqi&J3+SH4TN_n8IBN1%rmMZ<3%nU+y+g9TtXO8Yv zJL~H8P0Y0s|D-ZI(s8LlfklGEU5*FHi4Q$dM2COV_ zbd-xz`>7ZPp4_`X6=y)WO=$Q>nn#F`c7<5{ho zhp5SPC0VXl6Ts;^$#SoanfS?H1=nzK5SeSgHPl2~C&aR605;R;Z8KwJlau8oR@cEE zHuB9?K_3ga^vou7D>+%#GF0(wb#+7UysZ;J&V+7pZe5Tg$DhiRDo)W#E%sBRTc`VY zk3)xVy%rbAk#_cDQ|wvVxPX`9E$XOi^rLtSFj!=UfntJbn`E%mq`YxRbk_H zuQ0V3{b^IxqMZY!-_5*3{$OJD8@=_U%Wt5E>(Y5k83sQ)y=k_QB<&${5XQGY6gj~z z>@+rUp$)5i=qp+Jv;&5ibLXt&jBJF>;%zCKPwe@=dnLd(jKgJtp z&7Uycy(Bf0vfCc+POjPg^nPhw2BA)lqa!l`EH>nV6KpY3{zrHOUFGI3yiRVOE253t zR~=(3d&%h2m)Jti2n;$NG={H@?LGLmX9Jjf$wT2Wbhb;k9(uSZ6%PZ~#KxmI=htHW z0$wc{aTr9}8_kEJOMQ#0RPIQ%`*>&9!9sV;i#T8Tb#G(w!CVI3f0_Z&I=w(C2ylYF z$z)LIW@IT0;Lo}1s$^*&vY4E3(o$u)-gnxcsl#?=-rH;XD1_gKKb+#qotX~^BnLhz za!QrgWVxx~B5z_Nh;_30?;33dlm3)>k6wo(1XWhms|IvxNV?Q1aleEFk~H!m*r3`h zz2YH8`M$uk7Rv;6|F|3q!X=@1BundlYsf`5Ju=7}CNcxT%6P+b$a z3Fp5RSY)vK0H2p{70dFBPH)^5I*KqTtNC&2u7(&NibhdsIiScGM!yR<3)!Z(wybXl zYZb1$zb*3heSJIbBH1hTaeCBW@~*U)Z;VJBn?|hem)@yibNMw#94}-XS!ie=3z`*U zj2n&oaYM3^r^_ruHk*7aP)}qY(C|F7nAHHmjV7-ul@eg63Ihx)sA>8NEq)10VpDxnoF1WVDh3 z33|ykmvE5xiuK9kWG4wpmv~cP$mwvm=_|X5pF~_$n`c^KjUuJNbuB3ApSeo;mPy~m z;Y7&lnk-&Et0~!Mxw55>V#M9?`XqYMsLFpH57N?B~F#&!>zfdporHRpg)m#d(IDmPAl~5Cu%;pnHh8le( zYtT7nDm|MzVvDKZfgfwd8(4QQO%@7mi z(8M(he|dpk&F%E+UBO|$@>!m%K1qMgkmH;>-h+ZKhwx2o-Wi|>!i3}x_7T4{n6_uH zDE8cM@H;tg&1!UXD#Vb!Oln_ClfB4ph<6i9)H~%Pb0HiXY9;E?50f=QIq=aCHfrL| z1FH)ld?C)0z5WAAmd)?b|HwEdx|x6^@}e5CH6?mSaA>bz#fLM(db#=Bj^L1gcg0K- z8fVEK1@HLJr6+BPJTYsgIk>W72`L&<+8ztFVo+PnEWniGQ?8Xfxk*d!Vrf@O_H0Ke z)so$Txs~0qY}uZQWk%uiq3|8zl$^$I?;iGY%*P*nJ9OdQdw{XLA*`t&Wawa!f;;Q% z&WHx=-~HNHH_bEl1Xu4gyuN3S%DXj=Z5gG6U1tp?aplq#xgh@e5>FUfr;6{5lNpU% zd{Ri}?${N(=kYBwWHvX>!w_mxF$VV=2z>-W;xEL+bVo;c-^lRs{N%xcnOfK}0UTPx z5F&Px4Ut{yxG!Pl0A=Jmo7oM%858DUdIetKyZD)h(vW)9U@p*0x$y;p&5!rnp~nxgVb#?y^C<1m>Qaw<^|6seATqtQ zx~-#x6YFkle6ASo%=WjEs6)%ly2jJ{9Lho9*X1$&YA(LX!m^nCU8+IW$`X}s1(Prv z5BmiaPiDm6wpKw{Pu{Z40|)0nNv~O9a_v?^puKRkPAj0_R<1&lMw&EqY^~$!9|}f4 z;RCsO@)JI``4i?a1pd-&qN|=$rk@%(1*J8+bIZX=`JKpU`&ZUv z#*(P*BMtG=H_%?mW7oPI4pK^XWtI%`00y-(;ylM#HnROvXJRT6n5R=&#rck=6I9#8 ze70t$iY$^6Z}92TuS^z1-^iO3wlx1 zE2LRp=+}(1b5v>YctcJo7IBxNU#!}_@u#yRZ4i}aHdWe)KGcZm2=a2jv9||^;?()5 zrO}`Mj2qN8Tk+ut@;jV27LiP8wod58XkJ3dIwXtwMqmkwR~7PY(V8wQ&qi1MTSHo? zSCzIv&usD6!E!a}2D7<#m6ss{2?+=ik3j_+Uya@fD%BCn?GUT7(*(3-@qDmc_navn zB^v5jEPckV?t)+ih=JyOUSGnvcHixmpo%X5dGqWKt%7WIz%p-x_sbz-sZFLTU3^Py4!B_M3IeDL;WjU(=ICH#skwm z%|RhY$g&LknA!PPdLagu(OGts$&8 z9>`Akc>)D{%+oO)!m8jiw*WuaJwujp`e=1wmv*-;zAt^FpbH6jc5YzlPoY22lOfBg zc(%-e9^Of3Hs}#GlwG>7k?bM@U^5(3YeD583}iiX;^jW?=8OIp9 z+p1pIA3rv)?Ynj-LvxS(Vk2z8MuCYrhbFRUe!O6C8lg0vxPGut`nJBO>B;k|R&04y z=H&5mhizUZORejF4ax5Ru@eV!*xIv-HKkd2vmF;j9RLdav?JRF_Jl88?^y> z=yCh*QZ#`4yPc0}uU=vhqK6>vssCioC`@4#(!pV{#8J-Tfo+uOhe+D3DJgfl%sm>+ zI@|ticzX)Ui!XmkEv`;iDz+ms|7~?fB(W`vRW>X*7d*FHdM6z$>0MjxY?q zuW=#PF`LJ^-sk;h6+yr2%_q3V0{=Y-ZDOsd;Y6x9HV7V!_VCy?F7WAKQ51 z4tag;nZ7OfhcS=3FS=wlScTt^%CqnvuUt+9M=mu=NxDgCOaK-;^9$a0wq7qYF*4!` zswAjNss$k#c=9df3uw8=O@GQ9MUS(=$AdH_=2iv?n6q9IPl4kQv`#Hcfgz}=4XM}Ori{Cu-k)9#aASwpAC z43!9#Z|?J$lE@-DYb+*|vAxcS)ZCA3$wVSialO0SJ0;1eEoeFah=9kUmL~)4vmeR2 zZRbz?V|3TBS{B;5{LET7u~soGBB57ccTVRbF!e#gUXKUN1ZFF_7E)~eQts3YSOX>) zC!ot9UyMY^c=sb+i(;-=xC|1mOf-fAfJfsTr4P_$SpdiF;+WaI-=)}M`;QgK_K%^F z_Ri79S459+c2S&W$G7+B`XmiiqsbqMawWNj7fQUdvKDRZl3)3|bv5I{*z)HgqF0H5MBJze}rF$8gSH<+<7T#XghV}3EG4d(z$rlunvVJ83Nyh7)&AkS?Ngf5Lc8Y8iSj7u+UKg%gw+4rkOZGo zjwH%o?~WONP!E$H*0qE1^hPXk>yBgxC|Yb+7A4V zPu}HIU$E~|6n2LpCTpJqqxq~I#2vG#jh=;Lf+gYd9w&*EPQjSw518Sx1F3r@aZSS4)18eg$l&X z{9He8MM|$N%%opR>q6!vb4T}TMd-!FZx^|$Du)`VzwG1PWh&=rRWwx$YdPz}zzyth z23t2+bLBH4f8DZXr+?`4KJ!Pc2) z{NfnyX7v#;IM1%yXOPk-)~MwqZycyPfjJJTOk57e-}-PU@2j$gtuhb1!<46v#Mu%kOwHFSFmrpVv8|j4;$YmqG%FN4&iZ{dKq*l zM{vsRiH+AO*(Kz9DTV;kP#zZn+<%ZnV|%;NA1V)^e*JWKxgIENn7s{UX$KY+z41{v zGEcwt$L~bgcvoTr(l+saF^FA+2KkPMi z;lkzUi&SVC!mb7-gHrX@?>kqejNjFsDWMj$bKC3+mc05{d)nN`fVZ>F7fL3lfHYNZ3wMKpU3JqAU?vcI7C4>K8n5+23TW?yb^c#%#;(* zUk_>B;wapsDBf=dK>l^LV4D)g2j>&(Es_7%w)+2C`~F+?@XzA(nZ{>A&W_nhQ0BgyYvexJ9= zxTlNv()@)uJH_f!+gJ5W>sabcD%_B@EXveFT#AlC-0v~sk7tpDj zJ8aq#u$ecqn*ChLy6vE!1V188NE;FEc^4+>h99Qj2boPqN=N>*I z9_$(DSXebyig8lxr0EK@LcUB47KTeF%1ahncAY2m@A2mtNiAMFO6Cj zJ>HF8rXp+FvhF2fSAh*DtBBPNt*sVO<@t$h@yjmkX`6W6rnhOJ=8INa7gnX^773mJ zIfF5GN>Z|#Q8sYXFiGQ%wN zi|?m&3SJjF`m$4r@ysM8zI#>nOf=G?qM2Z; zFhWUW`c?|>x{It!Ot1XwQ|fE!#&uz=;kr@umb0Qijg7h<4n*gw;in%NGV=*#*v1fY z%;rBzCAr-HNNnu0IfL7RJ0ui?35^~5E;hchbO9+fx&K3)iW!%I~h zmjA>g{irT%)4_3MI}hU!*Cf`Sd`U&mq}&QYW-XNuNw4l}*3TzB&ykw`lXVnNNn~dQ z_2iH;XO_GLHE^HYvyj*ugyv4|6H==CH2QgEXT|Xq+~uzc=La!IFZzE8?AkzsC@`Og z6u5E!ab#ipqwVEPbKN129kugPX45Zc5e#f+C{xzO3fYxAUY6PN5|ol~$Vn*otIL^- zi>ZE@ce;0+6iCFmK?rB(k&HKrvHjign-8}T0Y816inf6oX@btlR__d(3^6vv-m%gL zGZvO#B^qulWEm{XN#9`zC9+c`%EL&J-@n zwT+Z!aP!lz%(0XlGrx?2LTk;;pE-d?^BxN(d>L}_Q2MjxnTEa6h{4MJLB9uRe}!Yy zUHT`n=s;mMcxaP#js!Kl{gg|JH}m*-YOAC&Y($(D>_UDJ-(_8Z8tE62sKc$v&lc&R zx`FP~h#KBePl~>UEJR8jf}$`Rh}*M1<*6(m1xAKm!GXf zGB`U~V98Oq?-`gNxyp;lw za#mymQOtF02=VVH^%4Byy&*);Hj)_Hz-M}X^={+3V3W+`E1?)@#u^y}@jyjP_+Ano znH2eyDOjIR>g25`OTGC0v>AwVbs3t@+>em5Zo<){v&d;frUUueB#=!c7YohmTH|XY znD!`Qo2q0+j0S2mF7@~43%iP{aI z_u=&!Rh!ntAk&v`5Hk;8525_LoBdKS8#X8v#uYbcwbj?{iZGQj>*z-rRmuDCA~|0@ z%dcm#o9=BjJ4pJ+TvHb>XzqlUnf!C5yP8bwqm3<9J4@U^seoXtzJ&TIeo%|jhJ7zKElAS}ttS*%<1CI*{ z*yZv&zbjQ%I#v@%Y-GOr2Iz7tJVP0|#GsaBcAg2cpq3w2P9%x^QQy&d+xCL-SIS4J zQ}{COXU(MT^WSUyqngQ|=Pt_C*5y_iQ9HBhU8`$PV>SzOac!q|CHXk!~({XXFal+U|N>l4nkJGF9`3idW5Ed23c0JODmH&inaHapZ2+B^Q1%EKpnB!AV4|Vz zuxtQVbwgfE+8M9}2e-pG<`1zrcKR?HeReo%q8o?Bun1?^3P%h*fPGb_N|z8$&X4@r z%t08j{?0(!fzqwEm>TCO=89ii=>R%7%menY@2~b_8{$jyAgm@wU2cB?Y5&;LK^ZEn zJ<)k^-FT3Qvk8b3PDi%((b^f!>XPJ@#cO2R&a?{^4vRm!InM*33D519h)63oYVB{& zR&?rzmrqG$yRPk^u7X!P3Vo&Ek4bW8NA=ty^5wXCWO8oqWr(}oLsPcLeD3w>hUqCD;pv!Mk|%6jKxM2n1CB>0L27#`xG-be?Ah7#SjGo9M z1nBkjwy_64R2syJGyuf0;Gj)FU$LF*=ZAwy%UipVeSOhdR24O*;0?D(-@0N#Q5=HV zgfLK(jRyeWP&@pr6|4%sp%+1B3m_rCZsf7=c9^#Zz8Yw}am&pbr^v$irlQtlnSJ(D zJW_-T{CemUN+=kx%3N#1Q3F=h@ zx^1Et$A42hS1UUf&6Z^6KN`8ZDs1zjYP^N2In_#hWVNo-HA8kTjB&((_@WCq^FC^^ zDtguAgzlS?&*OZ6bmzXkB@TP@;!ODxee0fSysW%T-IosB8m)G^+jq$jjo|B1tjg02 z51RFgQqYE_HM)WbOl}`5{|-o)cz0?PxTCnW+M0xvOhupdQX(CCEL0XfE>7rY@3Kv_ zOjCR*v}bpwYT1$QP`@cW?e^Pp6@k%PCC3fYU0pMPxknQ3qm}P%A4L{)%mcT)>14b! z%)dbmzm2Sj9rUq*wM4#(pGq9A3(vXm;fDX~>?c^-PUhmXi^}3tSo}w>_2*%pa#d-Y z&*Q}F7)N+u&J{GyTQ!pp3{Su(1+J`MPbQ-*w9PWq~E*lUR@qi`n;wMxnil#(JDh z|DL9z@9i4w1)>VD!784Vc?OHP-x^3(C)4Sc{Vg{~&;7NXON?GISI-6RY+e$r?gzb* zI+?XuH&okIX%r03$=N9z)aBKeujI5u&1f^r2VPF#HjTW02RB*5+hyUEDmV{xd}eSMwtaeD?)`gRjuUxrMdcf|QGw zFl-YZw<>0nd20prYh}W=hVx>qHi0@L-U0Wp%F!^WDwIAM;)nW5yi|K9tN1wD&UC&WS-OzUWwgdAfR?-R;XFxhvHhGYF(|CNl1-V{caz1d@y) zD~W$myb!sap8C0e-Te#F5Y+_0JlcEf$!73wu47j&exE$^#a+OI?O?TaOJzzb6Se;Q z5;l37AcaNil&0_Tg{VsuZWv|Ao)ZeP(*{(1(Ns;h0@W8kib9}cUp+lkU zmy7yN@k||57e{fqzjl@#)$`uPPpM$wpO6avJmxc|vF@<*H=ys?)#DNS9kOf;nLc$a z!~TnQ8B)Ot_+li91{UP}Y^+o5mGhOiq>&3~(i66`Rsd=QNhO~V| z!`>#hE)x(#YzR;rn;2(!{6@nZDYZpBkbt(grV~H%=63yg+IU1rbk?%n6xI+(ToLZ7 zg$pAWcgZDjOfssBMJ*1*CyhDt8?gTKm>ic6*hTt^TouL=*91)6&&jqivspk=jC2yM zgVK-GK$I}>C;jod+8HYojK;Wl81uO^-Q@n(K=b9ktVfzz1;)-mU}HVOy!7q@&vtL>J`_otr~(%R;1FPCyl>E;?z552 z0-zwMDQAvS824*rq$xBvyw&exYC@BgK*kqM8~e0Drj1oSWLtN9?`SBFD0R7T=Zu0s zVu0NkgA}>PO2Ds-A&DmmT6l z&6|0|F+u=&gnTn{AP%+dRAV>ga*&p=VjOWtAFgy!<4?Vh>vD+i$LBpG>y14YKqYdCyGR?CMVlm5yxRkp$H|Jx_?`r@pVD9V7K4m@PG zil)Pr)|-8O)W?GDmYpJn_P#NpB(VLFF&^8f6pnYu$zlPd2gi=TzRYEJ zbvimJirs4%PH`%+`Xg+?qH{Pje~w^sab`Do(mq$PLjiYuamH#K!{cO$JF}|b6fTSO zR3`?nGwH#yvm0SkE&5~V*)ARVSu2#jhLA@pzy7vEDk02_JMbi;gHps|UVZ#)qnOQ^hT>MsLa~5Th2hlYGv(FJXw9qa$GjAgO68e)L6Ua#%>P#-b@Szzr z3vIqBRJ90b8a=0CH~R?OZQI$5fOQ@h-Ke4a5buUi>}T1zx2dF&s;6R?%{&ko4{rOZ*WVmOfg85LRniKzJJN!a$kHD&(U7XkIz7m<6xo{HLti~u@AnlcM% zK)Qln3M)Qc`JV#nL=JtUL)j_RtC1q>Gs$F-Z>-m&Pny|Ea40ohxMN1TX6s;Bv&@W< zd!=uC7FJHBY8*QB_=tbTDUaImPj~?0JZC|{d#U1 zE}E?_Q7Q-OZB^QFk*Ue<36|ML+RZV5){(5Qz08M0jl?$>&B4=jwAx^(Bt_`kDmAJq zEo_0{LGUu5JdejBQdivaeSlZjJlnjCRkr^ThJicpByvSIm&#f}jSg&~x7@x|IHh=; zh4Qs$4`x!>_@)2kbB2PhExYSiX%rDQsCBE~=BXu%MtLv?c^9LN5<{y&!`IOG(~XDR zU^XFLMWo2esY3D!(J=x}}d@V%>OBGy?C1WXK++{1zYHzI( z0;*by{(R;>)_RM%jaXS;^UeCir2zGh5}4OtyrU~L zeF1^5wHppuxT*Bk<=?V$VeEobDPFeE!*9>Jt*-TD8913JCEWGm zgXiI!UJbT9cv0captieBN@;G@ShZQPb4ibl;jVF?ROU9rFhoe}w<_@Wd}k47wDW8i z5oc!en=GM@O%{Q$nXxs#db9(h<`3Zr3Vsgm@;(Rr9_`8VpvP0$tF7(!ApxiI)^?>E z@Q0{mH(F$^`L$g^_8|(nBK*@tN}gw;L`r#`)EFnC&(6SRNcB6HG>gt4zQ|b06t!>) zWJ4)YAcZ5WW_3=`uZ^n>D!}~YxH*7TJ0;@Z8<&hh+g%7g z|6hA&9o9v+_5Gncr9(PJx}}j2luiNZ?(Qz>5|9>Y35g#qCEe034FZB7AkFXj>3!~l z@|@#+pZERaz24_~28N56{b8-yduH$1v-bKDCu(XMbYQH$=60}+>K6;=XZ0;D^23^) zzSPS({@HAB=e zkXjypiS24qmz>l3a=>f&rF-;_d#o+&r*<{z;WnraYUh~2Om0szQtHQN-?T3w&p-b?rp%fSP{Ch1L< zDJL^yYh#ulUw_29eb$tXA?6}!p*kQfsjqdnbG-0Z(pSi*Y#fAiay-`fh2tBs)0vQ4nc3-&!DzQq`vuV!t`1_Jlv(+nwi(yaP~uRLZ{2Bz zd+?0lgjrYeL6tQK*Yf83HJJM@aB9~ zwT49gCu;VIj6w!!E=`XmE1|4?dt)%c+4 zV@U86Imf)ldkp4ko>cB+wd_UyIPJIxHsY3|Siiit`tgy#=S`l<;& z=2($K@z*IV!9Ldvr~QyzcbD32pd1^h#bgp>lNHi^s#FP_5Xwn(U+@N3Qf|cJ%lmpA z9I?3_9F1&U(G{Hd@_Fws9bFHjzqe#X>JJ)j0k3?q6^8Q_yxu!&e>?B?tP6ZSX4-Of zo<~gI*J`ROe7@S7W4h>dy@`QAx=#wo<^rHom)q} zvhUeT+H5V}v?P0R#2D>LPn3gQdF7O$>CQ@g3fVz#N^x}Y7vxam93IbO13xo&CF!1& z57QUif!Z_p5InsVk9AjThLf%##E>vKMOKv1wx7q-RU{c3|3b&fh{{-1WW^xt`FdBe z9i!goae}Vg$ahc@Yf4}By1e?AwR@yPpDTR8z}#d`)v_`v^3-v`aGgHsVXBFHyd;kQ z6+RafIiDBWgxFQ2Y?}Vi__NfdLOwn0g$i%tE0^HH`be0^Y9SZsl4^CsWxI2{AeB_Q z=N|0FW$_~8W!;1+BDpE*c(cxt2`5dSyC0j{%d6b*9(-e*H^|yk#9OLudGh7Gd1Z`| z@w+dhUmA}Zr}NY&4$n?D+#gJKA6$MYo$0tIzjWtukN-<7r@$S_p*;6C5$5eMT1NCs zbL!9(UKD)x!frps10=i=QCMY${Ct`q0Yg%USS9ASgdQ`!T8pX`AkDYcUs3WOidRU% zRL#@t3ZQU%;BEI?GZ$tPl-w(^-x>^>K(%{f&Q&^F33_i-zzk2Dk4bQZccd$ZMEcOiPjlGK zAIz3|^uq`}e(>?(-Ly4UIU3at}AFC;!EqkHbaV8vt%&aD_rS2ce^MTCRMVmD$*t1NAKdF$rV8V6lJA^(xZ~xXKY#@ zuDG5k;`%XiIqO-m#Yvz;j0^5yiJR#fxn#DLBsq7Mu(L~QW-*Mf7oCyjP(74< zhANeHt~F7t9XGX!yKlO;;MzVJ8hl0gT*!ij-nwxI{JC}y0gf$w)yY*iR~Vx zZ)wP`Y?mfJ7+z6J>(hc)(5<}}$BFd0YechC+3wNcL&Ja~-@x6YUH_=jOf)9t2u3x-f^;G&+_U_~3 zFwNzn^prNCG58pB*8Y&7v?M)}xLT)p)DhUFC2EYF0I4cp@9;q{Gg$gLl=odTL4-cd zVK^PfRbeRMm(l8&J6h=xP${z9Zgt$wbu%f-)ukI0aRjqe);fcbQYOq>V+zVDFt1AvjOp@M_0 zog<|Cake(F(KokZwy-lc1y6&Q08AOlN0I;(6cnHb`2fIkfH(jH4gLKSvcW<=;Sk~A zU}52q5fI=JQISzmQIJtk(9p3l(a^EbQBW}PFtP67;Ns$h?F zh)BpNkOQhQ0ca=~7-(3S?^Z*O_J;f&fW?BtreG6=zoV#+KxvP|?jM(pNF`R$hO0EP zPt9TA5P*b?hfhFAL_V-r&|a|=f& zXBSsDcaOlJ;E>SgVd3!!FA|fIU#6tK&dJToFDNW}Q&|P7uBol7Z+PF{(b?7A)7v*X zHa;;qH9a%Cyt2BszOngfYy05q;nDH8lhd>F?|yyv^Uv)M$8PzB1@Q|S78V8;;k#c@ z(5{dj1`8IBf(;&9R1rbn{thL(KO&A;Ty{kp5*3HiKCXeo2r?cu=Q7Q~ch`P+_V*kM z_&@UOpN`%5H4C6Z5g4(dMjp~V`!XwErxF*my^skbuU-0x9 z_o$Bq09NkJ4UVgX&XyzG5#N1H9xy@kQl@`k5*HZ%HlEywvvJlxK^$7-eY zSASLPw=(I2*WHduk$Dnpd;0TaP->vpz(B5p9dHgMf<7rk?Pe?Tt^qXeEWJxtO_*pT zc_#J3ct~V(4fdK~=W*p}%5tfcDv@ zVBmwg1Q=*DTD*)CAtl8f`qiHQd55URAk6MedXOl(#h*_pOu$r_mn^fWhv827Jp%-L}~|q8Z_uo6YRbCkv?=Mdz|*{p5tVVg>ZhozGR9#MRN-=o^ zp$^)^Qggi}a92dSxU%c2v}Ivt7TPN)rn(n;##tPM4#US5OUbK5D7oZ{=6Be`Zt-+9 zgO)yp>p^|+E+{C9c{3Ovs{#xpdHc~;(F=&K44+EcQq{DK=Sn)OVN2yd=wg=!D&wKf zh^k{GlD{F8dnWE4m;;SO;RJdsaJ_ z?p@-5eSpfjYRfJ|irs8dzT`q35z}f`a(d9P;o@n`)7t!rwjOkP_vw*JVvntpm=ffp@b`)V>%Qu+FA9r-m^ z0px^9WL+MNLvGfNV4!OonUDUOMhldwlA543M9zajk`xxC&{(c5tq}iiiS3hPpkn`l z%n)&YE&q#+$BB>4qYlg2*F_f6{b566d9^2xH_8t`f@)4YqCoJhneyKR+b$Rm0|YqW7F)hOHs7a z!sw-Kb#{2e4N~ScJB?W3iEjjC!J1$o==Jcm+qUp26C~Z`sxutn)YItff{zwi7UN6N zeO-MIY7;)E`#o-~Iy{1AJD=n`4Ms8FafL7V_VT4Rmi6n}p3Z2UXCt_F|OP>5I+406XQP3T?~^Qg#1~_5I`fRb3^WMwQ(Q z4pEoh;^?8{s>HqovN-&lJPjbMZT{eNbxY(~Kkh2Ypn%~X3`SI=OsT@Mm}~3^;j`63 zFtAj)2?nAjX(LJ*Pvsv~BNffeciY;K@vHXU1JNG0#ni!hS1D*_fB_Ujp%D^atCt-k zXZg;Rk`Hw%cm`neLeD>S%jJH99f2Ic-aTX{S@~2X&yUDc&o6RwV<8$>UU_ThsiAVi zY|4Z)Q^Jple>$FedffHfa^uU+XAK85?D;UW|3C>+mV>+l|Yf4e!VW;T5j@BB$7=mO?HUiB;Gr*!Tkv zLinuq)e&FGR&H#q44VNchzTJHYta$4c5yPa3B=DM|o1{};QlTS6z1X^_)#(}YdZ=)$@*)=TWC zQoO2wD{T(Mn0|3E@T&Cf)g`X&(L^(M&h1pedE1-PglIlTc5 zs=YdGC8?j!{Cov%kpcnDulj%htucud>p--^**=ayT`0<`~!s&|T6#CA)&N z_WiPybHi1(@)B`IiKu%$$_xfZY$QO@Pf<ajU-zBvsku}a zZ?R2kiczWwCfhq*Qv>C)&C)dQP!m`NTu7k&>)yUasl^@t<$pWP_ErZC+UIL)i8Z=D zVv1!f^N9BvTDX2^l66(UA*o?j8|jh&5+LlcvIYa1ktavpUI)^MEz9g5z`&<_mXKiz z9NB(~neP#9H5h0KD6l*WWF-{N ze?;P5FNiTkV&o`-{fj-qKa<(pJJ1TnH}a$00*B&Bq(7KCaNKiBo{$CuJF1D}CMLkA z6AK^h^;fHckFGXtz(BqkG}-B6knlP3EErg9Q>pe16C(ToI%Bcrig4S_q4ElswTAVN zN8s(fyeE4h1a4ej^8%lKw z2Clp@aCky?38gu7Azjb74T5sk7cg)h3KI^rRqhYQJWuPuJI^SI7i;Lw3^dd-4#EQ3 z;{5H$Z3I2AI-T&EiOVa?Q-=^UAGo`$drewdtE$e`rR!1d^RDhFbT13?<7K?YR{-se z$`~|d@H7G_B*JvafM7)zqwT4bwX5l1^AKbNLVQ!&xUYYfq~e>F8fATT@JospSsloTsbAr3hJbuJhJvHXj0}AIW1Jp zkDo6dnwXU4ujzbz?fa%o#uKiAZSs7k>(w%0tR}-yu5N?POtdSGv%I0 z4A_t|G{8XHi*`R_b~jC0Q?e`4HdRclT$K@h4<1)i5KemvRLA7?;B9Y<$Dpau zab2$nM(XARz{*5?EFCJ^V^J4VM>M~^2odOS_e84Bvb7asHf4qp!}0VplLD*uFyy8PWZFc4^T%v! z{q?KYZp-Z4JrmQ=yH^vWkG_#iI8O}pmBK}>^~roSz{14vl2$obj2t_~kH8Y7SK&3_;E$iq}fTx;gJH+nr zYR7@13*37rYHI4$FchvN?#fwy71)g&Wy!vr&+`Ec#FM146g%mng>0CHY>~(yyY@KPd znD}3XxVpyT!t!%kb-c++^k6`VbXm&;LHUIDS4VC&yq@Xmn3E?-D6!Ivv?}onrXy$g z$okF@fyA!i1)wN_^B?WXMp3j;IdQnFda8*bq6meQ84`eTY13XZp_Y}dR-r+8UmLY( z7nT+QRUvV?hw+>HV%wu%F{o5V%kI*$JYTQA5UZIOvP*028R7`=FceT5#DVJ9*zzG5 zGpV!`#^|=BDSkWil$3O=mz{!8iHcZAN&GHbIyXo7^o955&&BAe2hm9J+o}>>b|T0c zNigY{2vnJMBd`4!w$9zFO;gREbgsR5tyuvg!4^^NMb5Er-PwM$QTDgn%}=8pHIwWW zYr03KS_?9h1KAYs&Q#g_a*qWAAsxCua+rXAb}xC9vnL0IM1pC0V@<3y?~t7c?>Que z0HSRCQc7e#rE%|D&C%&Kw(wh#I>O=Y^zob{Yw^ zo&9mA!|dJKm5V`Uq-qSb>Dgzzw9@do@<|SM^;jfJhM8A(9R8)NC}CEYG^N9^Q8*(S zLtB(2?B!9<$g*;Dj4b7Dm+Y`KSvsF`_-&U+XQ!UTeLmUX?o59z{ z_!t-u_2vtD`K(Rj{k@n%vkI*lS+45gZ`l`J;j*W@Ffix9?$EHC5Wx40w(K7s$T5$tiH z$X?-n+5M*Qd`c_RyXR1>(t!--OT%g=yyNg43%t}ONYXsK=DXu8R+A!B4Un;HxW-9- zC-p8nHu_3`jGh|;Csfl_-iq)!)Qs;^Z_DW*hvK zWF>nQWsMwNX9Xv7oTl)Jtcx2NX@x#6p#?wRBGb)ygS-E^qL(QmZ!R<;#CDg#v2B>F zakXe`M@JQBd>z-QtyAzRAMpZ;3cp!(RV7INs9!6IeX(a=g6Xly)zpbWcm3KiZ9#%~ zL(+w3pqZlyocCG+-Y~<0S0;g`HiD-QgVT!6;f@QUbZ^wJl1&qtX z<0xUqio7k?n)9UeQBhmCjKmj7#g)ss4v!*iRi2Hcr=Q@qNTADUl|Q;45Kdo2@Som4 z{~sL>1)lj;;L+&an>->&Y&;ZX4Id=u&kwP>@9J7VUOs(aulMf7g2fywLC7#dBf_d< zXZ(F|M+Zav{dPTKLnr)KGY*s{T9Qhs(G{LqE4-TG3@;Qn66D1ehYy?aX6FUbn@-3< zaTZKopnzt&F3D<#dED#T3nHUtQzkO4kHv~O*Yf(8jk_1FX$)B)Rs+y{!zjzFW0YjV z;rm6c2kWAGsZ9iRnnjNy_ezvKmL{!_na#L_8=%C=f-q^@TGv0Z9G`O?zu7UOVw@m> zN3gSbEg_unvMGHaZ|*47M&5az+&V+LN?nR>4@bzcbd!2>%&P_m-tpODd~;T^jo8f2 zZb&1n;lilrlJ~OZ$oZbYX&USL%7_oAi>5omeH1YMJ;nIZH_(KAVtzsj!6aR>qFsn-c24BlG}P0+HpWFjuo{@B8A4 z)Hv_S@40S6F}|SK5;4ocUR^Lg`x?(OC@k|jS>v-2Ck%KA3cG=8Ar}EY#xu}RWYT`Y z-W;S?j*Q}7vzRAfFYoSHjHABuA>S;`4nD`xQS7<3RCb-+FrMx!(6 zxJ#5fIM}qVKou?LSx?~M?EG}Y5&QGk4LI6|dfDu}!47IhPdXG(9`JmTGiE5b$GO}+ z%px7bS*AvVFv!1cqX zRq|IKCxBE^t=vmuw%}9DVkD7yAjoR6 zqkDmb4XDB|5z)itaK0FLOv26OQrj{V z;Am?o-EtDNLWfNAzBUmzFqLB!fu}OyOR_m}rR81dx8q@L{A6tj3#Ui1?y?B-0 zsmStPYSN!!cgDZwFoG#>PR$Nakn@3up)taw^Fr~({q2*OeMqv*ye12J)r3T#BmS`h zoemV!1lLOy+4X>Nzhh#@&2~9EDb5ns)(q~?8_N~DB-^x`0a`pK@|7=Q7i?w%HsRFI zRQF4Sy%mtIBRG}eCoA1m@ucpLPV@@pED*-RUE4S%cVr(i6G1N6~e> zF_fyoPr5qiM@PzjB{R1X&%+@47%ZLZ})Q;|rL3_yu0W*Wuct zT*}cTMFyWU7XzzH5s%AZWvLOT$-f_|{BTmqt{fz_j}?cods!?Cp=#TqZ)+THRlQ`w zQ)UQPD5TJ&d;EU9QCyk!zS|b9=tgT_nl78Zfa9j*1L<_`o^W;FlXgTwHk7=ARkQ9$ zy*>%tQe^0)VCRq_kOQ7IlU+cXk)O27lF5n7G zC5GvBbUMGh{oHu_%e)TTrSjbV2XSN9iJ^|2eiu))RCb2UNQflnu%w?TMtRFqVY&0* zCN%zCzyjkiSpFCvd=RCaf&ZBF(?d3QJ0E}Qb2Yg}X{Pfs>;OyXGm3GRMg{}#VUqE7 zk4);FNWvwbvow7hY@|l-+&Aceo{!kUGTOr^i5GBNM!b3$wc3#{VKG4!vX*Th@*cYyL=mVTV(eJ zXAX%``wqJ;xqF*%Ta)yL;Dz>!b3alI{fMW&jkq0&euEf=47&b#e*Vl^^plwOAHTYh zBmDd1X~=?}zb4;E{QZ6MM@ZxPYx0e3-tWlY^~P?tHHZxIUz2~5qWc}1>4ppWZU-VK z``73jdAHw@e>M65@9&WRkgoZ$w%~W@UyVM61Wf+5(Lb8pe@MP2_&ekqX|KO;Gb|*| z^RI2bk<0q~WMtyML%xxqx=p?<SUT+_v<#2FeZ3m*OvYKNL}J^KL6I-0(DMe$V?+Pq%Tmv!LJLNEm;Q z`_Z6o<8G&BzQIjGuC(8ty?@n<-OjXngHvPw3HN6;|EP?gD)_c_w=>M$;7a*^!u^Z7 z{m~L{<8G&wy1^j`{T}yYanfzv?c^#qI0fF`J-OfGeso5+asR$o?p6L5xS#F^e^mC(YO&ko+iR|F3>{Ya zgM51h)*pm_A2okNkk|URG4!|1xIG1W1L=1D!;D`hL~j?rJ@Iu@9MkO|Ui>-@b{lYe za_I)J`wze`Q%$#v|2nsEQy2hBJ$^a|f6jE=F8=E?=g-AeynibGk0($CSvbhB8USb@ OA5S3a4z+&YfBG+N4}WO@ literal 0 HcmV?d00001 From a3b0804cce29d53512d7cda0b9c14ec14c8a3145 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Mon, 22 Aug 2016 11:49:35 -0400 Subject: [PATCH 02/34] Fixed relative paths --- TileFormats/FeatureTable/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 0e433ff05..2e0ba9346 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -8,7 +8,7 @@ ## Overview -The _Feature Table_ is used by the [Instanced 3D Model]('../Instanced3DModel') and [Point Cloud]('../Points) tile formats to define special behavior for each feature in the tile. +The _Feature Table_ is used by the [Instanced 3D Model](../Instanced3DModel) and [Point Cloud](../Points) tile formats to define special behavior for each feature in the tile. For Instanced 3D Models, each instance is a feature, and for Point Clouds, each point is a feature. The features are defined through the use of Feature Table semantics which can be found in the tile format specification. From 0dcb9a07b28975f433e2302e3e07b1e71f1cbd36 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Mon, 22 Aug 2016 11:52:03 -0400 Subject: [PATCH 03/34] Edit --- TileFormats/FeatureTable/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 2e0ba9346..6a45dabf8 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -60,5 +60,5 @@ This may vary between implementations, but in javascript, a `TypedArray` cannot This means that a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. The data types used in 3D Tiles have a maximum length of four bytes, so padding to a multiple of four will work for all cases, since smaller types with lengths of one and two will also be byte-aligned. -If the string generated from the JSON header has a length that is not a multiple of four, it can be padded with space characters in order to ensure that the binary body is byte-aligned. +If the string generated from the JSON header has a length that is not a multiple of four, it should be padded with space characters in order to ensure that the binary body is byte-aligned. The binary body should also be padded to a multiple of four when there is data following the Feature Table. \ No newline at end of file From 4cf46ee599b653d978c656f5755bfd5adc1673c5 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Mon, 22 Aug 2016 11:53:30 -0400 Subject: [PATCH 04/34] Added link to feature table specification --- TileFormats/Instanced3DModel/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/Instanced3DModel/README.md b/TileFormats/Instanced3DModel/README.md index a3350569e..6a1a17012 100644 --- a/TileFormats/Instanced3DModel/README.md +++ b/TileFormats/Instanced3DModel/README.md @@ -51,7 +51,7 @@ in the Cesium implementation of 3D Tiles. ## Feature Table Contains values for `i3dm` semantics used to create instanced models. -[//]: # "TODO: Change this link to the feature table specification URL" +More information is available in the [Feature Table specification](../FeatureTable). ### Semantics From 877ffb946a025a51355c9fbffc2347a9e207c77d Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Mon, 22 Aug 2016 12:59:07 -0400 Subject: [PATCH 05/34] Tweaks --- TileFormats/FeatureTable/README.md | 41 +++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 6a45dabf8..160b67d0b 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -3,38 +3,45 @@ ## Contributors * Sean Lilley, [@lilleyse](https://twitter.com/lilleyse) -* Patrick Cozzi, [@pjcozzi](https://twitter.com/pjcozzi) * Rob Taglang, [@lasalvavida](https://github.com/lasalvavida) +* Dan Bagnell, [@bagnell](https://github.com/bagnell) +* Patrick Cozzi, [@pjcozzi](https://twitter.com/pjcozzi) ## Overview -The _Feature Table_ is used by the [Instanced 3D Model](../Instanced3DModel) and [Point Cloud](../Points) tile formats to define special behavior for each feature in the tile. +The _Feature Table_ describes position and appearance properties for each feature in a tile, compared to the _Batch Table_ (TODO: link), which contains per-feature application-specific metadata not necessarily used for rendering. -For Instanced 3D Models, each instance is a feature, and for Point Clouds, each point is a feature. The features are defined through the use of Feature Table semantics which can be found in the tile format specification. +The Feature Table is used by the following tile formats: +* [Instanced 3D Model](../Instanced3DModel) (i3dm) - each model instance is a feature. +* [Point Cloud](../PointCloud) (pnts) - each point is a feature. +* [Vector](../VectorData) (vctr) - each point/polyline/polygon is a feature. + +Per-feature properties are defined using tile-format-specific semantics defined in each tile format's specification. For example, in _Instanced 3D Model_, `SCALE_NON_UNIFORM` defines the non-uniform scale applied to each instance. ## Layout -The Feature Table is composed of two parts: a JSON header and a binary body. The JSON keys are tile format semantics, and the values can either be defined directly in the JSON, or refer to locations in the binary. -The binary body is a tightly packed binary buffer containing data used by the header. It is more efficient to store long arrays of data in the binary. +The Feature Table is composed of two parts: a JSON header and an optional binary body. The JSON keys are tile-format-specific semantics, and the values can either be defined directly in the JSON, or refer to sections in the binary body. +The binary body is a tightly packed binary buffer containing data used by the header. It is more efficient to store long numeric arrays in the binary body. **Figure 1**: Feature Table layout ![feature table layout](figures/feature-table-layout.png) -Code for reading the Feature Table can be found in [Cesium3DTileFeatureTableResources](https://github.com/AnalyticalGraphicsInc/cesium/blob/3d-tiles/Source/Scene/Cesium3DTileFeatureTableResources.js) in the Cesium implementation of 3D tiles. +Code for reading the Feature Table can be found in [Cesium3DTileFeatureTableResources.js](https://github.com/AnalyticalGraphicsInc/cesium/blob/3d-tiles/Source/Scene/Cesium3DTileFeatureTableResources.js) in the Cesium implementation of 3D Tiles. ## JSON Header Feature table values can be defined in the JSON header in three different ways. -1. A single JSON value. (e.g. `INSTANCES_LENGTH` : `4`) - * This is common for global semantics like `INSTANCES_LENGTH` and `POINTS_LENGTH`. -2. A JSON array of values. (e.g. `POSITION` : `[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]`) - * `POSITION` refers to a `float32[3]` data type. This example shows three features: `POSITION(0)`=`[1.0, 0.0, 0.0]`, `POSITION(1)`=`[0.0, 1.0, 0.0]`, `POSITION(2)`=`[0.0, 0.0, 1.0]`. - * Feature values are always stored as a single, flat array, not an array of arrays. -3. A reference to the binary. (e.g. `SCALE` : { `byteOffset` : `24` } ) +1. A single JSON value. (e.g. `"INSTANCES_LENGTH" : 4`) + * This is common for global semantics like `"INSTANCES_LENGTH"` and `"POINTS_LENGTH"`. +2. A JSON array of values. (e.g. `"POSITION" : [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]`) + * Feature values are always stored as a single, flat array, not an array of arrays. Above, each `POSITION` refers to a `float32[3]` data type so there are three features: `Feature 0's position`=`(1.0, 0.0, 0.0)`, `Feature 1's position`=`(0.0, 1.0, 0.0)`, `Feature 2's position`=`(0.0, 0.0, 1.0)`. +3. A reference to data in the binary body, denoated by an object with a `byteOffset` property. (e.g. `"SCALE" : { "byteOffset" : 24` } ) * `byteOffset` is always relative to the start of the binary body. +The only valid keys in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. + ## Binary Body When the JSON header includes a reference to the binary, the provided `byteOffset` is used to index into the data. @@ -43,7 +50,7 @@ When the JSON header includes a reference to the binary, the provided `byteOffse ![feature table binary index](figures/feature-table-binary-index.png) -The value can be retrieved using knowledge of the number of features: `featuresLength`, the desired feature id `featureId`, and the data type for the feature semantic. +Values can be retrieved using the number of features, `featuresLength`, the desired feature id, `featureId`, and the data type for the feature semantic. For example, using the `POSITION` semantic, which has a `float32[3]` data type: @@ -51,14 +58,14 @@ For example, using the `POSITION` semantic, which has a `float32[3]` data type: var byteOffset = featureTableJSON.POSTION.byteOffset; var positionArray = new Float32Array(featureTableBinary.buffer, byteOffset, featuresLength * 3); // There are three components for each POSITION feature. -var position = positionArray.subarray(featureId * 3, featureId * 3 + 3); // Using subarray creates a view into the array data, and not a new array, which is better for performance. +var position = positionArray.subarray(featureId * 3, featureId * 3 + 3); // Using subarray creates a view into the array, and not a new array. ``` ## Implementation Notes -This may vary between implementations, but in javascript, a `TypedArray` cannot be created on data unless it is byte-aligned. -This means that a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. +In JavaScript, a `TypedArray` cannot be created on data unless it is byte-aligned to the data type. +For example, a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. The data types used in 3D Tiles have a maximum length of four bytes, so padding to a multiple of four will work for all cases, since smaller types with lengths of one and two will also be byte-aligned. If the string generated from the JSON header has a length that is not a multiple of four, it should be padded with space characters in order to ensure that the binary body is byte-aligned. -The binary body should also be padded to a multiple of four when there is data following the Feature Table. \ No newline at end of file +The binary body should also be padded to a multiple of four when there is data following the Feature Table. From 3558daa8213b7a702896a86d313d04efa0fac0e5 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Tue, 23 Aug 2016 11:19:34 -0400 Subject: [PATCH 06/34] Initial commit of schema changes --- TileFormats/FeatureTable/README.md | 8 +-- .../examples/i3dm.featureTable.json | 14 ++++ .../examples/pnts.featureTable.json | 10 +++ .../figures/feature-table-binary-index.png | Bin 7810 -> 7608 bytes .../figures/feature-table-binary-index.pptx | Bin 36501 -> 36496 bytes .../figures/feature-table-layout.png | Bin 4033 -> 3483 bytes .../figures/feature-table-layout.pptx | Bin 35866 -> 35849 bytes .../schema/featureTable.schema.json | 18 +++++ .../schema/i3dm.featureTable.schema.json | 62 ++++++++++++++++++ .../schema/pnts.featureTable.schema.json | 49 ++++++++++++++ TileFormats/PointCloud/README.md | 1 + 11 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 TileFormats/FeatureTable/examples/i3dm.featureTable.json create mode 100644 TileFormats/FeatureTable/examples/pnts.featureTable.json create mode 100644 TileFormats/FeatureTable/schema/featureTable.schema.json create mode 100644 TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json create mode 100644 TileFormats/FeatureTable/schema/pnts.featureTable.schema.json diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 160b67d0b..1133b350c 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -27,6 +27,7 @@ The binary body is a tightly packed binary buffer containing data used by the he ![feature table layout](figures/feature-table-layout.png) +A tile utilizing a feature table will contain the `featureTableJSONByteLength` and `featureTableBinaryByteLength` fields in its header which can be used to extract each respective part of the Feature Table. Code for reading the Feature Table can be found in [Cesium3DTileFeatureTableResources.js](https://github.com/AnalyticalGraphicsInc/cesium/blob/3d-tiles/Source/Scene/Cesium3DTileFeatureTableResources.js) in the Cesium implementation of 3D Tiles. ## JSON Header @@ -37,7 +38,7 @@ Feature table values can be defined in the JSON header in three different ways. * This is common for global semantics like `"INSTANCES_LENGTH"` and `"POINTS_LENGTH"`. 2. A JSON array of values. (e.g. `"POSITION" : [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]`) * Feature values are always stored as a single, flat array, not an array of arrays. Above, each `POSITION` refers to a `float32[3]` data type so there are three features: `Feature 0's position`=`(1.0, 0.0, 0.0)`, `Feature 1's position`=`(0.0, 1.0, 0.0)`, `Feature 2's position`=`(0.0, 0.0, 1.0)`. -3. A reference to data in the binary body, denoated by an object with a `byteOffset` property. (e.g. `"SCALE" : { "byteOffset" : 24` } ) +3. A reference to data in the binary body, denoted by an object with a `byteOffset` property. (e.g. `"SCALE" : { "byteOffset" : 24` } ) * `byteOffset` is always relative to the start of the binary body. The only valid keys in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. @@ -65,7 +66,6 @@ var position = positionArray.subarray(featureId * 3, featureId * 3 + 3); // Usin In JavaScript, a `TypedArray` cannot be created on data unless it is byte-aligned to the data type. For example, a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. -The data types used in 3D Tiles have a maximum length of four bytes, so padding to a multiple of four will work for all cases, since smaller types with lengths of one and two will also be byte-aligned. -If the string generated from the JSON header has a length that is not a multiple of four, it should be padded with space characters in order to ensure that the binary body is byte-aligned. -The binary body should also be padded to a multiple of four when there is data following the Feature Table. +The string generated from the JSON header should be padded with space characters in order to ensure that the binary body is byte-aligned. +The binary body should also be padded if necessary when there is data following the Feature Table. diff --git a/TileFormats/FeatureTable/examples/i3dm.featureTable.json b/TileFormats/FeatureTable/examples/i3dm.featureTable.json new file mode 100644 index 000000000..cc279ffe8 --- /dev/null +++ b/TileFormats/FeatureTable/examples/i3dm.featureTable.json @@ -0,0 +1,14 @@ +{ + "INSTANCES_LENGTH" : 4, + "QUANTIZED_VOLUME_OFFSET" : [-250.0, 0.0, -250.0], + "QUANTIZED_VOLUME_SCALE" : [500.0, 0.0, 500.0], + "POSITION_QUANTIZED" : { + "byteOffset" : 0 + }, + "NORMAL_UP_OCT32P" : { + "byteOffset" : 24 + }, + "NORMAL_RIGHT_OCT32P" : { + "byteOffset" : 40 + } +} \ No newline at end of file diff --git a/TileFormats/FeatureTable/examples/pnts.featureTable.json b/TileFormats/FeatureTable/examples/pnts.featureTable.json new file mode 100644 index 000000000..e7da08d7e --- /dev/null +++ b/TileFormats/FeatureTable/examples/pnts.featureTable.json @@ -0,0 +1,10 @@ +{ + "POINTS_LENGTH" : 4, + "RTC_CENTER" : [1215013.8, -4736316.7, 4081608.4], + "POSITION" : { + "byteOffset" : 0 + }, + "RGB" : { + "byteOffset" : 48 + } +} \ No newline at end of file diff --git a/TileFormats/FeatureTable/figures/feature-table-binary-index.png b/TileFormats/FeatureTable/figures/feature-table-binary-index.png index fbebdb7881e6c2d4bd078660cda9c45b7d2ab8ba..3439ff9e0cc3a0f393d6dfe2a325e3b645a4c271 100644 GIT binary patch literal 7608 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 mNdEsUN~-=}8M?2QNES9_P&ab5k+K#OUA8> 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 diff --git a/TileFormats/FeatureTable/figures/feature-table-layout.png b/TileFormats/FeatureTable/figures/feature-table-layout.png index e887230d273b7ed188b4c0daf852a38d7e1c72d4..966af2831f9d5926b1af5d95da1660ad73f32047 100644 GIT binary patch literal 3483 zcmc(iX*3&Zx5ufss#P@=MaeO=dXy?!Lk%g_(&a5Wn6*Stsv51arjQ&Y#*~_S6lp1H z3{msc5@HUip=yXZNJ9jXNW{&#_r7c0weGj~%llzJ`@g^Jwbx$%_1o)7u(PogIVE?B zkB?8}u9d|DUOwVQsE{Dk>7zXaD`A=Of!K+Q zb0F8}DE;E1$C6s7PZlZ7d0>mgmXJNCc85KP4o%t@pV#!?bBq(A*9VX-hm7g2aDj4i1C-L!E-5KF zm8xRdNn8{YtWA{Gv)=e#oqCeeDpfa(O9oMvIM)S0${w^lNpM3@P9`Cq@_E$CBRII8 zra5BGy=@`^m+@N+JhnO0RO?jBr^eo1!b1w9OG`_wZR~(A=5hMkq==m%kgGBFDkOnf z9WQ9U>@;VtqXU}d9Vk||D#=BUvpRx5bU`c%>HM$py*(T8jX_Re zl{}pRcY7`pN0Q2A=6=~8Ia`kC-E3S+&H)$1y!-2~(!7~p6wT}etgBVSpt+8Qeo~^r z+Ra{oxeu?K_t}m-IxF?qHXv*{l^E*ZR5sYbTgox(){w0^7pGRUIoGs`J9U&#Y*YU?)(nU(!`*m9wQ=YTtyH;21nE`rUVUN+zJXu7DT z260HC9qy9=9Bm%~4Q1(J*ZS`zws9BytD|eIa-6?CV^I*;r*F|UH8sU6PdlJnZ4E-2 zxR2jazsrw4%7mw;@8=W<(9<(83X^WHRuVOZasGZRu=$TH*Usj>rVGLY5tq)_3wR$G z)N9QXj@wP^nV2YKv91Dr_NNC~Up_i>UBt=2x$LK`mrWyGX?x3~7+o@@TI0w7j(S4Z zk{&5(;W<@={eSG55tW2)X@e2t#60uOAWuImdNr?WHp~!raMDV5#BoRSF`}`SMKL42 zH*vOR6BW*q#qV`mC^dM+%aJx37X4S_7Fc97q26aoqS$!rXI;4-sSvPh&-o)aY{uYX zz{tsw^1Dp)YtQyKXQv>THfhd37uBJLWv335VYqv-vWvPG(xZKTRGSsZ6d>+2&v$Fy z>2EFk*4sp9^MlH4k;~@32G-U#HdXp~)JWOqh*&_|ITI=0Onw!<_Y@JgNg*Y>OPf74e@W<+Cw+o5EVisMX}OZVf3SC7R5VH>ZU&j(U_>KJ9w{eZn~_ol@3 zvym3m>u$7Q^kHO^qbe77A6k@D#rhw!C>8W?u^>Ot1}`uR1^tY)6&dCGsxT#q+lxFQ zZO`EhLp(i|-SDAafNS$`aQzuz$040&W;eZgB;rAFJ_Z!hOqvwRFLa48HB*dZ^Y4w= z%t;Hl!{;(hd{>id9k#m6)m9)UhDk*;`PUm4952jX?DP5EbkkcbKR^G9d0-%v@vHRx zti`(&1v%V)?WZ3$KTN}WEyW@^4mg$}j1y|f4v9gV=w15x5`#VX98f>y`8|ccw-izO z0Baro188LA%3M8i4W=*m&3oZ)Vg`ZyC>alCwwC!T4|bgS&Bf@_)4pmWifxNGkYsFy6+0k|4AGEY!8$%m$hla5V7zMe^lpq!*<4{f1 zK=EZS1IrkEb8q3j+Ax5f`b>=#)7TCL^_^zBhb|yH9nW+fB*|#S_AiBy@W~37>osnw zFa`P4nVT(J!)Fw3JgQh%AP#DpQ7yKIU-D?7$qG+QM2y& zkpH`+T9DL#52R0VTN7eUi_pmO+(43MUIwP(o`G$pu6_jf7sfcLuOI;)5P)?Me{?k~ zIejj^@|1t0U|~T*+|J~V+bxs5v4=%jDalKtmVhC;l0!{#UU7vCPqPP8*we(mE^ zF(EF#iiQYM`pRw7n`taaR=YUad&KS{>DisB&l)}Cyn}@Z+FrMnTCsiD6graXrY;Fq zb7J{@l6QUFdA>G}fl#MA>M3vJv}RXk*rYArNMstc1nV}&VqdNX4A}YP#X&AjuVRp# zaL(6uu3V)o_dpcL?pco_bQre878=7$CPOstibmuZ5c_Ron***{qZ_yD8_!Hf9l9(~ zx{Taqz^renq~Hx*(>t&x=`4rh-~ouYt4ZB0qZO4l15qy1A4;6_hnnlNcB*~tpTB}H zqFkUL*(StHONejHGw*ND`3JVP072$OR3x+fljz+j7-KN&>%Eoq1hlURZoYCn3@ zr#yBZoe~Os7OpK{)8F)S_P1>{9--4rRi`Alew`?FLOP+{BxIvg1fXT4Kc0Ipj zuPF}im7Pm~z*g^P53T#+VzsJz>m$MTv=prlV-5ZR)cAgdT!*?5gO2lcU!@mKaJ1e1_!C^?5sV$F9=bWKg| zn`%U4(>K`w@HA|9(l5cUdu1T8j%s#dXSvp^i`z)ztVC3HX@@q&=u62!*R+U#taTIW zcA5goJWo=ECS1AiAeeRyc3*?RWqXP)TrTBnf^yeNwq?MgM-!wOWqfP-gb{<;f7MyDHZKs?o9#U1=sml3Z_4q!vfyEH1LE=E(+P?r_7;v`zfHk z7Q_RmKXh&T^>xzRQVtqVb`I4ti_!409+mb(lAD;U{`Mk1@!Au9X|WURg6ShhP(}#Bt^E>1^W+*xVmxspZaKW^T&EM z_Zsc*vqXSOIKK~|4zUE~ZQ;~Q68{0A>db&UP-@OQB1Kue4;A0t+cp+e<{r`i1z)Ww AEC2ui literal 4033 zcmc&%doy+HZ$Zh1BG)?5Pm2nviJ7tG5TjS0c$tB}5 z8jKp3QHpYnahn-q6uD2tFwB^7?B6-(kF(bA-*e6%@B2Q_yWaJz&%2&yeLm}x?C4-6 zv;X*h003lctj(PO02H+wXYG^N-G@rBle-7#zLS+1P}BFz{BE)baoPSd0KCFTZ~N`t z&87aZzJ4D7l#>2gAn)QMe*%E4n~nKpmna_&3mT*Zlk5Bu=Irr|Fce|$VzyVe;?N=Y z$vtXCAt^smZY1t$cznp)QSGGlSx09B#Zw+7XWSmRCnT72)YT6k3{P}VklrWfUew_t z6yLW1Sd`k!8ECrNr84rip3WNMz506uSY9EbhN?2p@5LMGM9z5Q4V0<5`lJF|02p|q z9={hbPXc6t^k3V=FO}?D0S>8&83X1i|0h?WmjoLeJ4;K;k&KFpie}4Z2b`hn>t*%X zK3wB`F>D?3or#&UXAZ$d1M?`F_u|dp6&_*CqS2K&n*nFEPIa<{&diPluFT<32;VmGX zxd^sSg@6Ls!6vQ(#5=tVI9FcFr{^y>`gsz(DFE_WW*2SgWZ8)qfTGi-n59>p9`u%l zhnGJhcJ_xHt8ENr7xqf;nAKLn=+)YuFZ_bL5fr}k7256Fw@F(bK2sRuRck#+5WXXF zX)L{WG07ni-n9V@U*gKAG+ag^oz(fMrDqahF%1~9sCY`6D-J>@I{o2%W53pKXPR72 zuv%gw!R;B?u?r^vaIZnro@>;jW;} z3jTp%?fb*m^b{rD5rW(}VCHT86!P!kS@pMFNdVvcNF*#dJVxS^TZu%&=C5-5hbbNB1?#psgrh)bxl099tG544>D}CXU zqgeC73^75j4ua&_%<+Q)j;i~YD5=pqA|YKSYX63-K5LkDNxWG!J`l`SN7X#mj%>Q( zm=`+Q88@P6H@QHFqeSLj~cPK9#{*eLF z{S)f6{y|55|M!#t`(%IdH|8&JqgE$nl?*dXDm}CBF5Q?PmR;)t6PriA4a*OlRuI;? z`hG61^4_sKn(rGaE>vCQG_ni5sZMR9mj@Lbb!)`m?>=7jD2K@nye1(}mv@cHH3j)# zgZP%}Dy=so{+@0a^|8|nohZm;hBRrsS*&S#Mk|M^@>r3+r2DgdRgY%`;++?#p6G-m zo!4ZNgS&5L-OOoKcT&N)^bA6-uL~s!v)Sv5FE4l}s_Zdgdcghsp1bPSAOgz~9o>a- zN~V(eVI3wN3Ov4vre1JJh-NAj)k-aU^rPF>`2aV1#ZVDt+8)}YaHuWmi@)2?#O)~O z1K*?}?=CGLo�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 delta 2850 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 Date: Tue, 23 Aug 2016 12:01:50 -0400 Subject: [PATCH 07/34] Finalize v4 schema changes --- .../schema/featureTable.schema.json | 4 +-- .../schema/i3dm.featureTable.schema.json | 8 ++--- .../schema/pnts.featureTable.schema.json | 14 ++++---- schema/asset.schema.json | 9 ++--- schema/boundingVolume.schema.json | 5 +-- schema/properties.schema.json | 12 +++---- schema/tile.content.schema.json | 13 +++---- schema/tile.schema.json | 16 ++++----- schema/tileset.schema.json | 35 +++++++------------ 9 files changed, 55 insertions(+), 61 deletions(-) diff --git a/TileFormats/FeatureTable/schema/featureTable.schema.json b/TileFormats/FeatureTable/schema/featureTable.schema.json index e77e3cdf0..ebb27c3b2 100644 --- a/TileFormats/FeatureTable/schema/featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/featureTable.schema.json @@ -1,7 +1,7 @@ { "$schema" : "http://json-schema.org/draft-04/schema#", - "id" : "featureTable", - "title" : "featureTable", + "id" : "featureTable.schema.json", + "title" : "Feature Table", "type" : "object", "description" : "A set of semantics containing per-tile and per-point values defining custom behavior for features.", "patternProperties" : { diff --git a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json index 90315e6f9..0c565b773 100644 --- a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json @@ -1,12 +1,12 @@ { "$schema" : "http://json-schema.org/draft-04/schema#", - "id" : "i3dm.featureTable", - "title" : "i3dm featureTable", + "id" : "i3dm.featureTable.schema.json", + "title" : "Instanced 3D Model Feature Table", "type" : "object", - "description": "A set of Instanced3DModel semantics containing per-tile and per-point values that define where and how to render instanced models.", + "description": "A set of Instanced 3D Model semantics containing per-tile and per-point values that define where and how to render instanced models.", "allOf" : [ { - "$ref" : "featureTable.schema.json#/featureTable" + "$ref" : "featureTable.schema.json" }, { "properties": { diff --git a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json index 404967055..1ec04f82e 100644 --- a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json @@ -1,12 +1,12 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id" : "pnts.featureTable", - "title": "pnts featureTable", - "type": "object", - "description": "A set of Point Cloud semantics containing per-tile and per-point values that define where and how to render points.", + "$schema" : "http://json-schema.org/draft-04/schema#", + "id" : "pnts.featureTable.schema.json", + "title" : "Point Cloud Feature Table", + "type" : "object", + "description" : "A set of Point Cloud semantics containing per-tile and per-point values that define where and how to render points.", "allOf" : [ { - "$ref" : "featureTable" + "$ref" : "featureTable.schema.json" }, { "properties": { @@ -38,7 +38,7 @@ "POSITION_QUANTIZED": [ "QUANTIZED_VOLUME_OFFSET", "QUANTIZED_VOLUME_SCALE" - ], + ] }, "required": [ "POINTS_LENGTH" diff --git a/schema/asset.schema.json b/schema/asset.schema.json index 1d37bdfd8..150591549 100644 --- a/schema/asset.schema.json +++ b/schema/asset.schema.json @@ -1,18 +1,19 @@ { - "$schema" : "http://json-schema.org/draft-03/schema", - "title" : "asset", + "$schema" : "http://json-schema.org/draft-04/schema", + "id" : "asset.schema.json", + "title" : "Asset", "type" : "object", "description" : "Metadata about the entire tileset.", "properties" : { "version" : { "type" : "string", - "description" : "The 3D Tiles version. The version defines the JSON schema for tileset.json and the base set of tile formats.", - "required" : true + "description" : "The 3D Tiles version. The version defines the JSON schema for tileset.json and the base set of tile formats." }, "tilesetVersion" : { "type" : "string", "description" : "Application-specific version of this tileset, e.g., for when an existing tileset is updated." } }, + "required" : ["version"], "additionalProperties" : false } diff --git a/schema/boundingVolume.schema.json b/schema/boundingVolume.schema.json index 70fac198c..3d32c0db2 100644 --- a/schema/boundingVolume.schema.json +++ b/schema/boundingVolume.schema.json @@ -1,6 +1,7 @@ { - "$schema" : "http://json-schema.org/draft-03/schema", - "title" : "boundingVolume", + "$schema" : "http://json-schema.org/draft-04/schema", + "id" : "boundingVolume.schema.json", + "title" : "Bounding Volume", "type" : "object", "description" : "A bounding volume that encloses a tile or its contents. At least one property is required. If more than one property is defined, the runtime can determine which to use.", "properties" : { diff --git a/schema/properties.schema.json b/schema/properties.schema.json index ace71ed8f..13ebf051c 100644 --- a/schema/properties.schema.json +++ b/schema/properties.schema.json @@ -1,19 +1,19 @@ { - "$schema" : "http://json-schema.org/draft-03/schema", - "title" : "properties", + "$schema" : "http://json-schema.org/draft-04/schema", + "id" : "properties.schema.json", + "title" : "Properties", "type" : "object", "description" : "A dictionary object of metadata about per-feature properties.", "properties" : { "maximum" : { "type" : "number", - "description" : "The maximum value of this property of all the features in the tileset.", - "required" : true + "description" : "The maximum value of this property of all the features in the tileset." }, "minimum" : { "type" : "number", - "description" : "The minimum value of this property of all the features in the tileset.", - "required" : true + "description" : "The minimum value of this property of all the features in the tileset." } }, + "required" : ["maximum", "minimum"], "additionalProperties" : false } diff --git a/schema/tile.content.schema.json b/schema/tile.content.schema.json index d7f52631a..e32dac790 100644 --- a/schema/tile.content.schema.json +++ b/schema/tile.content.schema.json @@ -1,18 +1,19 @@ { - "$schema" : "http://json-schema.org/draft-03/schema", - "title" : "content", + "$schema" : "http://json-schema.org/draft-04/schema", + "id" : "tile.content.schema.json", + "title" : "Tile Content", "type" : "object", "description" : "Metadata about the tile's content and a link to the content.", "properties" : { "boundingVolume" : { - "extends" : { "$ref" : "boundingVolume.schema.json" }, - "description" : "An optional bounding volume that tightly enclosing just the tile's contents. This is used for replacement refinement; tile.boundingVolume provides spatial coherence and tile.content.boundingVolume enables tight view frustum culling. When this is omitted, tile.boundingVolume is used." + "description" : "An optional bounding volume that tightly enclosing just the tile's contents. This is used for replacement refinement; tile.boundingVolume provides spatial coherence and tile.content.boundingVolume enables tight view frustum culling. When this is omitted, tile.boundingVolume is used.", + "$ref" : "boundingVolume.schema.json" }, "url" : { "type" : "string", - "description" : "A string that points to the tile's contents with an absolute or relative url. When the url is relative, it is relative to the referring tileset.json. The file extension of content.url defines the tile format. The core 3D Tiles spec supports the following tile formats: Batched 3D Model (*.b3dm), Instanced 3D Model (*.i3dm), Composite (*.cmpt), and 3D Tiles TileSet (*.json)", - "required" : true + "description" : "A string that points to the tile's contents with an absolute or relative url. When the url is relative, it is relative to the referring tileset.json. The file extension of content.url defines the tile format. The core 3D Tiles spec supports the following tile formats: Batched 3D Model (*.b3dm), Instanced 3D Model (*.i3dm), Composite (*.cmpt), and 3D Tiles TileSet (*.json)" } }, + "required" : ["url"], "additionalProperties" : false } diff --git a/schema/tile.schema.json b/schema/tile.schema.json index 4851f8039..fddc97aa9 100644 --- a/schema/tile.schema.json +++ b/schema/tile.schema.json @@ -1,19 +1,18 @@ { - "$schema" : "http://json-schema.org/draft-03/schema", - "title" : "tile", + "$schema" : "http://json-schema.org/draft-04/schema", + "id" : "tile.schema.json", + "title" : "Tile", "type" : "object", "description" : "A tile in a 3D Tiles tileset.", "properties" : { "boundingVolume" : { - "extends" : { "$ref" : "boundingVolume.schema.json" }, "description" : "The bounding volume that encloses the tile.", - "required" : true + "$ref" : "boundingVolume.schema.json" }, "geometricError" : { "type" : "number", "description" : "The error, in meters, introduced if this tile is rendered and its children are not. At runtime, the geometric error is used to compute Screen-Space Error (SSE), i.e., the error measured in pixels.", - "minimum" : 0, - "required" : true + "minimum" : 0 }, "refine" : { "type" : "string", @@ -21,8 +20,8 @@ "enum" : ["add", "replace"] }, "content" : { - "extends" : { "$ref" : "tile.content.schema.json" }, - "description" : "Metadata about the tile's content and a link to the content. When this is omitted the tile is just used for culling. This is required for leaf tiles." + "description" : "Metadata about the tile's content and a link to the content. When this is omitted the tile is just used for culling. This is required for leaf tiles.", + "$ref" : "tile.content.schema.json" }, "children" : { "type" : "array", @@ -33,5 +32,6 @@ "uniqueItems" : true } }, + "required" : ["boundingVolume", "geometricError"], "additionalProperties" : false } diff --git a/schema/tileset.schema.json b/schema/tileset.schema.json index f945ac997..2737a6bc8 100644 --- a/schema/tileset.schema.json +++ b/schema/tileset.schema.json @@ -1,40 +1,31 @@ { - "$schema" : "http://json-schema.org/draft-03/schema", - "title" : "tileset", + "$schema" : "http://json-schema.org/draft-04/schema", + "id" : "tileset.schema.json", + "title" : "Tileset", "type" : "object", "description" : "A 3D Tiles tileset.", "properties" : { "asset" : { - "type" : "object", - "description" : "Metadata about the entire tileset.", - "properties" : { - }, - "additionalProperties" : { - "$ref" : "asset.schema.json" - }, - "required" : true + "$ref" : "asset.schema.json" }, "properties" : { - "type" : "object", - "description" : "A dictionary object of metadata about per-feature properties.", - "properties" : { - }, - "additionalProperties" : { - "$ref" : "properties.schema.json" - }, - "default" : {} + "description": "A dictionary object of metadata about per-feature properties.", + "patternProperties" : { + ".*": { + "$ref": "properties.schema.json" + } + } }, "geometricError" : { "type" : "number", "description" : "The error, in meters, introduced if this tileset is not rendered. At runtime, the geometric error is used to compute Screen-Space Error (SSE), i.e., the error measured in pixels.", - "minimum" : 0, - "required" : true + "minimum" : 0 }, "root" : { - "extends" : { "$ref" : "tile.schema.json" }, "description" : "The root node.", - "required" : true + "$ref" : "tile.schema.json" } }, + "required" : ["asset", "geometricError", "root"], "additionalProperties" : false } From d627dcb2771c42cae20a2b376f988de33448baeb Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Tue, 23 Aug 2016 12:03:51 -0400 Subject: [PATCH 08/34] Fix a grammatical error in tile.content schema --- schema/tile.content.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/tile.content.schema.json b/schema/tile.content.schema.json index e32dac790..ad00aba84 100644 --- a/schema/tile.content.schema.json +++ b/schema/tile.content.schema.json @@ -6,7 +6,7 @@ "description" : "Metadata about the tile's content and a link to the content.", "properties" : { "boundingVolume" : { - "description" : "An optional bounding volume that tightly enclosing just the tile's contents. This is used for replacement refinement; tile.boundingVolume provides spatial coherence and tile.content.boundingVolume enables tight view frustum culling. When this is omitted, tile.boundingVolume is used.", + "description" : "An optional bounding volume that tightly encloses just the tile's contents. This is used for replacement refinement; tile.boundingVolume provides spatial coherence and tile.content.boundingVolume enables tight view frustum culling. When this is omitted, tile.boundingVolume is used.", "$ref" : "boundingVolume.schema.json" }, "url" : { From d161ccb61178dc8bb37b9849af37af92da3eda06 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Aug 2016 12:41:38 -0400 Subject: [PATCH 09/34] Tweaks --- TileFormats/FeatureTable/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 1133b350c..b37c7d1a2 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -9,9 +9,9 @@ ## Overview -The _Feature Table_ describes position and appearance properties for each feature in a tile, compared to the _Batch Table_ (TODO: link), which contains per-feature application-specific metadata not necessarily used for rendering. +A _Feature Table_ describes position and appearance properties for each feature in a tile. The _Batch Table_ (TODO: link), on the other hand, contains per-feature application-specific metadata not necessarily used for rendering. -The Feature Table is used by the following tile formats: +A Feature Table is used by the following tile formats: * [Instanced 3D Model](../Instanced3DModel) (i3dm) - each model instance is a feature. * [Point Cloud](../PointCloud) (pnts) - each point is a feature. * [Vector](../VectorData) (vctr) - each point/polyline/polygon is a feature. @@ -20,26 +20,26 @@ Per-feature properties are defined using tile-format-specific semantics defined ## Layout -The Feature Table is composed of two parts: a JSON header and an optional binary body. The JSON keys are tile-format-specific semantics, and the values can either be defined directly in the JSON, or refer to sections in the binary body. -The binary body is a tightly packed binary buffer containing data used by the header. It is more efficient to store long numeric arrays in the binary body. +A Feature Table is composed of two parts: a JSON header and an optional binary body. The JSON keys are tile-format-specific semantics, and the values can either be defined directly in the JSON, or refer to sections in the binary body. +The binary body is a binary buffer containing data referenced by the header. It is more efficient to store long numeric arrays in the binary body. **Figure 1**: Feature Table layout ![feature table layout](figures/feature-table-layout.png) -A tile utilizing a feature table will contain the `featureTableJSONByteLength` and `featureTableBinaryByteLength` fields in its header which can be used to extract each respective part of the Feature Table. +When a tile format includes a Feature Table, the Feature Table immediately follows the header. The header will also contain `featureTableJSONByteLength` and `featureTableBinaryByteLength` `uint32` fields, which can be used to extract each respective part of the Feature Table. Code for reading the Feature Table can be found in [Cesium3DTileFeatureTableResources.js](https://github.com/AnalyticalGraphicsInc/cesium/blob/3d-tiles/Source/Scene/Cesium3DTileFeatureTableResources.js) in the Cesium implementation of 3D Tiles. ## JSON Header -Feature table values can be defined in the JSON header in three different ways. +Feature Table values can be represented in the JSON header in three different ways. 1. A single JSON value. (e.g. `"INSTANCES_LENGTH" : 4`) - * This is common for global semantics like `"INSTANCES_LENGTH"` and `"POINTS_LENGTH"`. + * This is common for global semantics like `"INSTANCES_LENGTH"`, which defines the number of model instances in an Instanced 3D Moel tile. 2. A JSON array of values. (e.g. `"POSITION" : [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]`) * Feature values are always stored as a single, flat array, not an array of arrays. Above, each `POSITION` refers to a `float32[3]` data type so there are three features: `Feature 0's position`=`(1.0, 0.0, 0.0)`, `Feature 1's position`=`(0.0, 1.0, 0.0)`, `Feature 2's position`=`(0.0, 0.0, 1.0)`. 3. A reference to data in the binary body, denoted by an object with a `byteOffset` property. (e.g. `"SCALE" : { "byteOffset" : 24` } ) - * `byteOffset` is always relative to the start of the binary body. + * `byteOffset` is a zero-based offset relative to the start of the binary body. The only valid keys in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. @@ -47,7 +47,7 @@ The only valid keys in the JSON header are the defined semantics by the tile for When the JSON header includes a reference to the binary, the provided `byteOffset` is used to index into the data. -**Figure 2**: Indexing into the Feature Table binary +**Figure 2**: Indexing into the Feature Table binary body ![feature table binary index](figures/feature-table-binary-index.png) From 53481cb3115240455191286b1fb9f6c2baf73928 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Aug 2016 12:45:22 -0400 Subject: [PATCH 10/34] Tweak description --- TileFormats/FeatureTable/schema/featureTable.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/schema/featureTable.schema.json b/TileFormats/FeatureTable/schema/featureTable.schema.json index ebb27c3b2..4dafc0cb8 100644 --- a/TileFormats/FeatureTable/schema/featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/featureTable.schema.json @@ -3,7 +3,7 @@ "id" : "featureTable.schema.json", "title" : "Feature Table", "type" : "object", - "description" : "A set of semantics containing per-tile and per-point values defining custom behavior for features.", + "description" : "A set of semantics containing per-tile and per-feature values defining the position and appearance properties for features in a tile.", "patternProperties" : { ".*" : { "properties" : { From eca2eff13567fc38b46d78e65e471404cbef05ac Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Aug 2016 12:51:05 -0400 Subject: [PATCH 11/34] Tweak description --- TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json index 0c565b773..b11fd5fd3 100644 --- a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json @@ -3,7 +3,7 @@ "id" : "i3dm.featureTable.schema.json", "title" : "Instanced 3D Model Feature Table", "type" : "object", - "description": "A set of Instanced 3D Model semantics containing per-tile and per-point values that define where and how to render instanced models.", + "description": "A set of Instanced 3D Model semantics that containper-feature values defining the position and appearance properties for instanced models in a tile.", "allOf" : [ { "$ref" : "featureTable.schema.json" @@ -59,4 +59,4 @@ "additionalProperties": false } ] -} \ No newline at end of file +} From 61dbe26322d530e12086f2191790696dfce71610 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Aug 2016 12:53:04 -0400 Subject: [PATCH 12/34] Tweak description --- TileFormats/FeatureTable/schema/pnts.featureTable.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json index 1ec04f82e..01c6f3e3f 100644 --- a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json @@ -3,7 +3,7 @@ "id" : "pnts.featureTable.schema.json", "title" : "Point Cloud Feature Table", "type" : "object", - "description" : "A set of Point Cloud semantics containing per-tile and per-point values that define where and how to render points.", + "description": "A set of Point Cloud semantics that contains values defining the position and appearance properties for points in a tile.", "allOf" : [ { "$ref" : "featureTable.schema.json" @@ -46,4 +46,4 @@ "additionalProperties": false } ] -} \ No newline at end of file +} From dac1be0c4d7979ff3e52294a4e378d16be59e8a0 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Tue, 23 Aug 2016 12:53:33 -0400 Subject: [PATCH 13/34] Fix typo --- TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json index b11fd5fd3..ace047f8d 100644 --- a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json @@ -3,7 +3,7 @@ "id" : "i3dm.featureTable.schema.json", "title" : "Instanced 3D Model Feature Table", "type" : "object", - "description": "A set of Instanced 3D Model semantics that containper-feature values defining the position and appearance properties for instanced models in a tile.", + "description": "A set of Instanced 3D Model semantics that contains values defining the position and appearance properties for instanced models in a tile.", "allOf" : [ { "$ref" : "featureTable.schema.json" From 21978df3ea5b0c5d82ba6322f5fc271427725db2 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 10:27:00 -0400 Subject: [PATCH 14/34] Schema now contains targets for specific feature types --- .../schema/featureTable.schema.json | 75 ++++++++++- .../schema/i3dm.featureTable.schema.json | 121 ++++++++++-------- .../schema/pnts.featureTable.schema.json | 102 ++++++++------- 3 files changed, 198 insertions(+), 100 deletions(-) diff --git a/TileFormats/FeatureTable/schema/featureTable.schema.json b/TileFormats/FeatureTable/schema/featureTable.schema.json index 4dafc0cb8..ed303d89e 100644 --- a/TileFormats/FeatureTable/schema/featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/featureTable.schema.json @@ -11,8 +11,81 @@ "type" : "integer", "minimum" : 0 } - }, + } + } + }, + "definitions" : { + "featureReference" : { + "type" : "object", "required" : ["byteOffset"] + }, + "featureArray" : { + "type" : "array", + "items" : { + "type" : "number" + } + }, + "feature" : { + "oneOf" : [ + { + "$ref" : "#/definitions/featureReference" + },{ + "$ref" : "#/definitions/featureArray" + } + ] + }, + "featureGlobalScalar" : { + "oneOf" : [ + { + "$ref" : "#/definitions/featureReference" + },{ + "allOf" : [ + { + "$ref" : "#/definitions/featureArray" + },{ + "minItems" : 1, + "maxItems" : 1 + } + ] + },{ + "type" : "integer", + "minimum" : 0 + } + ] + }, + "featureGlobalCartesian3" : { + "oneOf" : [ + { + "$ref" : "#/definitions/featureReference" + }, + { + "allOf" : [ + { + "$ref": "#/definitions/featureArray" + },{ + "minItems" : 3, + "maxItems" : 3 + } + ] + } + ] + }, + "featureGlobalCartesian4" : { + "oneOf" : [ + { + "$ref" : "#/definitions/featureReference" + }, + { + "allOf" : [ + { + "$ref": "#/definitions/featureArray" + },{ + "minItems" : 4, + "maxItems" : 4 + } + ] + } + ] } } } diff --git a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json index ace047f8d..bdf54de37 100644 --- a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json @@ -1,62 +1,71 @@ { - "$schema" : "http://json-schema.org/draft-04/schema#", - "id" : "i3dm.featureTable.schema.json", - "title" : "Instanced 3D Model Feature Table", - "type" : "object", - "description": "A set of Instanced 3D Model semantics that contains values defining the position and appearance properties for instanced models in a tile.", - "allOf" : [ - { - "$ref" : "featureTable.schema.json" - }, - { - "properties": { - "POSITION": {}, - "POSITION_QUANTIZED": {}, - "NORMAL_UP": {}, - "NORMAL_RIGHT": {}, - "NORMAL_UP_OCT32P": {}, - "NORMAL_RIGHT_OCT32P": {}, - "SCALE": {}, - "SCALE_NON_UNIFORM": {}, - "BATCH_ID": {}, - "INSTANCES_LENGTH": {}, - "QUANTIZED_VOLUME_OFFSET": {}, - "QUANTIZED_VOLUME_SCALE": {} - }, - "oneOf": [ + "$schema" : "http://json-schema.org/draft-04/schema#", + "id" : "i3dm.featureTable.schema.json", + "title" : "Instanced 3D Model Feature Table", + "type" : "object", + "description" : "A set of Instanced 3D Model semantics that contains values defining the position and appearance properties for instanced models in a tile.", + "allOf" : [ { - "required": [ - "POSITION" - ] + "$ref" : "featureTable.schema.json" }, { - "required": [ - "POSITION_QUANTIZED" - ] + "properties" : { + "POSITION" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "POSITION_QUANTIZED" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "NORMAL_UP" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "NORMAL_RIGHT" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "NORMAL_UP_OCT32P" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "NORMAL_RIGHT_OCT32P" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "SCALE" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "SCALE_NON_UNIFORM" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "BATCH_ID" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "INSTANCES_LENGTH" : { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalScalar" + }, + "QUANTIZED_VOLUME_OFFSET" : { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" + }, + "QUANTIZED_VOLUME_SCALE": { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" + } + }, + "anyOf" : [ + { + "required" : ["POSITION"] + },{ + "required" : ["POSITION_QUANTIZED"] + } + ], + "dependencies" : { + "POSITION_QUANTIZED" : [ + "QUANTIZED_VOLUME_OFFSET", + "QUANTIZED_VOLUME_SCALE" + ], + "NORMAL_UP" : ["NORMAL_RIGHT"], + "NORMAL_RIGHT" : ["NORMAL_UP"], + "NORMAL_UP_OCT32P" : ["NORMAL_RIGHT_OCT32P"], + "NORMAL_RIGHT_OCT32P" : ["NORMAL_UP_OCT32P"] + }, + "required" : ["INSTANCES_LENGTH"], + "additionalProperties" : false } - ], - "dependencies": { - "POSITION_QUANTIZED": [ - "QUANTIZED_VOLUME_OFFSET", - "QUANTIZED_VOLUME_SCALE" - ], - "NORMAL_UP": [ - "NORMAL_RIGHT" - ], - "NORMAL_RIGHT": [ - "NORMAL_UP" - ], - "NORMAL_UP_OCT32P": [ - "NORMAL_RIGHT_OCT32P" - ], - "NORMAL_RIGHT_OCT32P": [ - "NORMAL_UP_OCT32P" - ] - }, - "required": [ - "INSTANCES_LENGTH" - ], - "additionalProperties": false - } - ] + ] } diff --git a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json index 01c6f3e3f..6a0ee0f57 100644 --- a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json @@ -1,49 +1,65 @@ { - "$schema" : "http://json-schema.org/draft-04/schema#", - "id" : "pnts.featureTable.schema.json", - "title" : "Point Cloud Feature Table", - "type" : "object", - "description": "A set of Point Cloud semantics that contains values defining the position and appearance properties for points in a tile.", - "allOf" : [ - { - "$ref" : "featureTable.schema.json" - }, - { - "properties": { - "POSITION": {}, - "POSITION_QUANTIZED": {}, - "RGBA": {}, - "RGB": {}, - "NORMAL": {}, - "NORMAL_OCT16P": {}, - "POINTS_LENGTH": {}, - "RTC_CENTER": {}, - "QUANTIZED_VOLUME_OFFSET": {}, - "QUANTIZED_VOLUME_SCALE": {}, - "CONSTANT_RGBA": {} - }, - "anyOf": [ + "$schema" : "http://json-schema.org/draft-04/schema#", + "id" : "pnts.featureTable.schema.json", + "title" : "Point Cloud Feature Table", + "type" : "object", + "description" : "A set of Point Cloud semantics that contains values defining the position and appearance properties for points in a tile.", + "allOf" : [ { - "required": [ - "POSITION" - ] + "$ref" : "featureTable.schema.json" }, { - "required": [ - "POSITION_QUANTIZED" - ] + "properties" : { + "POSITION" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "POSITION_QUANTIZED": { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "RGBA" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "RGB" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "NORMAL" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "NORMAL_OCT16P" : { + "$ref" : "featureTable.schema.json#/definitions/feature" + }, + "POINTS_LENGTH" : { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalScalar" + }, + "RTC_CENTER" : { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" + }, + "QUANTIZED_VOLUME_OFFSET" : { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" + }, + "QUANTIZED_VOLUME_SCALE" : { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" + }, + "CONSTANT_RGBA" : { + "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian4" + } + }, + "anyOf" : [ + { + "required" : ["POSITION"] + }, + { + "required" : ["POSITION_QUANTIZED"] + } + ], + "dependencies" : { + "POSITION_QUANTIZED" : [ + "QUANTIZED_VOLUME_OFFSET", + "QUANTIZED_VOLUME_SCALE" + ] + }, + "required" : ["POINTS_LENGTH"], + "additionalProperties": false } - ], - "dependencies": { - "POSITION_QUANTIZED": [ - "QUANTIZED_VOLUME_OFFSET", - "QUANTIZED_VOLUME_SCALE" - ] - }, - "required": [ - "POINTS_LENGTH" - ], - "additionalProperties": false - } - ] + ] } From a05392234e1c6230f15962dc96d4b67a3b18c281 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 10:55:08 -0400 Subject: [PATCH 15/34] Added JSON schema README --- schema/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 schema/README.md diff --git a/schema/README.md b/schema/README.md new file mode 100644 index 000000000..20ef8fa92 --- /dev/null +++ b/schema/README.md @@ -0,0 +1,22 @@ +# 3D Tiles JSON Schema + +## Overview + +3D Tiles JSON assets, like `tileset.json`, or the JSON header of a [Feature Table](../TileFormats/FeatureTable) have certain definition rules they must abide by. +Those rules are expressed in human-readable form throughout the specification, but assets can also be verified using JSON Schema. + +Every JSON asset in 3D Tiles has an accompanying schema document conforming to [JSON Schema](http://json-schema.org/) draft v4. + +## Usage + +For a JSON schema validator, we recommend [Ajv: Another JSON Schema Validator](https://github.com/epoberezkin/ajv). It is fast and has full draft v4 support. +A command line interface tool for node is available on [npm](https://www.npmjs.com/package/ajv-cli). + +### Example + +1. Install : `npm install ajv-cli -g` +2. Validate : `ajv -s schema/i3dm.featureTable.schema.json -r schema/featureTable.schema.json -d examples/i3dm.featureTable.json` + +The `-s` flag should point to the schema you want to use for validation. +Multiple `-r` flags should be used to include any external dependencies for the schema. +The `-d` flag should point to the JSON you wish to validate. \ No newline at end of file From 662462f5c0fe4a1cfd439f3dec6e75b3b56a1715 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 10:56:17 -0400 Subject: [PATCH 16/34] Edits --- schema/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/README.md b/schema/README.md index 20ef8fa92..6d90a7567 100644 --- a/schema/README.md +++ b/schema/README.md @@ -2,7 +2,7 @@ ## Overview -3D Tiles JSON assets, like `tileset.json`, or the JSON header of a [Feature Table](../TileFormats/FeatureTable) have certain definition rules they must abide by. +3D Tiles JSON assets, like `tileset.json`, or the JSON header of a [Feature Table](../TileFormats/FeatureTable) have rules. Those rules are expressed in human-readable form throughout the specification, but assets can also be verified using JSON Schema. Every JSON asset in 3D Tiles has an accompanying schema document conforming to [JSON Schema](http://json-schema.org/) draft v4. From e362488ad22048327d4bb29cc94c6d9358ef0e94 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Wed, 24 Aug 2016 10:58:51 -0400 Subject: [PATCH 17/34] Tweaks --- TileFormats/FeatureTable/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index b37c7d1a2..a69bf34c9 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -16,31 +16,31 @@ A Feature Table is used by the following tile formats: * [Point Cloud](../PointCloud) (pnts) - each point is a feature. * [Vector](../VectorData) (vctr) - each point/polyline/polygon is a feature. -Per-feature properties are defined using tile-format-specific semantics defined in each tile format's specification. For example, in _Instanced 3D Model_, `SCALE_NON_UNIFORM` defines the non-uniform scale applied to each instance. +Per-feature properties are defined using tile-format-specific semantics defined in each tile format's specification. For example, in _Instanced 3D Model_, `SCALE_NON_UNIFORM` defines the non-uniform scale applied to each 3D model instance. ## Layout -A Feature Table is composed of two parts: a JSON header and an optional binary body. The JSON keys are tile-format-specific semantics, and the values can either be defined directly in the JSON, or refer to sections in the binary body. -The binary body is a binary buffer containing data referenced by the header. It is more efficient to store long numeric arrays in the binary body. +A Feature Table is composed of two parts: a JSON header and an optional binary body. The JSON property names are tile-format-specific semantics, and their values can either be defined directly in the JSON, or refer to sections in the binary body. It is more efficient to store long numeric arrays in the binary body. **Figure 1**: Feature Table layout ![feature table layout](figures/feature-table-layout.png) -When a tile format includes a Feature Table, the Feature Table immediately follows the header. The header will also contain `featureTableJSONByteLength` and `featureTableBinaryByteLength` `uint32` fields, which can be used to extract each respective part of the Feature Table. +When a tile format includes a Feature Table, the Feature Table immediately follows the tile's header. The header will also contain `featureTableJSONByteLength` and `featureTableBinaryByteLength` `uint32` fields, which can be used to extract each respective part of the Feature Table. + Code for reading the Feature Table can be found in [Cesium3DTileFeatureTableResources.js](https://github.com/AnalyticalGraphicsInc/cesium/blob/3d-tiles/Source/Scene/Cesium3DTileFeatureTableResources.js) in the Cesium implementation of 3D Tiles. ## JSON Header Feature Table values can be represented in the JSON header in three different ways. -1. A single JSON value. (e.g. `"INSTANCES_LENGTH" : 4`) - * This is common for global semantics like `"INSTANCES_LENGTH"`, which defines the number of model instances in an Instanced 3D Moel tile. -2. A JSON array of values. (e.g. `"POSITION" : [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]`) - * Feature values are always stored as a single, flat array, not an array of arrays. Above, each `POSITION` refers to a `float32[3]` data type so there are three features: `Feature 0's position`=`(1.0, 0.0, 0.0)`, `Feature 1's position`=`(0.0, 1.0, 0.0)`, `Feature 2's position`=`(0.0, 0.0, 1.0)`. -3. A reference to data in the binary body, denoted by an object with a `byteOffset` property. (e.g. `"SCALE" : { "byteOffset" : 24` } ) - * `byteOffset` is a zero-based offset relative to the start of the binary body. - +1. A single value or object. (e.g. `"INSTANCES_LENGTH" : 4`) + * This is used for global semantics like `"INSTANCES_LENGTH"`, which defines the number of model instances in an Instanced 3D Model tile. +2. An array of values. (e.g. `"POSITION" : [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]`) + * This is used for per-feature semantics like `"POSITION"` in Instanced 3D Model. Above, each `POSITION` refers to a `float32[3]` data type so there are three features: `Feature 0's position`=`(1.0, 0.0, 0.0)`, `Feature 1's position`=`(0.0, 1.0, 0.0)`, `Feature 2's position`=`(0.0, 0.0, 1.0)`. +3. A reference to data in the binary body, denoted by an object with a `byteOffset` property. (e.g. `"SCALE" : { "byteOffset" : 24}`). + * `byteOffset` is a zero-based offset relative to the start of the binary body. + * The semantic defines the allowed data type, e.g., when `"POSITION"` in Instanced Model refers to the binary body, the component type is `float32` and the number of components is `3`. The only valid keys in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. ## Binary Body @@ -51,7 +51,7 @@ When the JSON header includes a reference to the binary, the provided `byteOffse ![feature table binary index](figures/feature-table-binary-index.png) -Values can be retrieved using the number of features, `featuresLength`, the desired feature id, `featureId`, and the data type for the feature semantic. +Values can be retrieved using the number of features, `featuresLength`, the desired feature id, `featureId`, and the data type (component type and number of components) for the feature semantic. For example, using the `POSITION` semantic, which has a `float32[3]` data type: From ae362a2feafe9c10d995b5cf0601f122a6946d09 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Wed, 24 Aug 2016 10:59:54 -0400 Subject: [PATCH 18/34] Rename --- TileFormats/FeatureTable/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index a69bf34c9..fc7bf49fb 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -41,7 +41,7 @@ Feature Table values can be represented in the JSON header in three different wa 3. A reference to data in the binary body, denoted by an object with a `byteOffset` property. (e.g. `"SCALE" : { "byteOffset" : 24}`). * `byteOffset` is a zero-based offset relative to the start of the binary body. * The semantic defines the allowed data type, e.g., when `"POSITION"` in Instanced Model refers to the binary body, the component type is `float32` and the number of components is `3`. -The only valid keys in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. +The only valid properties in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. ## Binary Body From fe8c394e907594d75526694ae51fcf56bc63618f Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Wed, 24 Aug 2016 11:10:04 -0400 Subject: [PATCH 19/34] Whitespace --- TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json index bdf54de37..272ff7a67 100644 --- a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json @@ -27,7 +27,7 @@ }, "NORMAL_RIGHT_OCT32P" : { "$ref" : "featureTable.schema.json#/definitions/feature" - }, + }, "SCALE" : { "$ref" : "featureTable.schema.json#/definitions/feature" }, From 06b1305d1a9da55e6d20518011ba4140d862b28a Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Wed, 24 Aug 2016 11:24:56 -0400 Subject: [PATCH 20/34] Edit validation instructions --- schema/README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/schema/README.md b/schema/README.md index 6d90a7567..575b0a3f1 100644 --- a/schema/README.md +++ b/schema/README.md @@ -1,22 +1,18 @@ # 3D Tiles JSON Schema -## Overview - -3D Tiles JSON assets, like `tileset.json`, or the JSON header of a [Feature Table](../TileFormats/FeatureTable) have rules. -Those rules are expressed in human-readable form throughout the specification, but assets can also be verified using JSON Schema. - -Every JSON asset in 3D Tiles has an accompanying schema document conforming to [JSON Schema](http://json-schema.org/) draft v4. +Parts of 3D Tiles, such as `tileset.json and the [Feature Table](../TileFormats/FeatureTable) header, are represented with JSON. The JSON schema is defined using [JSON Schema](http://json-schema.org/) draft v4 in schema subdirectories. ## Usage -For a JSON schema validator, we recommend [Ajv: Another JSON Schema Validator](https://github.com/epoberezkin/ajv). It is fast and has full draft v4 support. -A command line interface tool for node is available on [npm](https://www.npmjs.com/package/ajv-cli). +A JSON object can be validated against the schema using a JSON schema validator such as [Ajv: Another JSON Schema Validator](https://github.com/epoberezkin/ajv), which supports JSON Schema draft v4. A command-line tool is available on npm as [ajv-cli](https://www.npmjs.com/package/ajv-cli). + +Validating against the schema does not prove full compliance with the 3D Tiles specification since not all requirements can be represented with JSON schema. For full compliance validation, see [3d-tiles-tools](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/). ### Example 1. Install : `npm install ajv-cli -g` 2. Validate : `ajv -s schema/i3dm.featureTable.schema.json -r schema/featureTable.schema.json -d examples/i3dm.featureTable.json` -The `-s` flag should point to the schema you want to use for validation. -Multiple `-r` flags should be used to include any external dependencies for the schema. -The `-d` flag should point to the JSON you wish to validate. \ No newline at end of file +* The `-s` flag points to the schema you want to use for validation. +* Multiple `-r` flags includes any external dependencies for the schema. +* The `-d` flag points to the JSON to validate. From 519f006144812db86e15134df122ab66110f0740 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 11:27:20 -0400 Subject: [PATCH 21/34] Updated feature table schema --- .../schema/featureTable.schema.json | 91 ++++++-------- .../schema/i3dm.featureTable.schema.json | 119 +++++++++--------- .../schema/pnts.featureTable.schema.json | 106 ++++++++-------- 3 files changed, 146 insertions(+), 170 deletions(-) diff --git a/TileFormats/FeatureTable/schema/featureTable.schema.json b/TileFormats/FeatureTable/schema/featureTable.schema.json index ed303d89e..ccdb4035a 100644 --- a/TileFormats/FeatureTable/schema/featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/featureTable.schema.json @@ -15,54 +15,46 @@ } }, "definitions" : { - "featureReference" : { + "binaryReference" : { "type" : "object", "required" : ["byteOffset"] }, - "featureArray" : { + "numericArray" : { "type" : "array", "items" : { "type" : "number" } }, - "feature" : { - "oneOf" : [ - { - "$ref" : "#/definitions/featureReference" - },{ - "$ref" : "#/definitions/featureArray" + "perFeaturePropertyNumeric" : { + "oneOf" : [{ + "$ref" : "#/definitions/binaryReference" + }, { + "$ref" : "#/definitions/numericArray" } ] }, - "featureGlobalScalar" : { - "oneOf" : [ - { - "$ref" : "#/definitions/featureReference" - },{ - "allOf" : [ - { - "$ref" : "#/definitions/featureArray" - },{ - "minItems" : 1, - "maxItems" : 1 - } - ] - },{ - "type" : "integer", - "minimum" : 0 - } - ] + "globalPropertyScalar" : { + "oneOf" : [{ + "$ref" : "#/definitions/binaryReference" + }, { + "allOf" : [{ + "$ref" : "#/definitions/numericArray" + }, { + "minItems" : 1, + "maxItems" : 1 + }] + }, { + "type" : "integer", + "minimum" : 0 + }] }, - "featureGlobalCartesian3" : { - "oneOf" : [ - { - "$ref" : "#/definitions/featureReference" - }, - { - "allOf" : [ - { - "$ref": "#/definitions/featureArray" - },{ + "globalPropertyCartesian3" : { + "oneOf" : [{ + "$ref" : "#/definitions/binaryReference" + }, { + "allOf" : [{ + "$ref": "#/definitions/numericArray" + }, { "minItems" : 3, "maxItems" : 3 } @@ -70,22 +62,17 @@ } ] }, - "featureGlobalCartesian4" : { - "oneOf" : [ - { - "$ref" : "#/definitions/featureReference" - }, - { - "allOf" : [ - { - "$ref": "#/definitions/featureArray" - },{ - "minItems" : 4, - "maxItems" : 4 - } - ] - } - ] + "globalPropertyCartesian4" : { + "oneOf" : [{ + "$ref" : "#/definitions/binaryReference" + }, { + "allOf": [{ + "$ref": "#/definitions/numericArray" + }, { + "minItems": 4, + "maxItems": 4 + }] + }] } } } diff --git a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json index 272ff7a67..a9741073b 100644 --- a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json @@ -4,68 +4,63 @@ "title" : "Instanced 3D Model Feature Table", "type" : "object", "description" : "A set of Instanced 3D Model semantics that contains values defining the position and appearance properties for instanced models in a tile.", - "allOf" : [ - { - "$ref" : "featureTable.schema.json" - }, - { - "properties" : { - "POSITION" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "POSITION_QUANTIZED" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "NORMAL_UP" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "NORMAL_RIGHT" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "NORMAL_UP_OCT32P" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "NORMAL_RIGHT_OCT32P" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "SCALE" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "SCALE_NON_UNIFORM" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "BATCH_ID" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "INSTANCES_LENGTH" : { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalScalar" - }, - "QUANTIZED_VOLUME_OFFSET" : { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" - }, - "QUANTIZED_VOLUME_SCALE": { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" - } + "allOf" : [{ + "$ref" : "featureTable.schema.json" + }, { + "properties" : { + "POSITION" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" }, - "anyOf" : [ - { - "required" : ["POSITION"] - },{ - "required" : ["POSITION_QUANTIZED"] - } - ], - "dependencies" : { - "POSITION_QUANTIZED" : [ - "QUANTIZED_VOLUME_OFFSET", - "QUANTIZED_VOLUME_SCALE" - ], - "NORMAL_UP" : ["NORMAL_RIGHT"], - "NORMAL_RIGHT" : ["NORMAL_UP"], - "NORMAL_UP_OCT32P" : ["NORMAL_RIGHT_OCT32P"], - "NORMAL_RIGHT_OCT32P" : ["NORMAL_UP_OCT32P"] + "POSITION_QUANTIZED" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "NORMAL_UP" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "NORMAL_RIGHT" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "NORMAL_UP_OCT32P" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "NORMAL_RIGHT_OCT32P" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "SCALE" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" }, - "required" : ["INSTANCES_LENGTH"], - "additionalProperties" : false - } - ] + "SCALE_NON_UNIFORM" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "BATCH_ID" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "INSTANCES_LENGTH" : { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyScalar" + }, + "QUANTIZED_VOLUME_OFFSET" : { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyCartesian3" + }, + "QUANTIZED_VOLUME_SCALE": { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyCartesian3" + } + }, + "anyOf" : [{ + "required" : ["POSITION"] + }, { + "required" : ["POSITION_QUANTIZED"] + }], + "dependencies" : { + "POSITION_QUANTIZED" : [ + "QUANTIZED_VOLUME_OFFSET", + "QUANTIZED_VOLUME_SCALE" + ], + "NORMAL_UP" : ["NORMAL_RIGHT"], + "NORMAL_RIGHT" : ["NORMAL_UP"], + "NORMAL_UP_OCT32P" : ["NORMAL_RIGHT_OCT32P"], + "NORMAL_RIGHT_OCT32P" : ["NORMAL_UP_OCT32P"] + }, + "required" : ["INSTANCES_LENGTH"], + "additionalProperties" : false + }] } diff --git a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json index 6a0ee0f57..cac653b04 100644 --- a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json @@ -4,62 +4,56 @@ "title" : "Point Cloud Feature Table", "type" : "object", "description" : "A set of Point Cloud semantics that contains values defining the position and appearance properties for points in a tile.", - "allOf" : [ - { - "$ref" : "featureTable.schema.json" - }, - { - "properties" : { - "POSITION" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "POSITION_QUANTIZED": { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "RGBA" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "RGB" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "NORMAL" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "NORMAL_OCT16P" : { - "$ref" : "featureTable.schema.json#/definitions/feature" - }, - "POINTS_LENGTH" : { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalScalar" - }, - "RTC_CENTER" : { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" - }, - "QUANTIZED_VOLUME_OFFSET" : { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" - }, - "QUANTIZED_VOLUME_SCALE" : { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian3" - }, - "CONSTANT_RGBA" : { - "$ref" : "featureTable.schema.json#/definitions/featureGlobalCartesian4" - } + "allOf" : [{ + "$ref" : "featureTable.schema.json" + }, { + "properties" : { + "POSITION" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "POSITION_QUANTIZED": { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "RGBA" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "RGB" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "NORMAL" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + }, + "NORMAL_OCT16P" : { + "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" }, - "anyOf" : [ - { - "required" : ["POSITION"] - }, - { - "required" : ["POSITION_QUANTIZED"] - } - ], - "dependencies" : { - "POSITION_QUANTIZED" : [ - "QUANTIZED_VOLUME_OFFSET", - "QUANTIZED_VOLUME_SCALE" - ] + "POINTS_LENGTH" : { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyScalar" }, - "required" : ["POINTS_LENGTH"], - "additionalProperties": false - } - ] + "RTC_CENTER" : { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyCartesian3" + }, + "QUANTIZED_VOLUME_OFFSET" : { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyCartesian3" + }, + "QUANTIZED_VOLUME_SCALE" : { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyCartesian3" + }, + "CONSTANT_RGBA" : { + "$ref" : "featureTable.schema.json#/definitions/globalPropertyCartesian3" + } + }, + "anyOf" : [{ + "required" : ["POSITION"] + }, { + "required" : ["POSITION_QUANTIZED"] + }], + "dependencies" : { + "POSITION_QUANTIZED" : [ + "QUANTIZED_VOLUME_OFFSET", + "QUANTIZED_VOLUME_SCALE" + ] + }, + "required" : ["POINTS_LENGTH"], + "additionalProperties": false + }] } From 024a45efbcc4384343c9f567a8da8454ad75d125 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Wed, 24 Aug 2016 11:27:25 -0400 Subject: [PATCH 22/34] Fix link --- schema/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/README.md b/schema/README.md index 575b0a3f1..9faaafb4b 100644 --- a/schema/README.md +++ b/schema/README.md @@ -1,6 +1,6 @@ # 3D Tiles JSON Schema -Parts of 3D Tiles, such as `tileset.json and the [Feature Table](../TileFormats/FeatureTable) header, are represented with JSON. The JSON schema is defined using [JSON Schema](http://json-schema.org/) draft v4 in schema subdirectories. +Parts of 3D Tiles, such as [tileset.json](../#tilesetjson) and the [Feature Table](../TileFormats/FeatureTable) header, are represented with JSON. The JSON schema is defined using [JSON Schema](http://json-schema.org/) draft v4 in schema subdirectories. ## Usage From f46d4add1b17c534321a757087ac439085e021b3 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 11:29:08 -0400 Subject: [PATCH 23/34] Whitespace fix --- .../schema/featureTable.schema.json | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/TileFormats/FeatureTable/schema/featureTable.schema.json b/TileFormats/FeatureTable/schema/featureTable.schema.json index ccdb4035a..02251584f 100644 --- a/TileFormats/FeatureTable/schema/featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/featureTable.schema.json @@ -50,17 +50,15 @@ }, "globalPropertyCartesian3" : { "oneOf" : [{ - "$ref" : "#/definitions/binaryReference" + "$ref" : "#/definitions/binaryReference" + }, { + "allOf" : [{ + "$ref": "#/definitions/numericArray" }, { - "allOf" : [{ - "$ref": "#/definitions/numericArray" - }, { - "minItems" : 3, - "maxItems" : 3 - } - ] - } - ] + "minItems" : 3, + "maxItems" : 3 + }] + }] }, "globalPropertyCartesian4" : { "oneOf" : [{ From 6c0cfc9e379f6636f5bd49d4f679f0d06c5a57af Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 11:31:00 -0400 Subject: [PATCH 24/34] Replace binaryReference with binaryBodyReference --- .../FeatureTable/schema/featureTable.schema.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TileFormats/FeatureTable/schema/featureTable.schema.json b/TileFormats/FeatureTable/schema/featureTable.schema.json index 02251584f..b5d26b32c 100644 --- a/TileFormats/FeatureTable/schema/featureTable.schema.json +++ b/TileFormats/FeatureTable/schema/featureTable.schema.json @@ -15,7 +15,7 @@ } }, "definitions" : { - "binaryReference" : { + "binaryBodyReference" : { "type" : "object", "required" : ["byteOffset"] }, @@ -27,7 +27,7 @@ }, "perFeaturePropertyNumeric" : { "oneOf" : [{ - "$ref" : "#/definitions/binaryReference" + "$ref" : "#/definitions/binaryBodyReference" }, { "$ref" : "#/definitions/numericArray" } @@ -35,7 +35,7 @@ }, "globalPropertyScalar" : { "oneOf" : [{ - "$ref" : "#/definitions/binaryReference" + "$ref" : "#/definitions/binaryBodyReference" }, { "allOf" : [{ "$ref" : "#/definitions/numericArray" @@ -50,7 +50,7 @@ }, "globalPropertyCartesian3" : { "oneOf" : [{ - "$ref" : "#/definitions/binaryReference" + "$ref" : "#/definitions/binaryBodyReference" }, { "allOf" : [{ "$ref": "#/definitions/numericArray" @@ -62,7 +62,7 @@ }, "globalPropertyCartesian4" : { "oneOf" : [{ - "$ref" : "#/definitions/binaryReference" + "$ref" : "#/definitions/binaryBodyReference" }, { "allOf": [{ "$ref": "#/definitions/numericArray" From ec3927a2b26ae13e1f40dadb090bd953be2bd53b Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 11:55:40 -0400 Subject: [PATCH 25/34] Moved schema and examples out to global folders --- TileFormats/FeatureTable/README.md | 2 ++ TileFormats/Instanced3DModel/README.md | 2 ++ TileFormats/PointCloud/README.md | 2 ++ .../FeatureTable/examples => examples}/i3dm.featureTable.json | 0 .../FeatureTable/examples => examples}/pnts.featureTable.json | 0 .../FeatureTable/schema => schema}/featureTable.schema.json | 0 .../schema => schema}/i3dm.featureTable.schema.json | 0 .../schema => schema}/pnts.featureTable.schema.json | 0 8 files changed, 6 insertions(+) rename {TileFormats/FeatureTable/examples => examples}/i3dm.featureTable.json (100%) rename {TileFormats/FeatureTable/examples => examples}/pnts.featureTable.json (100%) rename {TileFormats/FeatureTable/schema => schema}/featureTable.schema.json (100%) rename {TileFormats/FeatureTable/schema => schema}/i3dm.featureTable.schema.json (100%) rename {TileFormats/FeatureTable/schema => schema}/pnts.featureTable.schema.json (100%) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index fc7bf49fb..68c9b5f32 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -43,6 +43,8 @@ Feature Table values can be represented in the JSON header in three different wa * The semantic defines the allowed data type, e.g., when `"POSITION"` in Instanced Model refers to the binary body, the component type is `float32` and the number of components is `3`. The only valid properties in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. +JSON Schema Feature Table definitions can be found in [featureTable.schema.json](../../../schema/featureTable.schema.json) + ## Binary Body When the JSON header includes a reference to the binary, the provided `byteOffset` is used to index into the data. diff --git a/TileFormats/Instanced3DModel/README.md b/TileFormats/Instanced3DModel/README.md index 741214f7d..1747631c8 100644 --- a/TileFormats/Instanced3DModel/README.md +++ b/TileFormats/Instanced3DModel/README.md @@ -52,6 +52,8 @@ in the Cesium implementation of 3D Tiles. Contains values for `i3dm` semantics used to create instanced models. More information is available in the [Feature Table specification](../FeatureTable). +The `i3dm` Feature Table JSON Schema is defined in [i3dm.featureTable.schema.json](../../../schema/i3dm.featureTable.schema.json). + ### Semantics #### Instance Semantics diff --git a/TileFormats/PointCloud/README.md b/TileFormats/PointCloud/README.md index 83c71e9bc..f43410625 100644 --- a/TileFormats/PointCloud/README.md +++ b/TileFormats/PointCloud/README.md @@ -44,6 +44,8 @@ Code for reading the header can be found in [Points3DModelTileContent.js](https: Contains per-tile and per-point values that define where and how to render points. More information is available in the [Feature Table specification](../FeatureTable). +The `pnts` Feature Table JSON Schema is defined in [pnts.featureTable.schema.json](../../../schema/pnts.featureTable.schema.json) + ### Semantics #### Point Semantics diff --git a/TileFormats/FeatureTable/examples/i3dm.featureTable.json b/examples/i3dm.featureTable.json similarity index 100% rename from TileFormats/FeatureTable/examples/i3dm.featureTable.json rename to examples/i3dm.featureTable.json diff --git a/TileFormats/FeatureTable/examples/pnts.featureTable.json b/examples/pnts.featureTable.json similarity index 100% rename from TileFormats/FeatureTable/examples/pnts.featureTable.json rename to examples/pnts.featureTable.json diff --git a/TileFormats/FeatureTable/schema/featureTable.schema.json b/schema/featureTable.schema.json similarity index 100% rename from TileFormats/FeatureTable/schema/featureTable.schema.json rename to schema/featureTable.schema.json diff --git a/TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json b/schema/i3dm.featureTable.schema.json similarity index 100% rename from TileFormats/FeatureTable/schema/i3dm.featureTable.schema.json rename to schema/i3dm.featureTable.schema.json diff --git a/TileFormats/FeatureTable/schema/pnts.featureTable.schema.json b/schema/pnts.featureTable.schema.json similarity index 100% rename from TileFormats/FeatureTable/schema/pnts.featureTable.schema.json rename to schema/pnts.featureTable.schema.json From e81a934209448f6f4fff15f23820fbe3f050aed9 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 11:58:08 -0400 Subject: [PATCH 26/34] Fixed relative paths --- TileFormats/FeatureTable/README.md | 2 +- TileFormats/Instanced3DModel/README.md | 2 +- TileFormats/PointCloud/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 68c9b5f32..0250840f5 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -43,7 +43,7 @@ Feature Table values can be represented in the JSON header in three different wa * The semantic defines the allowed data type, e.g., when `"POSITION"` in Instanced Model refers to the binary body, the component type is `float32` and the number of components is `3`. The only valid properties in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. -JSON Schema Feature Table definitions can be found in [featureTable.schema.json](../../../schema/featureTable.schema.json) +JSON Schema Feature Table definitions can be found in [featureTable.schema.json](../../schema/featureTable.schema.json) ## Binary Body diff --git a/TileFormats/Instanced3DModel/README.md b/TileFormats/Instanced3DModel/README.md index 1747631c8..9f9ae253b 100644 --- a/TileFormats/Instanced3DModel/README.md +++ b/TileFormats/Instanced3DModel/README.md @@ -52,7 +52,7 @@ in the Cesium implementation of 3D Tiles. Contains values for `i3dm` semantics used to create instanced models. More information is available in the [Feature Table specification](../FeatureTable). -The `i3dm` Feature Table JSON Schema is defined in [i3dm.featureTable.schema.json](../../../schema/i3dm.featureTable.schema.json). +The `i3dm` Feature Table JSON Schema is defined in [i3dm.featureTable.schema.json](../../schema/i3dm.featureTable.schema.json). ### Semantics diff --git a/TileFormats/PointCloud/README.md b/TileFormats/PointCloud/README.md index f43410625..a48cd53d3 100644 --- a/TileFormats/PointCloud/README.md +++ b/TileFormats/PointCloud/README.md @@ -44,7 +44,7 @@ Code for reading the header can be found in [Points3DModelTileContent.js](https: Contains per-tile and per-point values that define where and how to render points. More information is available in the [Feature Table specification](../FeatureTable). -The `pnts` Feature Table JSON Schema is defined in [pnts.featureTable.schema.json](../../../schema/pnts.featureTable.schema.json) +The `pnts` Feature Table JSON Schema is defined in [pnts.featureTable.schema.json](../../schema/pnts.featureTable.schema.json) ### Semantics From 18dabade8062bc3f1a31367fe64b0f079633433d Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Wed, 24 Aug 2016 11:59:20 -0400 Subject: [PATCH 27/34] Missing periods --- TileFormats/FeatureTable/README.md | 2 +- TileFormats/PointCloud/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 0250840f5..e99e24ccc 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -43,7 +43,7 @@ Feature Table values can be represented in the JSON header in three different wa * The semantic defines the allowed data type, e.g., when `"POSITION"` in Instanced Model refers to the binary body, the component type is `float32` and the number of components is `3`. The only valid properties in the JSON header are the defined semantics by the tile format. Application-specific data should be stored in the Batch Table. -JSON Schema Feature Table definitions can be found in [featureTable.schema.json](../../schema/featureTable.schema.json) +JSON Schema Feature Table definitions can be found in [featureTable.schema.json](../../schema/featureTable.schema.json). ## Binary Body diff --git a/TileFormats/PointCloud/README.md b/TileFormats/PointCloud/README.md index a48cd53d3..bada36f59 100644 --- a/TileFormats/PointCloud/README.md +++ b/TileFormats/PointCloud/README.md @@ -44,7 +44,7 @@ Code for reading the header can be found in [Points3DModelTileContent.js](https: Contains per-tile and per-point values that define where and how to render points. More information is available in the [Feature Table specification](../FeatureTable). -The `pnts` Feature Table JSON Schema is defined in [pnts.featureTable.schema.json](../../schema/pnts.featureTable.schema.json) +The `pnts` Feature Table JSON Schema is defined in [pnts.featureTable.schema.json](../../schema/pnts.featureTable.schema.json). ### Semantics From 702cbcc35c50e7068d763f519aa07f5e38c15930 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Thu, 25 Aug 2016 10:59:02 -0400 Subject: [PATCH 28/34] Per-feature properties for i3dm and pnts must be binary body references --- TileFormats/Instanced3DModel/README.md | 3 ++- TileFormats/PointCloud/README.md | 1 + schema/featureTable.schema.json | 8 -------- schema/i3dm.featureTable.schema.json | 18 +++++++++--------- schema/pnts.featureTable.schema.json | 12 ++++++------ 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/TileFormats/Instanced3DModel/README.md b/TileFormats/Instanced3DModel/README.md index 9f9ae253b..519ccbde2 100644 --- a/TileFormats/Instanced3DModel/README.md +++ b/TileFormats/Instanced3DModel/README.md @@ -59,13 +59,14 @@ The `i3dm` Feature Table JSON Schema is defined in [i3dm.featureTable.schema.jso #### Instance Semantics These semantics map to an array of feature values that are used to create instances. The length of these arrays must be the same for all semantics and is equal to the number of instances. +The value for each instance semantic must be a reference to the Feature Table Binary Body; they cannot be embedded in the Feature Table JSON Header. If a semantic has a dependency on another semantic, that semantic must be defined. If both `SCALE` and `SCALE_NON_UNIFORM` are defined for an instance, both scaling operations will be applied. If both `POSITION` and `POSITION_QUANTIZED` are defined for an instance, the higher precision `POSITION` will be used. If `NORMAL_UP`, `NORMAL_RIGHT`, `NORMAL_UP_OCT32P`, and `NORMAL_RIGHT_OCT32P` are defined for an instance, the higher precision `NORMAL_UP`, and `NORMAL_RIGHT` will be used. -| Semantic | Data Type | Description | Required | +| Semantic | Data Type | Description | Required | | --- | --- | --- | --- | --- | | `POSITION` | `float32[3]` | A 3-component array of numbers containing `x`, `y`, and `z` Cartesian coordinates for the position of the instance. | :white_check_mark: Yes, unless `POSITION_QUANTIZED` is defined. | | `POSITION_QUANTIZED` | `uint16[3]` | A 3-component array of numbers containing `x`, `y`, and `z` in quantized Cartesian coordinates for the position of the instance. | :white_check_mark: Yes, unless `POSITION` is defined. | diff --git a/TileFormats/PointCloud/README.md b/TileFormats/PointCloud/README.md index bada36f59..d566a4ef5 100644 --- a/TileFormats/PointCloud/README.md +++ b/TileFormats/PointCloud/README.md @@ -51,6 +51,7 @@ The `pnts` Feature Table JSON Schema is defined in [pnts.featureTable.schema.jso #### Point Semantics These semantics map to an array of feature values that are define each point. The length of these arrays must be the same for all semantics and is equal to the number of points. +The value for each point semantic must be a reference to the Feature Table Binary Body; they cannot be embedded in the Feature Table JSON Header. If a semantic has a dependency on another semantic, that semantic must be defined. If both `POSITION` and `POSITION_QUANTIZED` are defined for a point, the higher precision `POSITION` will be used. diff --git a/schema/featureTable.schema.json b/schema/featureTable.schema.json index b5d26b32c..96c3a57c2 100644 --- a/schema/featureTable.schema.json +++ b/schema/featureTable.schema.json @@ -25,14 +25,6 @@ "type" : "number" } }, - "perFeaturePropertyNumeric" : { - "oneOf" : [{ - "$ref" : "#/definitions/binaryBodyReference" - }, { - "$ref" : "#/definitions/numericArray" - } - ] - }, "globalPropertyScalar" : { "oneOf" : [{ "$ref" : "#/definitions/binaryBodyReference" diff --git a/schema/i3dm.featureTable.schema.json b/schema/i3dm.featureTable.schema.json index a9741073b..1cb814704 100644 --- a/schema/i3dm.featureTable.schema.json +++ b/schema/i3dm.featureTable.schema.json @@ -9,31 +9,31 @@ }, { "properties" : { "POSITION" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "POSITION_QUANTIZED" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "NORMAL_UP" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "NORMAL_RIGHT" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "NORMAL_UP_OCT32P" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "NORMAL_RIGHT_OCT32P" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "SCALE" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "SCALE_NON_UNIFORM" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "BATCH_ID" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "INSTANCES_LENGTH" : { "$ref" : "featureTable.schema.json#/definitions/globalPropertyScalar" diff --git a/schema/pnts.featureTable.schema.json b/schema/pnts.featureTable.schema.json index cac653b04..8e68f12b9 100644 --- a/schema/pnts.featureTable.schema.json +++ b/schema/pnts.featureTable.schema.json @@ -9,22 +9,22 @@ }, { "properties" : { "POSITION" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "POSITION_QUANTIZED": { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "RGBA" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "RGB" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "NORMAL" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "NORMAL_OCT16P" : { - "$ref" : "featureTable.schema.json#/definitions/perFeaturePropertyNumeric" + "$ref" : "featureTable.schema.json#/definitions/binaryBodyReference" }, "POINTS_LENGTH" : { "$ref" : "featureTable.schema.json#/definitions/globalPropertyScalar" From 9e84298bbddba445640fb5557da9b30b91daa21b Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Thu, 25 Aug 2016 12:21:53 -0400 Subject: [PATCH 29/34] Only one type of bounding volume is allowed --- schema/boundingVolume.schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/schema/boundingVolume.schema.json b/schema/boundingVolume.schema.json index 3d32c0db2..69180fce3 100644 --- a/schema/boundingVolume.schema.json +++ b/schema/boundingVolume.schema.json @@ -33,5 +33,12 @@ "maxItems" : 4 } }, + "oneOf" : [{ + "required" : ["box"] + }, { + "required" : ["region"] + }, { + "required" : ["sphere"] + }], "additionalProperties" : false } From 8ade7ba2ce85ad9672e28924120f38b277a81824 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Thu, 25 Aug 2016 14:56:31 -0400 Subject: [PATCH 30/34] Made byte-alignment restriction more verbose --- TileFormats/FeatureTable/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index e99e24ccc..0fcfb80f0 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -70,4 +70,4 @@ In JavaScript, a `TypedArray` cannot be created on data unless it is byte-aligne For example, a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. The string generated from the JSON header should be padded with space characters in order to ensure that the binary body is byte-aligned. -The binary body should also be padded if necessary when there is data following the Feature Table. +The binary body should also be padded to a multiple of eight bytes so that any data following it will be byte aligned. From 05cc8bc47b3593c8fd7e32b4a762b78b8b225ac5 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Thu, 25 Aug 2016 14:57:21 -0400 Subject: [PATCH 31/34] Missed a dash --- TileFormats/FeatureTable/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 0fcfb80f0..4b92f9337 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -70,4 +70,4 @@ In JavaScript, a `TypedArray` cannot be created on data unless it is byte-aligne For example, a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. The string generated from the JSON header should be padded with space characters in order to ensure that the binary body is byte-aligned. -The binary body should also be padded to a multiple of eight bytes so that any data following it will be byte aligned. +The binary body should also be padded to a multiple of eight bytes so that any data following it will be byte-aligned. From c362dafb36ed53a5a81e0182d5eb6eb5d44b8802 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Thu, 25 Aug 2016 15:10:07 -0400 Subject: [PATCH 32/34] Tweak --- TileFormats/FeatureTable/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index 4b92f9337..c60bd8834 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -70,4 +70,4 @@ In JavaScript, a `TypedArray` cannot be created on data unless it is byte-aligne For example, a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. The string generated from the JSON header should be padded with space characters in order to ensure that the binary body is byte-aligned. -The binary body should also be padded to a multiple of eight bytes so that any data following it will be byte-aligned. +The binary body should also be padded to a multiple of eight bytes. This ensures byte-alignment for the glTF for Batched 3D Models and Instanced 3D Models. From d48c5b5d3da7c0c0b2d50a3b171f06c1cc784a31 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Thu, 25 Aug 2016 15:10:54 -0400 Subject: [PATCH 33/34] Specify embedded --- TileFormats/FeatureTable/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index c60bd8834..ce5290a88 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -70,4 +70,4 @@ In JavaScript, a `TypedArray` cannot be created on data unless it is byte-aligne For example, a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. The string generated from the JSON header should be padded with space characters in order to ensure that the binary body is byte-aligned. -The binary body should also be padded to a multiple of eight bytes. This ensures byte-alignment for the glTF for Batched 3D Models and Instanced 3D Models. +The binary body should also be padded to a multiple of eight bytes. This ensures byte-alignment for the embedded glTF in Batched 3D Models and Instanced 3D Models. From 856ec21d823be74383141a79dac4a28145fcf2c0 Mon Sep 17 00:00:00 2001 From: Robert Taglang Date: Thu, 25 Aug 2016 17:05:34 -0400 Subject: [PATCH 34/34] Byte-alignment guarantee --- TileFormats/Batched3DModel/README.md | 2 ++ TileFormats/FeatureTable/README.md | 2 +- TileFormats/Instanced3DModel/README.md | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/TileFormats/Batched3DModel/README.md b/TileFormats/Batched3DModel/README.md index e7c620198..bcf208892 100644 --- a/TileFormats/Batched3DModel/README.md +++ b/TileFormats/Batched3DModel/README.md @@ -79,6 +79,8 @@ address[1] = {street : 'Main Street', houseNumber : '2'}; Binary glTF immediately follows the batch table. It begins `20 + batchTableByteLength` bytes from the start of the arraybuffer and continues for the rest of arraybuffer. It may embed all of its geometry, texture, and animations, or it may refer to external sources for some or all of these data. +The glTF asset must be 8-byte aligned so that glTF's byte-alignment guarantees are met. This can be done by padding the Batch Table if it is present. + As described above, each vertex has a `batchId` attribute indicating the model to which it belongs. For example, vertices for a batch with three models may look like this: ``` batchId: [0, 0, 0, ..., 1, 1, 1, ..., 2, 2, 2, ...] diff --git a/TileFormats/FeatureTable/README.md b/TileFormats/FeatureTable/README.md index ce5290a88..e99e24ccc 100644 --- a/TileFormats/FeatureTable/README.md +++ b/TileFormats/FeatureTable/README.md @@ -70,4 +70,4 @@ In JavaScript, a `TypedArray` cannot be created on data unless it is byte-aligne For example, a `Float32Array` must be stored in memory such that its data begins on a byte multiple of four since each `float` contains four bytes. The string generated from the JSON header should be padded with space characters in order to ensure that the binary body is byte-aligned. -The binary body should also be padded to a multiple of eight bytes. This ensures byte-alignment for the embedded glTF in Batched 3D Models and Instanced 3D Models. +The binary body should also be padded if necessary when there is data following the Feature Table. diff --git a/TileFormats/Instanced3DModel/README.md b/TileFormats/Instanced3DModel/README.md index 519ccbde2..ee6a80fde 100644 --- a/TileFormats/Instanced3DModel/README.md +++ b/TileFormats/Instanced3DModel/README.md @@ -236,6 +236,8 @@ When the value of `header.gltfFormat` is `1`, the glTF field is In either case, `header.gltfByteLength` contains the length of the glTF field in bytes. +If the glTF asset is embedded, it must be 8-byte aligned so that glTF's byte-alignment guarantees are met. This can be done by padding the Feature Table or Batch Table if they are present. + ## File Extension `.i3dm`