From e6c083782a9a2aece1a1709a2044234feec57412 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Thu, 13 Mar 2025 17:41:35 -0400 Subject: [PATCH 01/39] EXT_mesh_primitive_edge_visibility --- .../README.md | 148 ++++++++++++++++++ .../figures/depth-fighting.png | Bin 0 -> 5436 bytes .../figures/hard-edges.png | Bin 0 -> 7178 bytes .../figures/outline.png | Bin 0 -> 4976 bytes .../figures/visible-edges.png | Bin 0 -> 7231 bytes .../figures/wiremesh.png | Bin 0 -> 25349 bytes ...mitive.BENTLEY_edge_visibility.schema.json | 45 ++++++ 7 files changed, 193 insertions(+) create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/depth-fighting.png create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/hard-edges.png create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/outline.png create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/visible-edges.png create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/wiremesh.png create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md new file mode 100644 index 0000000000..fe6b3b936d --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -0,0 +1,148 @@ + + +# EXT_mesh_primitive_edge_visibility + +## Contributors + +* Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne) + +## Status + +Draft + +## Dependencies + +Written against the glTF 2.0 spec. Depends on `BENTLEY_primitive_restart` if primitive restart is used. + +## Overview + +3D modeling and computer-aided drafting environments like SketchUp, MicroStation, and Revit provide non-photorealistic visualizations that render 3D objects with their edges visible. The edges can improve the readability of complex models and convey semantics of the underlying topology. The `EXT_mesh_primitive_edge_visibility` extension augments a triangle mesh primitive with sufficient information to enable engines to produce such visualizations. The information is encoded in a compact form to minimize the impact on the size of the glTF asset. + +Figure 1 illustrates a typical rendering of a cylinder with its edges. The width of the edges has been exaggerated for emphasis. + +
+ +
Figure 1 Cylinder mesh with visible edges
+
+ +This image shows both of the two types of edges described by `EXT_mesh_primitive_edge_visibility`. +- A [silhouette edge](https://en.wikipedia.org/wiki/Silhouette_edge) is any edge separating a front-facing triangle from a back-facing triangle. In Figure 1, one silhouette is visible along each of the curved left and right sides of the cylinder. Silhouette edges are *conditionally visible* - their visibility is determined at display time based on the camera direction. In Figure 2, each of the vertical edges encircling the cylinder represents a potential silhouette edge. +- A hard edge is any edge attached to only a single triangle, or any edge between two logical faces of the 3D object. In Figure 1, hard edges are visible around the perimeters of the cylinder's circular end caps. Hard edges are *always visible* regardless of the camera direction. + +## Shortcomings of Existing Techniques + +Various techniques can be applied to a glTF asset to approximate the rendering in Figure 1. The approach illustrated in Figure 2 uses a wiremesh shader to render every edge of every triangle. While the interior edges can be useful for visualizing the structure of the triangle mesh, they obscure the underlying cylinder topology. + +
+ +
Figure 2 Triangle edges drawn using a wiremesh technique
+
+ +Screen-space techniques (e.g., a "toon" or "outline" shader) can be applied to add edges during image post-processing. Such techniques can only approximate the actual edges. The technique used in Figure 3, for example, fails to reconstruct the edges along the top end cap of the cylinder where they fall inside the cylinder's volume projected onto the image plane. + +
+ +
Figure 3 Edges drawn using a screen-space technique
+
+ +The hard edges could be encoded explicitly into the glTF asset as additional primitives to be drawn together with the triangle mesh. However, this will generally produce a "stippling" effect where the edges and triangles collide in the depth buffer, as shown in Figure 4. + +
+ +
Figure 4 Edges drawn as separate primitives
+
+ +The [CESIUM_primitive_outline](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/CESIUM_primitive_outline) provides an additional index buffer describing each visible edge as a line segment (a pair of indices into the triangle mesh's list of vertices). The extension leaves the details of how to render the edges up to the engine, requiring only that they render without depth-fighting. This approach satisfies the use case for which it was intended - displaying the edges of boxy, low-resolution buildings - but suffers some limitations: +- The surface geometry must be represented as indexed triangles. +- It only supports hard edges, not silhouettes. +- The representation of the edges are pairs of vertex indices can significantly increase the size of the glTF asset. +- The edges cannot specify their own materials. + +
+ +
Figure 5 Hard edges encoded using CESIUM_primitive_outline
+
+ +## glTF Schema Updates + +The `EXT_mesh_primitive_edge_visibility` extension is applied to a mesh primitive of topology type 4 (triangles), 5 (triangle strip), or 6 (triangle fan) that uses indexed or non-indexed geometry. It provides the information required for engines to produce a rendering of the primitive's edges. Engines are not required to *always* render the edges - they may, for example, permit the user to toggle edge display on and off. When rendering the edges, engines may use any technique that meets the requirements specified below. + +### Edge Visibility + +The visibility of a single edge is specified using 2 bits as one of the following visibility values: +- 0: Hidden edge - the edge should never be drawn. +- 1: Silhouette edge - the edge should be drawn only in silhouette - i.e., when separating a front-facing triangle from a back-facing triangle. +- 2: Hard edge - the edge should always be drawn. +- 3: Hard edge encoded in `primitives` - a representation of the edge is included in the extension's `primitives` property. The edge should always be drawn, either by drawing the `primitives` array or by drawing it like an edge with visibility value `2`. + +The extension's `visibility` property specifies the index of an accessor of `SCALAR` type and `UNSIGNED BYTE` component type that encodes as a bitfield the visibility of every edge of every triangle in the mesh. The ordering of triangles and vertices is as described by [Section 3.7.2.1](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the glTF 2.0 specification. For each triangle `(v0, v1, v2)`, the bitfield encodes three visibility values for the edges `(v0:v1, v1:v2, v2:v0)` in that order. Therefore, the number of bytes in the bitfield **MUST** be `6 * N / 8` rounded up to the nearest whole number, where `N` is the number of triangles in the primitive. + +The visibility of a given edge shared by a given pair of triangles **MUST** be encoded as a non-zero value no more than once. All other occurrences of the same edge in the bitfield **MUST** be encoded as zero, to prevent engines from either producing redundant geometry for shared edges or having to manually detect and account for such redundancies. + +Engines **MUST** render all edges according to their specified visibility values. The edges **MUST** draw in front of their corresponding triangles with no depth-fighting. Engines may choose a material with which to draw the edges - e.g., they could apply the primitive's material to the edges, or apply some uniform material to the edges of all primitives in the scene, or some other consistent option. If engines choose to render the edges specified by the `primitives` property, then they **MUST** render those primitives according to the glTF 2.0 specification (e.g., using the specified material) and they **MUST NOT** also produce their own rendering of the edges with visibility value `3`. + +#### Example + +Consider the simple example of a pair of adjacent triangles described by the index list `[0,1,2, 0,2,3]`: +``` + 0______3 + | ⟍ | + | ⟍ | + |_____⟍| + 1 2 +``` + +With 2 bits per edge, encoding the visibility of two triangles requires 12 bits. So the visibility buffer must be 2 bytes long, with 4 bits of the second byte going unused. The diagram below shows which edge's visibility will be encoded into each pair of bits. + +``` +Byte 0 1 + ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ + │0:2│2:0│1:2│0:1│ │ │ │3:0│2:3│ + └───┴───┴───┴───┘ └───┴───┴───┴───┘ +Bit 6 4 2 0 14 12 10 8 +``` + +Assume that the vertical edges `0:1` and `2:3` are hard edges, the horizontal edges `0:3` and `1:2` are hidden edges, and the shared diagonal edge `0:2` is a silhouette edge. Then the corresponding visibility values would be `[2,0,1, 1,2,0]`. However, we must ensure that the visibility of the shared edge is encoded only once, so after replacing one of the redundant `1`s the visibility values are `[2,0,1, 0,2,0]`. Encoding the edge visibility produces the sequence of bytes `[18, 2]`, as illustrated below. + +``` +Byte 0 1 + ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ +Binary │ 00│ 01│ 00│ 10│ │ 00│ 00│ 00│ 10│ + └───┴───┴───┴───┘ └───┴───┴───┴───┘ +Decimal 18 2 +``` + +### Edge Primitives + +The extension's `primitives` property optionally provides an array of primitives representing all of the hard edges encoded with visibility value `3` in the `visibility` property. Engines should render these primitives directly, in which case they **MUST** do so without depth-fighting and **MUST NOT** also produce their own rendering of these edges. The primary use case is for edges that should be drawn using a different material than the corresponding surface - for example, a red outline drawn around a filled green shape. + +The `BENTLEY_primitive_restart` extension *may* be applied to the `primitives` property. + +The `primitives` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `3` in `visibility`. + +### Silhouette Normals + +The extension's `silhouetteNormals` property specifies the index of an accessor providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using the "oct16" encoding described [here](https://jcgt.org/published/0003/02/01/). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. + +Engines should render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. + +The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. The buffer's byte length must be equal to `4 * N` where `N` is the number of edges encoded with visibility value `1`. + +## JSON Schema + +[./schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json](primitive.EXT_mesh_primitive_edge_visibility.schema.json) + +## Implementation Notes + +Example routines for encoding and decoding of oct16 normal vectors: + +- [GLSL](https://jcgt.org/published/0003/02/01/) (Section 3.1) +- [JavaScript](https://github.com/CesiumGS/cesium/blob/dd9c25b652460e8cd77ecd29fbbf7cc44d8b62a2/packages/engine/Source/Core/AttributeCompression.js#L77) +- [C++](https://github.com/iTwin/imodel-native/blob/4f0a5008dba61124b490a7506a6cd82ce1b8f148/iModelCore/iModelPlatform/PublicAPI/DgnPlatform/Render.h#L2012) + +The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` and `silhouetteNormals` buffers. + +iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It draws edges in a separate pass from surfaces to [ensure](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426) the mitigate z-fighting. diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/depth-fighting.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/depth-fighting.png new file mode 100644 index 0000000000000000000000000000000000000000..ba8c91d3e9111f58b6dd8af78e101926ebb90410 GIT binary patch literal 5436 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 M07*qoM6N<$f{Rq)@Bjb+ literal 0 HcmV?d00001 diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/outline.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/outline.png new file mode 100644 index 0000000000000000000000000000000000000000..8740901041057b605644efe53b0b403738f02437 GIT binary patch literal 4976 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 literal 0 HcmV?d00001 diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/visible-edges.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/visible-edges.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2f8cd3f6fecf4f72e4834b7eb8bf696a822642 GIT binary patch literal 7231 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 N002ovPDHLkV1hhV0@VNj literal 0 HcmV?d00001 diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/wiremesh.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/wiremesh.png new file mode 100644 index 0000000000000000000000000000000000000000..7d0e3264a2b81e8e538ef62df51dca3d8f0f09a6 GIT binary patch literal 25349 zcmV)?K!U%CP)ZgXgFbngSdJ^%m!Ep$a#bVG7w zVRUJ4ZXi@?ZDjyWZ*CwlFd$7~av(A=GBqGEF*-6dIx;aJK}{eGzXSn)woX<%0 znNfX)fnC*S-1&5!&jirZr`zoU0@$8>uYDl?JqA{aqNOThr_)hO&H?5Fqrl?ov&8u< z0u}(XffoY*1UxkHfzJZI47@w>_}ihgpaahb9tPYBYz1xxZUSzs{(p1z{jId-F_c|_In4c7+6BzyWfq>9T8Ag0_%Ydz}o8XKLWl5{5^1J;)C1|_&x$w z3VwGXtCebaPObiZBrq3&eQm?>Hv?}3W<%$*9k>Cw8o0drTmx(Yw!`?Y@uWxieRf5E zuYlzOB?P3-oDQr8t_2PR{sjHK82CcNg`W*v0(=B`E3!_}hp7I2Ebwo@He|)xWG65) z;W(2M5UA!j_l)WGIPjmyYQ3s@o&~^ufn&Qc@~IKH4mbrk9ykcN0>-!2fOFkX`~U9~ zxbF7A%8ea*y`EgR1<2|gRDCu9pGV4fF7N^16TsgBD`>V99pJBke+E7Z{3Gr8)ga^+7)^CB$=-FL~y~AgLGtqOeYd8+{UgM~(ehvHu@Fe{-^4Sr%0=NJ;AGj2_ zNv>s1ocFr`z@fVIBpmyG;40vq3CI2r_#p5+C`@j3?bUJiacIZ5|L+775@ZZ3{0|x{D1$+i69qi7Y z1xM&Lz#D-3ROf#^;o%+vJQcZ(XC@r~7^HAFBpjP+v9~DZiR?m?Fs_z4D zMoE}|r8%CocD)u^#1A1C`CY)Lq4TB=ohNnZtn1Jj=>+?7_oeQJWB=XCVuc#J)9Juc z;1J-!z`aq&PdXVm5BL;vlb(RA%4o|?ClDCF4m=)s0ZJmoq>C)j6)4_X*7QQi8a@ui zb4~J9l)ZaF%W>ZVz65*>nALFHO=#d4t^S?eaBLTW!|{$E!N%1a@{3?)$~ZcUXRY)jx5kQC`0!|;BeYI8>A#BAmtkY zo(UX9yX`&!S-<-r7ie+Av0G5QwGMbKios&?S>QC}zBS3`t7m_9!!bSJ708mju;I8} zz&nr|`madwV{$WcSpjb50=3_9bLx{}{z!O@ooi4wTJ29u0nn02e{`_U_%z zRlG|9%YPL2@6NoAQq)JHe`g`ZI1V_u`unlSsw_>o*-gl0`vHo(o{N$gO>!2BFGhiv z17j`6z71KTXEhvmHSjXzZZ=)-Qz*mt^oC=84ZI$Kv?;az-@vzl*8ul#IPOC9zPAB? z4Lq>nSdd5X8Q@~n7tf$MLuzt88n}Ll!1yQx#*NkAX9GV1&Vue8^`y5icO`(`Wq=iQ z^X7Fl`aYT(OuG+q{=XFgUVz#tCMQ+*T!RuO>l%)|2KXGZ zHupi6Ev4;$iCot+(7XKsiuYXi+{MZcG+_M@I2XFxC%cRO|M!;MrGT~D&Fi5KIudvQ z%HX9u|gycu|1 z^$f2kJac$6Qs6f>-0!{BZ%0Lb{~7}5Mw$@OT;;0MUPdnC2Hmm)W!Z09M+)f`0w=Hb9{ZIGNa}8uoSvpXopM+eH ztB{NN6yUyusXjI;f3CXMQ>*6<%_W_N=4qCrtlav*F<(PqeI;Rc=_F(={)=#S^DRgr zLkfRZ_4|i_Re|3>h6a{@M(^v(kE?qxLu`$)z%e(Vc=1Q5-JgRziI7-!?>OY{-5U*_ z52oEEzp{FsA6Nfhjo$f@)pIpr!hV}TI`R$RS9>RQKdr#>LDdD84Up?+zes z1T*O(SMMa`>TN^ZzQYlzwJG^9ffVsrWJxxlPJa|(SLSx$E6B}SgDgTQJ9Y_j*KS9_ z{{6h)d%$~8!2T8{Tg>e!MgB725rON!0l8eEd%q32%x?_*c0BNL;KfXyH11I+SRRHf zmG3IwiQfHdDAxK@Wc5Swkn8U4pw8}%1&(!};Zg*|pCT)@9s%hQG-o$M*Sa3{>zODq zbqmsmn`^emo&eXh0L#DRaA#QAol;z=G~y2$%D2Mv}NqV`*fk|Ga4p~hk~ zpnV#;=TDBorUPJ83arpPROir++yUQqhDdq2mS?t z_%FO`pN*{9Cz#A0`3KsnS0!Y2Dq~kkS;;(y&ic#_eY+K4}Oyg z)GuFv?v2w6uqgx9B)4yEM}h6($oI{;%Z+FhJOd>!wxH4PAOy7gqEV@-9eSwYPeCrm z_4JR5p;%-W0?s#)a>bNo2TG5B9RdE?OpblNiU4>RvM6q6e+z-{<;blv=_0T_gUM<2 zSD^T3KkxXLAdB=&@A=-0!2f3Y2c^>y2uG32y1(<=O(-k+J=9juVX{km0~&zL-Lvf8 zCML^HWGPNYeYqWZBZs3l4N=cr*^c_*ECi;@QJ-%_Ai5uFj}*p@$q*X2KM#Bly1S+a zU{emP;m)wqy(`g6ABZ|{E$OLpU$&yr?o4Fau0@J^AOhNC?v48nSEG^rr$|wrOMl|xR5Tg84gtdj^ZQVM{TBLD&L2navA)>YeVcUPR^--OKVOC1<$F-~^9ITmJB_13;Zo!>UWk$-Tl-5Gxc?3! z0|U!FeRynG!S`G=(%1`Jg?#!sNV)b$ZovN4-*Yl=dK+~IoXg}6xF1r$SO;8z6z|6f z9P1Fk9?9gS!gVMC@I&NEJQ`UO*ERY%0@$_4!nv`?1qei4WN|E$b{mSfW+KC>UgxzPS;!w$&-QF;(EJ>M?J6b{lSA&_lbJl> zejDnGlMx_>5eSYzeG%*5o6rDuCbDeTqxjPX%=^+lVEWRnuDh99|1L*Xs)MEm#*qFU zfb`_6z&8Z2shVUS2v~ifdc?4p+M|Tsz8%P?zZikyqU!G*s4>@4zwCaf0aA#YcI}Ih z%WxI~&M#nffhfK@r22gqav{G`z0aRf z$1Yn?#_$pp(;QiSe-X;|9Zo+J;;)fXy6)XyAnRmq-L=Syy@7i9#+is&|3qqf)HNuc zx(vl=kEy=D90BTdWNn{KZ8^7~?B4egC`0bvR%CHcM%HXr)h0(&Z4#Q6@H2Kdqm13b z)wzcdkAr>bRp(!U!1LSc9dE3DUs}D-fz$w6OgMZPs^0gLqDf7KK@l=1T? zcvIvyWRfpJjdLlo6645KSx?^wD z&qv_2Z0VJUf4I8Wzaqe2huZbusFMrNK}m-N$U6QdwcEKJxrMh?*I0t$p-&+twq-8gtM0!G zxqMGV0Qhlr{3S@)U4G>aC@wvv`h5wSD?GevGwbtl`ZIQJ8{fBTsNzWu54iKwp8nfHA3Hr8l;PqWFdOtRmi~&`)x)I|6Ak=+(Ml&8KZ9ewS>8WHkzJ|+>#5CtG5~r5;p%~vBnmpTqjd2 zmr-jDsP6wgWPNT!@!FG-D{?%_w9Q0T{U~IOK88T^a-?jpub%78)!(nGuKnWb8J>mW zq`3$P@2jq{2+f`S6wQBaLNV3})iXRFWmZ3f6xEhlWEslU^as?og#27&(SB3C%L?jA>w8ttcO#l3I|(VoLNtOO zQQhOm$ckNpti>_a_n$+_gM*QKIF4eU5d^N8D4Fq4#%#09p7KB1`dq$f6#KvX;A$bvg}Mu|GgyyQMnrC)5d@!w}%s)1NRp54rC% z=}#DKpg)e?S@laOk(JUfDbhs)yMg9vYW*|NY)6B^-0GcI(64(dQ9sV1w!pUoZ-DM^ z%fOWktlgj*8CKTHmL{amQTgDz7B#}v)&FlsKrrx)61IFx5%^LP8{4aEn`?1V_4oGb z8f)nv8CM`>aVM?MM8Ww@Ob)b1P&{%HQsxCH5pYD+F2|w(e-&~E_d_7K5V?$h1pEh5 zw!f;{=L5(}Jpv^xUW*j>sR(G-A`ra=Dd@Wp)#?6dDN7Fl^W5tGOHmxP6J_@DlY7}OMo}P#MTLYJ*~ic!Ek?lhN&h*-H8C^T0R5cjYyfs5dan< z#qllQQl#WD|NA!jgJDaU>|%1lWK(s$ZPb(2mKq+Vf71F_2xRA@(a}Km3zV)0W#!f( zko1sS_G@G*o`V$ThsY!NA{yEM5w-c3kehc7vQ}@Xp7Vi-yn8@({m)nL_$O6+ejh35 zBM_iZrlx=01G%&hqd#MJ6B^x({c@kG9d4#hW&f5sl|78&&@uXB-%-K}JzhPdfyO+X zYpFqVC-PnlG%Ha*TVF3nIxs78k3nS!S@CznM0a9pfz_8%fBdk5S}P|u7roRhq{Mrw z=F?8%mhUD6OatF2QV7@bncF!^ZTWhrGcA340g5lSRKJB1CKn^1ore@-ZuOf@gr0=J zHoN+6f0VAj4uNhdigWfu!2Akw%{C*e`ll$C`frpD{~StqJfphyPGmX0S)F$U0>zQl zZ>OPw;t#6!IfHstz~NOJS;o%znr_DK3hLyM-xeF3w%Aa$r}h7?>UuX-ePV;-HE3|$ zT0P%L)lM#GR#Ss!l<5PFFB7Oeb|Ndd9ktc%zy&bz`H9_!rT{D%Si^@5s}Z)LK<|#~ z0d}H>?4br2K?*$^DcW3g-U8H63%t+#>X_N65oe+f8niJDd^a)~d^e(wx3bL!KC^sl z=m+0c`V%HLN{=8{!`z$=Xv9AUDWw~O{0aeZM)mj2)pz%(-v5^n=ud%P){? z_+Au?eFVi}XCQ!_hupUxA?vvdf#D$JN}pA=#k~-C&!o=St)rrf}+SNd_fO=4KL9?uC*Fh2zAf212-g!s$xdU0kTdU7))$?w1{`U2EoXPq- zr~1sR-lGQ-r>DDwNxT`);^-0`6e6gs>al1{$*tcUGS}n5-+edY{{=!Eh&GU75{fU2*lq{y!f9 z#@ChDb-ZRl9>5_=y#8nRDWB-WFhtFJ*&Fk&gz~c)wOS-cEwFPNYQRXN;yPrF;^nx z4NY~J+iO|9kaaLm!rEY>y1w5A?jU7=aNEPR4(95|tixSZ>{&kx-Or_^-&=t-yl7a( z9q&$n+7q&quoaE@@=aEBnErsi=X{2cQrpNp;ay{bdUAdd^&qmd`rfq;vxr*<-&LB= z@4%KB(;$BX6XWf%Ry(jZ-0jM{-~D7KvJRFAGV5S-gnkcoh+ENpV3>B9xV#-$?cMNP znv)iHcj-~PUU$;(F8aw9*BWg{*62fB!S`@-sa69^!8Qd_1O z)wO3*Z;c8yxaLv2hqF*J#pZv^?OatoW2zN(Gh)7**Obwn!JdhE_LdBxx+MwH4y>Fa zFMCof&1!+PWtN%(X+N_xPJirRu30R0SWCZBZKY0R_`I{7)wO3i4VfRZk5 zmM$e0HP^EVp!1!!zbnUK9g;TDYX??Sfy-QRt%J=w#8UF1WP+QLcdbKHO5SGwLn(R7 zELj^FS@UN4DKuBo%sX3y=9Wh2r%v5o9XpRYQDD|*HTBYmO=#ZB7#?e?zvrSkrfaLu z4b?H5P`Z9@^)5z&uBs93&fr~Dy_13U*6JOMVX}dG4V+~=f5T+vgFA`4wt6O`3%QfH z?!1zpIkbMYe=j9G;DzRu+}JQRqig5brsz{8iE9Mgfi;iGlW>0eyJ-^6VuzS@@EdCZ z^~9t*qPgksnbkFNMzn7ATsNap*G-$dyfXvB5@b={ir=cYzpGFhH>k#GXafqnd~@ry2}=TdOuR@Yx>CmZG;dTaEfI zr@vxM2;UC+pdWlL=g$Y`6m-8pFe)~VHf*P}&^H=t!LZZVw)RL2G zZ@~v&Y~b_z(7=~#`KC&G=$&5(-5quDnP{E(7_jU!vTj8DwB2V+;lu7i4es*JT!wRq ztZA0U1&#UnF$vX2*i4vUYn;DDXr#M>dO_qx$jw-b*xnCBBltOpX5`Y8j;PN6CUTwj zN0#O)RQh-sTF!9>;`u!lDfIiO_v(5rV!Z4?Yq#EyYOsS=5Wlu+hi9YZCp*!Sk`s}; zcyF}O@rTuSGZ09Rq%Nj&6?Ns=Mxj zVfRDJG`>UsUR_rscDgO+I1GX43dC4niu$BNxg^IP1SQ9gDiw!Oy?2Q#dNNww$Q(!p9OCVK;60(?vk7f zh%d=8QtvX-Q}iM5nFkY#lP$SavOdc3hfwGKZS`z#sQ&(!>K$H$Y7ssXk!t@BF<^Ef zcks`V_4zV#8ULwz-j5*wJp(N}IRV9KL)G6$qhXHdSqA zm6pyx>&UEXv8}YeH$4WE%Q_ZWsTtHeXS}(3t`8!sY*e#%AZ2}f_5NR~?tKJu8Sg}kAHRzRo980{ zTu|NTXVg`hjz)cV9kOC4BM_`WNs=R|i|K%L;#bsFi*Kmj>0s&wpsNUj#IIp;0UF#a zVdAblJ^&4tQ!)LM3%*6wvs|I~DFPPMG1kPy1T)?=L$1e@k-`{ga?{;yva5P2tB>$o zG)iovCY4%{d;sAf*_SQ!cls9kJN-~5m#V&o{!!Z$%JyU$qQBPr%?NDAAOPKofc90? zDbGTdYA32+@tx|}=Og9$Z&ZWgHK>$W4=vL_41o~@?jN9nGoZoYpHS7@<<&F17WK^= z5g1QGRh>4WJ-1dM#r?Ppbs z&p?*ety=s*GP76hI@LEyg*B_1wBbu9j%I{tge`n-k8{bm3FAOJ~3K~#WxZ{f31to0G< zlGG<6%lK;NU0;Mc`l;1-=OH)pNz}zUe~lJM4k5SlVCrpQKZg|f=_q43f)WoWA)sH0 zT+2U11CaIg7UW8vguI8v$c;P#^_dSCD?(#HyApX6`%_nQN-Z00)t%fLCZUAMJ*kVn zmMo@p5pfxE)9;Dibwa?hX{f<~^=9g&+V7xWIb-1Jp}55Lt1m+%z)*FKG3t4d4al0L zg0)M3#?CTWYp64JZV5C0*=)mvpCH9|3#qK}?sGbFnSPBr>?za*I-%YH^6cvS=TMh| zJ`W{-psu6@avk4P-ScJ6{oaPG-oH5CeHU3=celq+SI_kU=ew_?d9J^&u1oc9|AE^6 zuc)i@-iqeJj<4SR`P3D4dng6}4V1XpP`&fv^t;(NBRBFyr0Ao_dLN1UHI$UFs>Nqg z&$kVucD1;66M11oC}DCflM_}4R@YtcOOt$>DP$zeOgWSJ|RM)&0af{^k>lyS{Extc>CN^hB5$LaBV)5H$^e0Tl z2oLOri2p<(!9Sul-KzH(7+AetkIp+gGKHT&jsHG0x?PGo#Zsxp%X3GD<&?9Xwf?m}}>S5*I+y9dhNeHUf++;;ma0{4r&XMH&W@(Y~rwxM`yKC*gdVS30- z{bKb_FQVW2oQ$%Iw;}iP82YWoR^+;VyZZlO)iL`sS#Cw`eIl~*b5Q(vBz4)im{`@~ zGpXkecOu=gs>KJ=Tv4}4LJ5=0sY8(!)U29MBG>&0>OPn~Rls_C1+4!R9A*ZKB>j z)gLKOM{d~Fs12S*|5i3zkbCw^Gyomro&RxU$^N-|FOzENH%B3>;>zc%_xN+~`>!K+ z?Gy9^@qE;0M^jhQHTiD!3 z)V4E`n|>s6+1utQyVO;S&p=>ss}`Hf-?rCQll0JgT}PcTISwUwUQj*5S5TICUK+64 zLq=N{WU>jRP`{0=%0roKHBu(tZlu3X-wo7t`i?=P(<;(j#_g!#Po&zT9%rCMy6XAAh^*_QtNX8|U*@5h_Hbl%&P0RZzfn`cjznOK z_1_RRGw1P0w{D;&JAab?s>Khg+9sx3W;Cus8Puz)_x&~Ueq6mDpLBLq+Fika>1YRw z|3%9CGc-!Qw|bfPqel2U>UkdH*clbZ%=ao}?Mlt}Ey%4n3BCAi`rV2)F4`}U3v(U{ zT%U|OITRRoQB3i9)Syp8scb)6)kT)QSF_V?zdIZQ7$VKx<=IfAkeIrsTUp|D~ zVBf6-js71+{o`NvHWY8oK-R~9u3M{XpN1^e9|x|#1G$`EML>QI%AAJ8_7FP`xylbj zZsL7t9x!(xPvb`@iP1ygKN1Z#ZS$4BSk1;WP+z-Miyuhs4cRlDg{D1bp@I1TlyaDxR^&FsI{R!iQeTW*r)Ls&Ep9=I`3vNF{Yl`OccQ_khms8;cjr3@bcY6h zgSaaQvYsEVp21i6`KXTj>yWj4srUN=#DDt~ny9`G1?%qMF&j~#4zG@TC$a+1XR<3i z7Y#m7KuZ0M>fW1>m3~y@J!Ye1(!)@D8L9b$NRJLdRHTQZ@d(uD`;ktb^~Lt4^0&=-A>m2L zkX(k`y_1om9EE@%a`#NGLDuIKq~!lb9e?<85sFi`qV#ykJp?K2Z;++(Q~IEw`G?4Q z#@=fuacevmDf;&bTap7%yM3to|Kq&hJ7`p1iQK5`5g7dq;ntw{`~|X(e~&EaQ<*$q z4x<6=!N~3YCQ{~4Blqob#ABdYXyAMZYMb+rCHn|!2U~{oVAAPL3!LYHdd=CF(SY+L z6!(tM51t*~b*3Cx=H8j?q8^!tkW06J^#bRi#(x@87FQws*vplx2=Au&GWxk`ZO`hu zdmE5a9Zqx1bq87l^$i5#S5Z%}hU8?_(JPUQwIOocw@_OCCCIvm#HIm`YPjDnn%&%^ ztLNCtJ@f1oak2F|l&o4?u13D+IDnB5P*Z-iHu=?~rUk?)?vtYj|Je`OKtW$6*g!EYM5( zGK75kkDv$t9CF{Di#o%^jp%$T(K^)NuOhCjA-NSblx;ZhAQYU(l-YfjA63tKD;f;` zkhrDop?CW{0^UoAyC!ENYxQKLv@tmjDMr(lp-mPG^q%ujvf&AlWBvkJ{|}(aOP-1P!=PX@~J56b}Wj^4?%7IV46LV87R4NKcwKl zM(*OLkSlx;0@*{+K<{S`cA|mqtEhkf4h=Z3LfOiY?DYPga$vc2?3!+169%y5U=hv1GV>~QS$2>)U13<*SE#u9h4Bc57Na85Xe4@*b+Vo z;Adz6xDEmPS=BuZe78mJGp!b@i(bS{HTovgq~cNmayj0KEW#&H2h674lj$kw#a(4? zauwZspNTr;Tx8i^fEqG|;d}%2?urLd&s#OgWhe`JJyM=WHXOSH#a<)G1!@AscTh_D zkcMNR-4z54WG5iyiN%qdfd5xL-vPue=^{k9{x38ac0IC2F&53Ch)Q)lVvqb74OEY% zxjzr7KG&iF@^i==cnkt_$^hA+PW!(E#ZD(8R*cQ#U5KpfThYKVK^@{i99X`^is^ko z$}CooFbK9+|GM8Fg+|kNp={3#dm*C}J(?hA(clDg} zLGIGGkira8yL-kSPmv!ZH{k~4&)ag2Hu*Ajk(Za!9w2`MQHEZI+`uOJDFWAHT8@Ky zVQHHjiNM+fEReEaj0TLi(VSiTBc%8rM3c#{q$c-~Iq$kH9 zo%lIw&)1^C>=^o26!vgN`t%Sd=R;%&rdjhGYYu>U*a?EB5fhy8Z^59 zfM#HUV$&C+*l2(1g_bedA6bC+qFCicv=1O1G>ASN#a^F4gVpnC&)B;6ePwmd3CKqDJD zN|DDRCH{+qXE_1|_Mb+8^!bLqEJarT<7gnc4q5ZI3HwEevh`@>9=;B_ifx%#SH_V` zw}N^(kyW4CL|wi-1e}{&uDw0-{Q$0DJBPtg>fU4~aksDO=34{bnFX6mO$qua8cnZ4 zKK2Wc@}9}$fYKWB=V)a6ALJ5!0J#LI3i%B*5|35?rYL<6xl)%Q>z2Z(z5`7+G|g@K zVjCNz$Rp9zzM&3VN}d|wVkZToT*3cMWg5XY(Yzy``5 zvh1G${u){L?;z{gj`$1<6JM97#bUV%9RrrRh$FQ7vcAvj zHpC4m*nJkVawnlt{%zH@?}uE*`=eB_yPV^d)GKQJ#tc2voybKw0VOt`Px`X(uOPSU z6{K6AQ&9&U-gclPA&VW8Maa$i9%=6HOk`=gtD0{k%#dn)b1$>rD0@M zABx=dPa*g4d8l81g9e_{(aMPfQJniYYRhE(x`j0HJtbdaM#2ZulxGE@-}ReC809n2 zL&g#ezR7h#<$~Y_=ljKob^Rv@m_I>T!Vgr>)I%NmI;2QVpzu@s*P%I`-=b0Y@eSwt zavB2k(@2ZMOpZtKK@K4~#}x!+#NLnEJ0;n(2GzrO2a0!}mUy4Tk!xm2jaSf|8h6`% z0rEs1jllIL#A4ZjV(XV70Ir~ahf7zc3Ru1uWHPP53R$dKg#FUl)%UUGAh8$l<@(6? z^O0Zu`083eN3O*zG*4z#Ywv?bzXO?^bu)0Ci0sj8wT0W$gYY z;~ePUDm6tOjNb8dyPnzJS2Jr^RBqpqNA0V$DL0*L1BEC2<=pmL{Ertkca% zfyPm4_GgHH_yOuFtp_7(b_kl2IROzl?};Xgn;7rD+>YYCg=kdGQTXH0L~h$8voAJh zm@2c;i!lWrL(2M-PManGJ9KryaNApVzSJ!EBPi#e~Da7qKa`ZX15vgfTwU{_^zQ-STcj=CZ*6j8 z!ZFYete9o=S9R<)S8zVcNX29(vUF{J|4j|Qxk(ON8`cZ_28z9&QhkO{N1uj9;{}Mc zx|zDd>Iy`OIRIswtcJKL^%qehd32al{oY1xX#%G&!_<{fn`EduW(bXR@1wtl#}&wJ zIR~+>zl~g)UiG}|Pzw7Q$hvJtZp%FS=|-Uu9prubUg{$3XhL2uM-4(hMwr#M#PZF5^AXX!}Rb_gw_8i%_!T zCgiSs3AwA=sP}A|joyDTBHym4KFh1`FGGbNZIirV(!i22N&Uen;^`_&ik=1@00?`5V*NOC>%l#^Bvu5*o_PjgqO+~) z81arzkh0_jgw1Okuoe=YBsQ@JH}@{3Ge(iJ`xSvgVqm(H$#X-#r5UPzUt4_^A(r%) zylYzV{F{)%UX85Z^8;a^BD3*0y7TwhWY zYzLFwy0-I1QNN_VXNP7&g2_zOZ*9QxCB}(OkaVtKiYo}sz*<5Yu;$V}spm>Au$G`~ zmPzVJG?&RKUZ0m1liM=x6Oz<-4Bcb%0QbFWfOFfjalY7IU8ypQ_Eb6ucun!xT*8K8 z2K{=U(SPm0iX|^%#Koo;T}%zEriUD**=6+4y}IRkzN|zFlX8xG%u@0}>|Up7^1SU? zt|+|kpy&EYh*X(FJC(?n<(c=JN7?|~HorQTFz{xQ&KB|{9a#PiV!&z|8J3XN*o#T0 zt3bc?@g=0N3n){zMGfb*0V8DXa^%th&MgH8q{qa*ETY-^gcuF0s0Xf;LC*%@wqxhh zo~;fIdM>asy^VBN(6?AIB*y?}cS{LsmyzcZgAkB0yC!XmGDM+r%0p-BjTzK9Z zlwoU9@TT8N0S)S_&|FRm*oKhOHv3v$_Y&P33ljcjCMT7JPGs&H?C-eHQT%B>O|Nesjt8JiZ>hOg$$DvKv zSV~!pwvO6jtYkCZL3J__}o->hdi>cNtbx%w>{?| z*3Qavm)v(F<-poVJFsH+FHP0E`qA`mi%H8eFC;JBZubnuScy^|WBH4~Ok(vg+~3c1hg#N*zNkYFbp*-QTiE?0VpgeJs>YlqYGvhoPfJ5T8 z;e6T$(O9X8X#>`T_pgc1{56TKE_Q!U@uQ7k*(BC@D;b>Dlq!oTpKxtsUo^>Tl&%k5 zvrWbV=cc-`8z^^SC&-Uc=96yejJug~&9>u01G!&~Y+77FA6PCy(e0!*Ue*HAR*L3W ztyScAzR{X|X;ZkB*+7%8xfW|J=`N1jaq9?MjuaV$Yq*KYz)H0` z9m?HTD9=caGI=0rd*r6co5SBBN(yLIa+ ziya0@1gs%1!{)a6AaN3C#BR&h#$=o_2EsD(E#(TreHnYc8Kk+_rPDy}dru3np#SBp zFpMvwOwPc#=Pe=4hh9vY+tfp@U}(m^Nm<+p%pBQBxy9N*4UXJY{TThvn|uBs?2DO{ zEnHkh^ZZ%p9b6)A%JFkav#3@fEG99~?Ga$PGMg~4+L**kNCQmH+8lS@D^=mv(%*rp z=WV16kUa#B(l}`?b$gT$Y8OyORtCB6C38|GgV$=Bt(>1#bz}A>iFsJD>zYg(uq-p@ zr`N6OtuG;q<;r5xELIM~eKGlSQWliwD(PDKmCQfC>3W+epKM)$M&?qM`U=V@Ms{uq zb7L*}z*!AW4@E zl!0;$c}b-n0!q%nmFn5Ftu~Hjx!r!A7K`O0I#ws3tr~8dMKj4|xaVXMK>>Littq7V zGZBzWQqap7toFqc$}Qd)gVCBw5;iMIE5+dczL2~Daw%yk{kFx9v1Ht|SuA@o>NB%pr~DvWPsZkir-ArIgmRoHEN{?p$f@i;a|L6*myi zwzXN4b(FQ!d<2plx7pnK)L3#E=}SkNJenAX)z$fFVP5z(*5*bYB(}wm$t={^ZJjwv z-pJlG{}GcIt*K2y!F;MLWiWRx6noaXWT!%Co$~^ja5^0krzF_PcIkYBM+=rP7q} z1qY!uxmmItaI}H4?Y^ZefoG#WEJ?b|rrriASC)_;kLH|{>e-~MA`XQ%(*`WFSbiPr z*o4~%bv^Q!%qDF@cV^;oP|g<&1?4FV2TQ9wjWIRsi(DC^oP9Awb5WTlS;=HiqOE;b zkj^ojOWNR@ve+@EYp!uglyr;b11lCwS-It=td7s`iZ%3n(o^Uw$yb`Sl&!5QS%zUU z2>W6=Y3cgd@5?E-cuQ!f8ik~-3|fpYm^w(#M1#0_NwMcxKzlXZ(C_a2ZTn6KyJawo z6?5locZZmak_MQzQq!(1BcBoBuQf`QK@tHW_C=~IIcU0mE#;tIO_|%A%NI0Kb*fuG zOxk*@1vIx?Y*mH}tZB1YE?An96#mLfU)oZicF!fGTcJhd^96HgO|J4UMs9F!$+aGe z1xwwz=}6b7ave+8p{*m|O6FKBkS?}m9_P{wzE}w~cbaKhfHjkTi#3aQW^Xplqr)Q7 zlw%p`7G*AKfSff)Mr&$wBYc@dT_HC0UM9;Zt1N3WNV3&U(IJa;Ae?)Gx>TG+AbqM#lM)m!7gQY8r$)*NoPaAa)}Ys zX0cpg#c1}>PQ=ZzSUIEJqRiu=y@Ya8wySiacTjh(6j-Y%Pr8&QdPb3_lM|25ug*zr z)0B|G_LMfS%fEJv@Zcux_8735Qk!kSN~v_4T0+<)x5h$`a$3_M6=K`mIamC4X-e0( zRZv<&oI%kh%PE^&o{6+9C$6+Da#KsE8Ndl9tZQ+}Gi7;mE!-q2XuoT#vUFjxN&Y*_VcXKmEeKTQlLDU+$M?UxE&B)T%8_It z0Xe{Y5abKTu>ZyQ!OkVhLgI`{?Y+Z{?-Mr!7g!9r=%*y;Sk9=Ichu_&@{-XND4c#5 zqikK!DcGeqm66@zAyMLa(_8zKszU~|l$0PtEGQpkB%|ne@)9n)cf zNx+}HhZVO!)|YixIoLB{9){e$xVe~w;Ows2=TE80P~K{M{g+^Xcq8RS;%MhG7aHnH z$#{9{@*DhQ*>zHjmGW_mzQWsh_eJiWOhA8XpAkQ7;snif`N%kUj>Zh53 zM~c&UrBY0G20Wyq$zm!T;Iqw4k^3)#>q5XuF4($?+c#Bu-o`vEV-~gJJ}y{27P(|U zFmY4)3aXaKF%xJTS2;f-#4X^Jauz@EJd3A z;{6@7_5;LCxUa$}mKLdy{TVLoGnWswjgx>Te_7Fa-sAw5FmbPN>vYckz)1%Ust}^= z%0a}@1f4SO?cHXSrN7q5x8Nzp)ix|m+P!L^i_qPY4o}((fm4aB8mW3^ z15wK(@=a|g)@NrUAuGg>?*iQJmM^(m^uOHXL;)$XHV+)|v?)xou4f?-5AV)W3`7|a zEW@PF%N0cK3mKg(ghT1Iwo6T*(8CKBN2DB@&HkvGSHSUV_DZBwJN=od;(NzO>tKs1sv{9KkV1Na z-R`pj4K{;6+ka3Onq4=mWBCgplk_EzD&ka9Y>9|V_PY~8y}ku4H0uCc`5Ai;%lv{4 zfOuTZy4XDD(Q*mc0@+<^^k$<$iWr*gusQxv>dhjq4y;3-m;HANp;Gu^O*=WM#FJi_$G{Flo2`br!_ z#xy6Zzk(N+9IiL((R1_Lo*%bDyz4dTxExsNCU~ku`sX&6u+$ukETadQPU*@se zo-=&Dj&0dIugDs7o`FD`g^}sf7LeCRDDUmY)?i-XLgd1Mr3L)HqJpdiWQpKvJd!_o z2+E%h2iyw0db@{ekNz~)eMP6hvTs=N5Z&=?7@OtM-f;hG2S8SI)zF(U8{5Rk(9|e} zM)@(GYHuqy&1~FKuhMNbtEhiIl~qBa4i=KLjCQttGLc@x$1cc&_0s7S+1wbN%=ysT z`AdIS(1E=E*;d?#y|+@r+o2PUlG??HoyWD}rP@we$nizr>^Du#Zac4D9Vd1k6upfy zO3pdj&~`m>uEDQp)x9UZMysL7Q_3BC7GTLr4dZnX!#4HFXjCo3MMM)msAHF36fd=B3-Gn;AaBUqFw>k zfcD44zLlTAcinH4MaOBrDH`K&L&WwB)CG9maS^qf*|7UWS4!;b>kuZT*?XpKvZvl! z)K*@9HlU*n!e7I0dFH&Rb+;fHANczXUb>47(P=oqu<$xYmj+%(qLuj#rFZT>2R8B( zh%)RFI7?k~Kpu3G(5 z!LeoUen;VL@3LCPmn02&3RNFGwMI6-tt0Q_^pQX3f$Mv`ewGa~nXllRI$${M;{;fqq5klI zE*{4_QUk->Go1MMa|bZ&;!SddTahFxve#dfS2_Zb6nK}AhfEe-NPW)YH1u+^l;q z&ZTc_#d}`8f8{{ml4vC{oE{e51q9$_PSS<0eQ|JA#;uk8}LdM*YF+%@hwF#5Qq@8^DPPd$R5)k zP|7uAUa^xARZ?AUXqc5|BtKklRunN8o*R{~GONfRfnXr;Qb5ZaQo_Qg#=>7HRbt|w z0)3ohlCf6aGVc>-r@v(|5OBKfsZvmaL@BMOuK&8q*&kqDUVQKRJch9+GWD%}LBFq$ zJKd6uymPar@Pt)Y0jupO{oj@ASTh=Tq^XX~VArtUBi6J>=*}h#H;1D^0p_uK-iVI3 zzFWw($j&)#AW+PKt8UAz`fUns>C{@*IF_i2oCvK>j;J@JYBdA(*kTWC3GKKN&$Yd~ z1(yHS2&xnH5ssLIUKa)k@6o}i5)Vmey5Wt-#r}#Clpor(6z(=ot!* zA}D{?@sB}~A`w|6B*l;_YYS_6;I;tvZPUCN_sRh#3+)e2UOL_KqWx+q3`+;IAAtH) zt$sFYdC^*e|NAt#I@Gm@f2g3{^~{d$X- zG`AaxheSiyrSpSBVW$ay0;F}uBI>H|4K7x{upO^YTM*(`CNeYJvbfez|O$k zJk3MfU+;;kD3GU`*c^{st5Q5SgA>Jy{*l;v3t20TS<$xOT%lo7A8mUy2ydaTQU~TZ z$U=Z5a?F|*I8ZPT87l2J(Tnvlj$6)xYziext%p})~VIXd-k*wCMW+<}X z)V5TD$BpxcR+wwM%9}ltBo@fZdDF#V#nO2)Us@S?Ush0AhxlxIdlNv#M{<)sf$>7% ziO!9-GroJCdM3H%x@Gjgaoo zmwL8b>>1^f8X7!cYW{cy!|pYpu&NuE2(yaIYd9X>XCvc}!!ARo$kDBa3@k-KQ4dH| zF0ycfwerBHXa=oTa9=)cRCqn>2W9r8+Ik-fkwzK`DhyS0LPi-JzA5rBBPW|3J!Hr8 z67BO)9fv?J7WC8jUkaw=qw=C*^k(%Io~KL3Yuqq&#@(7<3EAj3(t`TGqmO}Q@}%z) z)=SVp@R}Q$*PE2*p4b1b*|weq_ot4O$}@b#2*8HlNB z-BAYFBrlf+ckhUmVlS32)pgtH7_iNBgQerddSP$(vC9E(W0cF~&NaVTBVhLU zO6{|_o=ZpxY07A#o`Xk{h8BR}y6%hpQ^q<>P7JV$?2S)Emk&r0pZonU5 z5>Xes-J1C2;C0m*#JN7)Htr6l0-#R&Ni|62Zey|%t1C(dXPeg$yJ)B2UQBeaE!40j z(b18UQ65IpaCi_>ywRSf#PQ{ht^EEdD|uGeYExUZwC(84a2F=o_JbfWG$I=10|+S& z9ovCqSJ*I#lk%yeSt%T7O-Dt7?U>$RFKs9L!_MS~Y%(IBxa`|5#y__VwTjoQ7$u3b z8&tKDRgG?x9Uv*)`ZTS8z?g3&8RLn)N{w7GwcV-q&gFnB0-jgX`1W*iq~4?1Nz{hB?wDd$@A_Z zKBv4JPH>d7&{pxL~4)CX#^;bcP7h(o)gZ=d43 znv&@*-38%~stVsc;yIO_mQ4RWB*CmRGrt$t=bU8HBrXu_J?O}I+UHJK|7e9<^F*oZ z0yWb9OU;*eCtap4-1KaE$4HKm{*k~lpnqap)ll%L=RycgwVR-Poj=7Njd?b@&+3ep zADNjRjY<0186*+rop&m{5MvP0FUjf^mh_)r5537!xGMek*-6ZtIOn%Lp&wHDN@HRX9Gq0MZtqFv_1`-kcUo z6)Gqqp|Ko%p%hnvtCikg24h)w?ZLAxq?~L*F{MT>C`Up zf1-#3YQSt52p?}wm$BW%)<-|Y+0ugUk%EPo_s6R~+CSZQ|0jNUNQ_9}9}UOU3=wig z<$&gkjHDiX4I9X1k|1?>OY-k8C!Wo{BubJhCR%^cPQua>Bb~Ck5c}jJW11#fvt>y) z`7y32c{#c3Y@zU1FK3{S?mIpCK%y%C!M|f3t`hGAGg?d)E@fx+>xlyk1~`i%VT8By zB!Uc`O|*mkGLL@LIF6trJ(g9&BxX_ z+e-Gxd%^3CPaa*o-#3GkaLZ%n`F?HyagKMh6Wk*CgI@JXs&6v}VE)*KfuLY3nGn8e zcO*N0{od0LiZu8ggzEQK643HrP2wOSYvlCkZN6$ulsGV41Fmei0umJBFQw_@R%-sE z-xwe}A)8%eZ=?61bMAC6NmN{{G3&z%X&r5SO9g2nNJnt>mUJ$&3SFD%7hGluhSiLTq4%6ZsfLIY=<{vnL)Dvgt{bDw96nZ}9w*=?oSnD%GN#|{_v<@-== zLqvcE>Gt^Op}`!=udQV_PPg$gYq2j`s^aXJj-9OatoNvo3If9a+_AAJmo;z$#h|=+ z1GmyAUmM(?9}_$nOH(~u&A|#qS_lMr0D>(5r`$>8s|)p4;a=;1GxbRSm4B)5;_|^; zi_6;-P)L8Jo8x^w>19AWUaVTm%%a+ja=XH#dz8uv&c0RKRBq@t=>$B_o$K2e*Sp78 zE%n5$wUCbMMSxbm_{+Pe|A<+5*Jd*uyG^ zuz!f!!;dtEEoQ9W<7yoB^X%)kywxYO;xUkpDc} z3oaBYIWO_UySMWaeU{t&6cY-Y(@m8h3825+9WICqb$R)11niB-9_^_sKm>EmgMZsw z<6fP__n<8tn0=#^xu7t3Ow!&#=KGjn`);L`%zn$DwAUm_@B;+UbRz0q61Bll1ip?yI458j($7L>D-Hj@IzrpY;v_WC1;{(zJ9$MA$8~+L(3!yU5$&hg@@jd z?|KYTf+I1T!nlnrG*y(!bQGvKKyQ{%;<%7F#J=eVe!AY9eS1SQb9&6|i8+6jv&vhoId5$gCnFR|%SUl&*k$&LmiRp#9ot&qhkoqXqS1 zP9i9yo-{5JaMVL2GNXaLDK{R|Jj#+a(!J?y19K#nNxC^ZTEbG})4a#y5-l^I4NTP|?ElyLk*!K*y%_T=NN;x#f|UE|)@qkvYnQc;UQ|dM>L0(B93xYeCKl zwB(n=$9bF|NH}#ywM~PU{`_e8jcOTf`P@Z*NUHIqHsmt$54? zTHowWsZ*uhpLsSAhXzvm53@+cxGjXfJtmDLWH^y+8(3yh;R392{C_xDRtv|?P|>s* zupjYG?^Sw!VC_=UKvDKPYM+i>Yf``9TxdarOnH1_?$%4IRFAXvTk64cYxfHEr0cA5=atF2Yp$qO$En@!XW6r@_~U zdl9@qzZ*9y$}Kxw>z{<0aKLoj5c`^kt4(fE;{34tfz@Z~!2{D~V#uhMn`(o-^349t zFNQYQt11NEh$jiI#&aqL7ax*bRwN4Q5glq!K%|UM8^`$`-d_J(s`iQR*jCtUrU?Qv zL=QIB^isJ`jou||RS&j&RDN11&K{bOI4rUzkM}zuo9PCAQ`ul1xFgUy-Ly}Y``-d( zndB?nb=*o1LYedAe4tels#l|_ms-5GFM5(TG|->Y zI(ogAhxx}{{EhFC6)1`PRa-A?0{3RFMmQ)XJwS`nl4|In8Z)wZGFWA}+X`{-D>c5b zK-9)Ym?%@1TJRxR5(myXdimuhx~Mt|c4a=l`<_&BAX8H))!8F2-O~#b+YORDvB7(- zlUI3_cvK~DDR&EfQlPGO`)`VJf(H(*=B@}ivH0ui-zaJkidoGTXRcMryXv_V!LMqb z{o1Yv*BAop@QpT)3SMSYxbVS7!k5@x?WLi*{`z*gzXcuiiS@BZ46P1L7}N#Ec}sI{ zX9DgAghB5sF{6)0#C013OV!=1pRWG#8vjlS|M5~tHE_-pz2MOJOB)I5K|rrm!E zT|dq>10de;+8@@?bQ)J}N617i^DGBj=RaRQqzLG}7`85dP;Mw*SE$fRLXN=h<+v?D z^4j_-Yr5Gcwf^jLLua?9_WzY*35ju(FR<00x`xeZ%m${IK-oTLS#BhZMsAv6OM798 z^kiFPQgH_!9TDc;+kuzi90`*s@lG}a@^>dZ&u{;Dy!BG4M0O~kToT}6T)d@!aEb+h zewuR$6Hs#3M^MjQl1WD4$J>EUjO2c_DTCQF8_kEB0E%Im7k4RflB|@d+A>a4lE3=u z)vNC(p`Gx$Fzsxc+JV6sowZW@PX?z@qhf}E z1}#Dw6xAui*v$aA6ZH3|6T2thlMy?@SHS&raz-|4V0cd&w(OhcH{vaVl^{}MU8t(9 zsc6kgd=)CNJLD2EQn~>7Ik&=4{CMaQzE_F)NB>S;*Y)>lhG9nn0B`W8s{`-d$*yX; zarNYL@ogjT-}_lA?=e3pwTgH6nlR5cY7q(?#oT7YL&NBfaT#P=-ZE=#waBU$FCaf8 z=py`vh3rY0|2bf+;`4JDIm@1K(q@uJPiWW5fog)XtjZ7FH6{F~9Qrz^jdsR>p(WUG z?>BSJ={$wGy1=K@y|@R>C*^hXFJPq^2C_0hNwKeUWpxVsMf96&FMaU6Da=1C4ktR< z%h&FaOXTeFo`$M+sCbNi=bngiAsgU>IZ==P@&TxR!YekQK_mTKJ)X! zONBI{;1(WZHxmxabvufg2fi|-lb}WJORFuwK8TYOHttbKl=nFO3oZ?_bLwM7tO4D1Va&O6|)0TJOqR z7SHUwQ&-siliu;*jCrZ@@qq3PUW>~%ij*<0XKAkzr!b3nvmI#e%tH4X&H%UtjK)53 z<7i6Rps0!m96*R(t+gG~l!3eq%PJl8mzI&m#CPhH!5X&h>xvkUQ3)jVMDA)-d?D&G z-L>Qt$>VL6C5HJ0>Is?(G5*gch9c)5nk7CxVJyF__J%lNe>}8AOafX3Th9IPz-N1R z(mT0Gzurv=IPv%JW!U;@;__;+bKsf{{%h5^>fXjqBdvKG5$FH4ireX~qhe@Pgh|I= zhh(gq)xfoDdRE4>&E?7O``F#fBRpZMHoT`0C~kyS{$O7u`>Crh0n znyCF~kjF-m{z*~W9@(I(zx7+wv<)+57*@usLZbLBe>d=lwf+Z(lPmPMzxHz+?*T(k z3x6Ly=zZS)*yH4RS1@5>d1;?+Xqm~ZCRVwJa;Y~{e&W@W_%FB1k83q%t5RxR8eaYG zbzy!0u`0$K8^HKuuut8Rok_cC+IgF27Vwv1Y=?SZv?AHA-9dJ$)@d?PEcGgkqf>5v z(r|sV_XQu3V2RP#)m1nD&_wn?^_ZIrHVd{f3FhCA{hr5IabAWWe+k#}@wkOsebjb- z)JZRjT-CFU>rEYhsi#iCZX-hOM zWkrPk1TE)MKrf{ANoyTnt(oN#b;c2N^{D&Lh?Z@RRu>JC!i3yG1T8lICWo(85O%20 zL^05^13x=H-cg?0`MuJMJ6K^RUAl1LSYQ~l*X$%ndiZ;9Sj~1H86m?!dhsu^*!IrE kwPa_k$$eVaYuBDn!VCv~rX-WDsJN!5{q%8-rfu~90lC)HL;wH) literal 0 HcmV?d00001 diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json new file mode 100644 index 0000000000..f38b34cb13 --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema", + "title": "EXT_mesh_primitive_edge_visibility glTF extension", + "type": "object", + "description": "glTF extension encoding the visibility of the edges of a triangles primitive", + "allOf": [ + { + "$ref": "glTFProperty.schema.json" + } + ], + "properties": { + "visibility": { + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ], + "description": "The index of the accessor encoding the visibility of each triangle edge as a bitfield with two bits per edge.", + "gltf_detailedDescription": "An edge visibility of 0 indicates the edge is never drawn; 1 indicates the edge is only drawn in silhouette; 2 indicates the edge is always drawn; and 3 indicates the edge is always drawn and can be drawn by a primitive defined in `primitives`." + }, + "silhouetteNormals": { + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ], + "description": "The index of the accessor encoding the pair of outward-facing normals for the two faces sharing each silhouette edge (visibility 1) encoded in `visibility`.", + "gltf_detailedDescription": "###TODO. This property **MUST** be defined if `visibility` encodes any edge with visibility value `1`." + }, + "primitives": { + "type": "array", + "description": "An array of primitives defining geometry that renders all of the edges encoded as `3` in `visibility`.", + "gltf_detailedDescription": "###TODO. This property **MUST** be defined if `visibility` encodes any edge with visibility value `3`", + "items": { + "$ref": "mesh.primitive.schema.json" + }, + "minItems": 1 + }, + "extensions": {}, + "extras": {} + }, + "required": [ + "visibility" + ] +} \ No newline at end of file From 771816c4634516a813e447ac60b27a025b424ddf Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Fri, 14 Mar 2025 04:26:30 -0400 Subject: [PATCH 02/39] tweaks --- .../README.md | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index fe6b3b936d..437c9c3043 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -29,7 +29,7 @@ Figure 1 illustrates a typical rendering of a cylinder with its edges. The width This image shows both of the two types of edges described by `EXT_mesh_primitive_edge_visibility`. -- A [silhouette edge](https://en.wikipedia.org/wiki/Silhouette_edge) is any edge separating a front-facing triangle from a back-facing triangle. In Figure 1, one silhouette is visible along each of the curved left and right sides of the cylinder. Silhouette edges are *conditionally visible* - their visibility is determined at display time based on the camera direction. In Figure 2, each of the vertical edges encircling the cylinder represents a potential silhouette edge. +- A [silhouette edge](https://en.wikipedia.org/wiki/Silhouette_edge) is any edge separating a front-facing triangle from a back-facing triangle. In Figure 1, one silhouette is visible along each of the curved left and right sides of the cylinder. Silhouette edges are *conditionally visible* - their visibility is determined at display time based on the orientations of the adjacent triangles relative to the camera. In Figure 2, each of the vertical edges encircling the cylinder represents a potential silhouette edge. - A hard edge is any edge attached to only a single triangle, or any edge between two logical faces of the 3D object. In Figure 1, hard edges are visible around the perimeters of the cylinder's circular end caps. Hard edges are *always visible* regardless of the camera direction. ## Shortcomings of Existing Techniques @@ -41,7 +41,7 @@ Various techniques can be applied to a glTF asset to approximate the rendering i
    Figure 2 Triangle edges drawn using a wiremesh technique
    -Screen-space techniques (e.g., a "toon" or "outline" shader) can be applied to add edges during image post-processing. Such techniques can only approximate the actual edges. The technique used in Figure 3, for example, fails to reconstruct the edges along the top end cap of the cylinder where they fall inside the cylinder's volume projected onto the image plane. +Screen-space techniques (e.g., "toon" or "outline" shaders) can be applied to add edges during image post-processing. Because they operate on pixels rather than geometry, such techniques can only approximate the actual edges. The technique used in Figure 3, for example, fails to reconstruct the edges along the upper end cap of the cylinder where they fall inside the cylinder's volume projected onto the image plane.
    @@ -55,10 +55,10 @@ The hard edges could be encoded explicitly into the glTF asset as additional pri
    Figure 4 Edges drawn as separate primitives
    -The [CESIUM_primitive_outline](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/CESIUM_primitive_outline) provides an additional index buffer describing each visible edge as a line segment (a pair of indices into the triangle mesh's list of vertices). The extension leaves the details of how to render the edges up to the engine, requiring only that they render without depth-fighting. This approach satisfies the use case for which it was intended - displaying the edges of boxy, low-resolution buildings - but suffers some limitations: +The [CESIUM_primitive_outline](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/CESIUM_primitive_outline) extension provides an additional index buffer describing each hard edge as a line segment (a pair of indices into the triangle mesh's list of vertices). The extension leaves the details of how to render the edges up to the engine, requiring only that they render without depth-fighting. This approach satisfies the use case for which it was intended - displaying the edges of boxy, low-resolution buildings - but suffers some limitations: - The surface geometry must be represented as indexed triangles. - It only supports hard edges, not silhouettes. -- The representation of the edges are pairs of vertex indices can significantly increase the size of the glTF asset. +- The representation of the edges as pairs of vertex indices can significantly increase the size of the glTF asset. - The edges cannot specify their own materials.
    @@ -74,11 +74,11 @@ The `EXT_mesh_primitive_edge_visibility` extension is applied to a mesh primitiv The visibility of a single edge is specified using 2 bits as one of the following visibility values: - 0: Hidden edge - the edge should never be drawn. -- 1: Silhouette edge - the edge should be drawn only in silhouette - i.e., when separating a front-facing triangle from a back-facing triangle. +- 1: Silhouette edge - the edge should be drawn only in silhouette (i.e., when separating a front-facing triangle from a back-facing triangle). - 2: Hard edge - the edge should always be drawn. -- 3: Hard edge encoded in `primitives` - a representation of the edge is included in the extension's `primitives` property. The edge should always be drawn, either by drawing the `primitives` array or by drawing it like an edge with visibility value `2`. +- 3: Hard edge encoded in `primitives` - a representation of the edge is included in the extension's `primitives` property. The edge should always be drawn, either by drawing the `primitives` array or by drawing it like an edge with visibility value `2` (never both). -The extension's `visibility` property specifies the index of an accessor of `SCALAR` type and `UNSIGNED BYTE` component type that encodes as a bitfield the visibility of every edge of every triangle in the mesh. The ordering of triangles and vertices is as described by [Section 3.7.2.1](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the glTF 2.0 specification. For each triangle `(v0, v1, v2)`, the bitfield encodes three visibility values for the edges `(v0:v1, v1:v2, v2:v0)` in that order. Therefore, the number of bytes in the bitfield **MUST** be `6 * N / 8` rounded up to the nearest whole number, where `N` is the number of triangles in the primitive. +The extension's `visibility` property specifies the index of an accessor of `SCALAR` type and component type 5121 (unsigned byte) that encodes as a bitfield the visibility of every edge of every triangle in the mesh. The ordering of triangles and vertices is as described by [Section 3.7.2.1](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the glTF 2.0 specification. For each triangle `(v0, v1, v2)`, the bitfield encodes three visibility values for the edges `(v0:v1, v1:v2, v2:v0)` in that order. Therefore, the accessor's `count` **MUST** be `6 * N / 8` rounded up to the nearest whole number, where `N` is the number of triangles in the primitive. The visibility of a given edge shared by a given pair of triangles **MUST** be encoded as a non-zero value no more than once. All other occurrences of the same edge in the bitfield **MUST** be encoded as zero, to prevent engines from either producing redundant geometry for shared edges or having to manually detect and account for such redundancies. @@ -105,7 +105,7 @@ Byte 0 1 Bit 6 4 2 0 14 12 10 8 ``` -Assume that the vertical edges `0:1` and `2:3` are hard edges, the horizontal edges `0:3` and `1:2` are hidden edges, and the shared diagonal edge `0:2` is a silhouette edge. Then the corresponding visibility values would be `[2,0,1, 1,2,0]`. However, we must ensure that the visibility of the shared edge is encoded only once, so after replacing one of the redundant `1`s the visibility values are `[2,0,1, 0,2,0]`. Encoding the edge visibility produces the sequence of bytes `[18, 2]`, as illustrated below. +Assume that the vertical edges `0:1` and `2:3` are hard edges, the horizontal edges `3:0` and `1:2` are hidden edges, and the shared diagonal edge `0:2` is a silhouette edge. Then the corresponding visibility values would be `[2,0,1, 1,2,0]`. However, we must ensure that the visibility of the shared edge is encoded only once, so after replacing one of the redundant `1`s the visibility values are `[2,0,1, 0,2,0]`. Encoding the edge visibility produces the sequence of bytes `[18, 2]`, as illustrated below. ``` Byte 0 1 @@ -119,17 +119,19 @@ Decimal 18 2 The extension's `primitives` property optionally provides an array of primitives representing all of the hard edges encoded with visibility value `3` in the `visibility` property. Engines should render these primitives directly, in which case they **MUST** do so without depth-fighting and **MUST NOT** also produce their own rendering of these edges. The primary use case is for edges that should be drawn using a different material than the corresponding surface - for example, a red outline drawn around a filled green shape. -The `BENTLEY_primitive_restart` extension *may* be applied to the `primitives` property. - The `primitives` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `3` in `visibility`. ### Silhouette Normals -The extension's `silhouetteNormals` property specifies the index of an accessor providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using the "oct16" encoding described [here](https://jcgt.org/published/0003/02/01/). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. +The extension's `silhouetteNormals` property specifies the index of an accessor of `SCALAR` type and component type 5123 (unsigned short) providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using the "oct16" encoding described [here](https://jcgt.org/published/0003/02/01/). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. Engines should render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. -The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. The buffer's byte length must be equal to `4 * N` where `N` is the number of edges encoded with visibility value `1`. +The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. + +### Extensions + +The `BENTLEY_primitive_restart` extension *may* be applied to the `EXT_mesh_primitive_edge_visibility` extension object, in which case it applies to the `primitives` property in the same way that it would apply to `mesh.primitives`. ## JSON Schema From 9159f1939631c433ea5be341a41fa99cd2ba02eb Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Fri, 14 Mar 2025 04:27:18 -0400 Subject: [PATCH 03/39] copyright --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 437c9c3043..91bf1f2fc2 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -1,5 +1,5 @@ From 75095711e21d89f2181adde4a70f2cbc76ce0115 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Fri, 14 Mar 2025 04:37:38 -0400 Subject: [PATCH 04/39] address schema TODOs --- .../schema/primitive.BENTLEY_edge_visibility.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json index f38b34cb13..da28c83a2e 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json @@ -25,12 +25,12 @@ } ], "description": "The index of the accessor encoding the pair of outward-facing normals for the two faces sharing each silhouette edge (visibility 1) encoded in `visibility`.", - "gltf_detailedDescription": "###TODO. This property **MUST** be defined if `visibility` encodes any edge with visibility value `1`." + "gltf_detailedDescription": "This property **MUST** be defined if and only if `visibility` encodes at least one edge with visibility value `1`." }, "primitives": { "type": "array", "description": "An array of primitives defining geometry that renders all of the edges encoded as `3` in `visibility`.", - "gltf_detailedDescription": "###TODO. This property **MUST** be defined if `visibility` encodes any edge with visibility value `3`", + "gltf_detailedDescription": "This property **MUST** be defined if and only if `visibility` encodes at least one edge with visibility value `3`", "items": { "$ref": "mesh.primitive.schema.json" }, From 2bf3eea345bffe8ab513ada2033852d5d863db03 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:38:36 -0400 Subject: [PATCH 05/39] require zero initialization. --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 91bf1f2fc2..62dbc87e18 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -78,7 +78,7 @@ The visibility of a single edge is specified using 2 bits as one of the followin - 2: Hard edge - the edge should always be drawn. - 3: Hard edge encoded in `primitives` - a representation of the edge is included in the extension's `primitives` property. The edge should always be drawn, either by drawing the `primitives` array or by drawing it like an edge with visibility value `2` (never both). -The extension's `visibility` property specifies the index of an accessor of `SCALAR` type and component type 5121 (unsigned byte) that encodes as a bitfield the visibility of every edge of every triangle in the mesh. The ordering of triangles and vertices is as described by [Section 3.7.2.1](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the glTF 2.0 specification. For each triangle `(v0, v1, v2)`, the bitfield encodes three visibility values for the edges `(v0:v1, v1:v2, v2:v0)` in that order. Therefore, the accessor's `count` **MUST** be `6 * N / 8` rounded up to the nearest whole number, where `N` is the number of triangles in the primitive. +The extension's `visibility` property specifies the index of an accessor of `SCALAR` type and component type 5121 (unsigned byte) that encodes as a bitfield the visibility of every edge of every triangle in the mesh. The ordering of triangles and vertices is as described by [Section 3.7.2.1](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the glTF 2.0 specification. For each triangle `(v0, v1, v2)`, the bitfield encodes three visibility values for the edges `(v0:v1, v1:v2, v2:v0)` in that order. Therefore, the accessor's `count` **MUST** be `6 * N / 8` rounded up to the nearest whole number, where `N` is the number of triangles in the primitive. Any unused bits in the last byte **MUST** be set to zero. The visibility of a given edge shared by a given pair of triangles **MUST** be encoded as a non-zero value no more than once. All other occurrences of the same edge in the bitfield **MUST** be encoded as zero, to prevent engines from either producing redundant geometry for shared edges or having to manually detect and account for such redundancies. From 6809e9bf022821759008b112dd9da2d65ad8b6e0 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:17:15 -0400 Subject: [PATCH 06/39] WIP revise --- .../README.md | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 62dbc87e18..40cbdec6fe 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -15,7 +15,7 @@ Draft ## Dependencies -Written against the glTF 2.0 spec. Depends on `BENTLEY_primitive_restart` if primitive restart is used. +Written against the glTF 2.0 spec. ## Overview @@ -68,7 +68,7 @@ The [CESIUM_primitive_outline](https://github.com/KhronosGroup/glTF/tree/main/ex ## glTF Schema Updates -The `EXT_mesh_primitive_edge_visibility` extension is applied to a mesh primitive of topology type 4 (triangles), 5 (triangle strip), or 6 (triangle fan) that uses indexed or non-indexed geometry. It provides the information required for engines to produce a rendering of the primitive's edges. Engines are not required to *always* render the edges - they may, for example, permit the user to toggle edge display on and off. When rendering the edges, engines may use any technique that meets the requirements specified below. +The `EXT_mesh_primitive_edge_visibility` extension is applied to a mesh primitive of topology type 4 (triangles), 5 (triangle strip), or 6 (triangle fan) that uses indexed or non-indexed geometry. It provides the information required for engines to produce a rendering of the primitive's edges. Engines are not required to *always* render the edges - they may, for example, permit the user to toggle edge display on and off. When rendering the edges, engines may use any techniques that meet the requirements specified below. ### Edge Visibility @@ -76,13 +76,17 @@ The visibility of a single edge is specified using 2 bits as one of the followin - 0: Hidden edge - the edge should never be drawn. - 1: Silhouette edge - the edge should be drawn only in silhouette (i.e., when separating a front-facing triangle from a back-facing triangle). - 2: Hard edge - the edge should always be drawn. -- 3: Hard edge encoded in `primitives` - a representation of the edge is included in the extension's `primitives` property. The edge should always be drawn, either by drawing the `primitives` array or by drawing it like an edge with visibility value `2` (never both). +- 3: Repeated hard edge - the edge should always be drawn, and its visibility is already encoded as `2` for an adjoining triangle. The extension's `visibility` property specifies the index of an accessor of `SCALAR` type and component type 5121 (unsigned byte) that encodes as a bitfield the visibility of every edge of every triangle in the mesh. The ordering of triangles and vertices is as described by [Section 3.7.2.1](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the glTF 2.0 specification. For each triangle `(v0, v1, v2)`, the bitfield encodes three visibility values for the edges `(v0:v1, v1:v2, v2:v0)` in that order. Therefore, the accessor's `count` **MUST** be `6 * N / 8` rounded up to the nearest whole number, where `N` is the number of triangles in the primitive. Any unused bits in the last byte **MUST** be set to zero. -The visibility of a given edge shared by a given pair of triangles **MUST** be encoded as a non-zero value no more than once. All other occurrences of the same edge in the bitfield **MUST** be encoded as zero, to prevent engines from either producing redundant geometry for shared edges or having to manually detect and account for such redundancies. +Edges are often shared between multiple adjacent triangles, which could lead to redunant encoding of their visibilities. For example, if the shared edge between two triangles was encoded as `2` twice, a client might produce two graphical representations of the edge when one would suffice, or be forced to manually detect and rectify the redundancy. The following rules enable clients to handle shared edges more efficiently: +- A hard edge **MUST** be encoded as `2` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `3`. +- A silhouette edge **MUST** be encoded as `1` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `0`. Silhouette edges are shared by definition, and clients can directly access the opposite triangle via the [silhouetteMates](#silhouette-mates) property. -Engines **MUST** render all edges according to their specified visibility values. The edges **MUST** draw in front of their corresponding triangles with no depth-fighting. Engines may choose a material with which to draw the edges - e.g., they could apply the primitive's material to the edges, or apply some uniform material to the edges of all primitives in the scene, or some other consistent option. If engines choose to render the edges specified by the `primitives` property, then they **MUST** render those primitives according to the glTF 2.0 specification (e.g., using the specified material) and they **MUST NOT** also produce their own rendering of the edges with visibility value `3`. +The bitfield **MUST** contain at least one non-zero visibility value. + +The `visibility` property **MUST** be defined if `lineStrings` is not defined - otherwise, the extension would encode no edges. #### Example @@ -95,13 +99,15 @@ Consider the simple example of a pair of adjacent triangles described by the ind 1 2 ``` +TODO fix these examples + With 2 bits per edge, encoding the visibility of two triangles requires 12 bits. So the visibility buffer must be 2 bytes long, with 4 bits of the second byte going unused. The diagram below shows which edge's visibility will be encoded into each pair of bits. ``` Byte 0 1 - ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ - │0:2│2:0│1:2│0:1│ │ │ │3:0│2:3│ - └───┴───┴───┴───┘ └───┴───┴───┴───┘ + ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ + │0:2│2:0│1:2│0:1│ │ │ │3:0│2:3│ + └───┴───┴───┴───┘ └───┴───┴───┴───┘ Bit 6 4 2 0 14 12 10 8 ``` @@ -115,13 +121,32 @@ Binary │ 00│ 01│ 00│ 10│ │ 00│ 00│ 00│ 10│ Decimal 18 2 ``` -### Edge Primitives +TODO add example for hard diagonal edge + +### Material + +The extension's `material` property optionally specifies the index of a glTF material with which the edges are to be drawn. If unspecified, the edges should be drawn using the same material as the triangle mesh primitive. + +### Line Strings + +The representation of hard edges in the `visibility` property - while compact - presents some drawbacks for certain use cases: It requires that all of the encoded edges draw using the same material, and it does not capture the connectivity between individual line segments. For example, the perimeters of the top and bottom faces of the cylinder in Figure 1 could each be represented as a single circular line string. Perhaps the outline of the top face should be drawn in a different color than that of the bottom face. Or perhaps the outlines are to be drawn with a dash pattern along the length of the line string, or as wide lines into which a client might insert additional triangles to smooth out the joints between line segments. -The extension's `primitives` property optionally provides an array of primitives representing all of the hard edges encoded with visibility value `3` in the `visibility` property. Engines should render these primitives directly, in which case they **MUST** do so without depth-fighting and **MUST NOT** also produce their own rendering of these edges. The primary use case is for edges that should be drawn using a different material than the corresponding surface - for example, a red outline drawn around a filled green shape. +The extension's `lineStrings` property provides an alternate representation for some or all of the hard edges that would otherwise be encoded in `visibility`. It is an array in which each entry is an object with the following properties: +- `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as a series of two or more indices. Multiple line strings can be encoded by inserting a "primitive restart" value (that is, the maximal possible value for the accessor's component type, like 255 for `UNSIGNED_BYTE`) between each line string. The `indices` buffer **MUST NOT** begin or end with a primitive restart value, and **MUST NOT** contain consecutive primitive restart values. +- `material` (optional): the index of the material with which the edges encoded by `indices` are to be drawn. If omitted, the extension's own `material` property is used, if defined; otherwise, the material of the triangle mesh primitive is used. -The `primitives` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `3` in `visibility`. +A hard edge encoded in `lineStrings` **MUST NOT** also be encoded in `visibility`. + +The `lineStrings` property **MUST** be defined if `visibility` is not defined - otherwise, the extension would encode no edges. + +#### Example + +TODO + +### Silhouette Mates + +A silhouette represents the edge between two vertices shared by a pair of adjacent triangles. -### Silhouette Normals The extension's `silhouetteNormals` property specifies the index of an accessor of `SCALAR` type and component type 5123 (unsigned short) providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using the "oct16" encoding described [here](https://jcgt.org/published/0003/02/01/). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. @@ -129,9 +154,9 @@ Engines should render a silhouette edge unless both adjacent triangles are front The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. -### Extensions +## TODO -The `BENTLEY_primitive_restart` extension *may* be applied to the `EXT_mesh_primitive_edge_visibility` extension object, in which case it applies to the `primitives` property in the same way that it would apply to `mesh.primitives`. +Engines **MUST** render all edges according to their specified visibility values. The edges **MUST** draw in front of their corresponding triangles with no depth-fighting. Engines may choose a material with which to draw the edges - e.g., they could apply the primitive's material to the edges, or apply some uniform material to the edges of all primitives in the scene, or some other consistent option. If engines choose to render the edges specified by the `primitives` property, then they **MUST** render those primitives according to the glTF 2.0 specification (e.g., using the specified material) and they **MUST NOT** also produce their own rendering of the edges with visibility value `3`. ## JSON Schema @@ -139,12 +164,6 @@ The `BENTLEY_primitive_restart` extension *may* be applied to the `EXT_mesh_prim ## Implementation Notes -Example routines for encoding and decoding of oct16 normal vectors: - -- [GLSL](https://jcgt.org/published/0003/02/01/) (Section 3.1) -- [JavaScript](https://github.com/CesiumGS/cesium/blob/dd9c25b652460e8cd77ecd29fbbf7cc44d8b62a2/packages/engine/Source/Core/AttributeCompression.js#L77) -- [C++](https://github.com/iTwin/imodel-native/blob/4f0a5008dba61124b490a7506a6cd82ce1b8f148/iModelCore/iModelPlatform/PublicAPI/DgnPlatform/Render.h#L2012) - The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` and `silhouetteNormals` buffers. iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It draws edges in a separate pass from surfaces to [ensure](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426) the mitigate z-fighting. From 00ed7a39d4804e39f8fb6c476ab5ee101e3bd52f Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:37:16 -0400 Subject: [PATCH 07/39] lineStrings --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 40cbdec6fe..6c764990a9 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -129,7 +129,7 @@ The extension's `material` property optionally specifies the index of a glTF mat ### Line Strings -The representation of hard edges in the `visibility` property - while compact - presents some drawbacks for certain use cases: It requires that all of the encoded edges draw using the same material, and it does not capture the connectivity between individual line segments. For example, the perimeters of the top and bottom faces of the cylinder in Figure 1 could each be represented as a single circular line string. Perhaps the outline of the top face should be drawn in a different color than that of the bottom face. Or perhaps the outlines are to be drawn with a dash pattern along the length of the line string, or as wide lines into which a client might insert additional triangles to smooth out the joints between line segments. +The representation of hard edges in the `visibility` property - while compact - presents some drawbacks for certain use cases: it requires that all of the encoded edges draw using the same material, and it does not capture the connectivity between individual line segments. For example, the perimeters of the top and bottom faces of the cylinder in Figure 1 are each logically a single continuous circle. Perhaps the outline of the top face should be drawn in a different color than that of the bottom face, which would require a way to specify a different material. Or perhaps the outlines are to be drawn with a dash pattern along their entire lengths, or as wide lines into which a client might insert additional triangles to smooth out the joints between line segments - both of which require connectivity information. The extension's `lineStrings` property provides an alternate representation for some or all of the hard edges that would otherwise be encoded in `visibility`. It is an array in which each entry is an object with the following properties: - `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as a series of two or more indices. Multiple line strings can be encoded by inserting a "primitive restart" value (that is, the maximal possible value for the accessor's component type, like 255 for `UNSIGNED_BYTE`) between each line string. The `indices` buffer **MUST NOT** begin or end with a primitive restart value, and **MUST NOT** contain consecutive primitive restart values. From ee0d6673faa5fa9fd8463929c1ab42f19b7a0630 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:53:02 -0400 Subject: [PATCH 08/39] silhouettes --- .../EXT_mesh_primitive_edge_visibility/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 6c764990a9..a4d8320278 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -145,14 +145,17 @@ TODO ### Silhouette Mates -A silhouette represents the edge between two vertices shared by a pair of adjacent triangles. +A silhouette edge represents the edge between two vertices shared by a pair of adjacent triangles. A client can directly obtain the indices of one of those triangles based on the `visibility` encoding, but to conditionally render the silhouette it also needs to know the third (unshared) vertex of the other triangle so that it can determine each triangle's facing relative to the camera. +The extension's `silhouetteMates` property specifices the index of an accessor of `SCALAR` type and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding indices into the triangle mesh's vertex attribute array. Each index refers to the unshared vertex of the other triangle adjacent to the `nth` silhouette edge encoded in `visibility`. The accessor's `count` **MUST** be equal to the number of edges encoded with visibility value `1`. -The extension's `silhouetteNormals` property specifies the index of an accessor of `SCALAR` type and component type 5123 (unsigned short) providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using the "oct16" encoding described [here](https://jcgt.org/published/0003/02/01/). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. +Engines **MUST** render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. -Engines should render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. +The `silhouetteMates` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. -The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. +#### Example + +TODO ## TODO From 9dfdb7386c430772ed6886b462e5b5ee95f49999 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 16:44:38 -0400 Subject: [PATCH 09/39] WIP example --- .../README.md | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index a4d8320278..7918b29cb0 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -88,19 +88,17 @@ The bitfield **MUST** contain at least one non-zero visibility value. The `visibility` property **MUST** be defined if `lineStrings` is not defined - otherwise, the extension would encode no edges. -#### Example +#### Examples Consider the simple example of a pair of adjacent triangles described by the index list `[0,1,2, 0,2,3]`: ``` 0______3 - | ⟍ | - | ⟍ | - |_____⟍| + |\ | + | \ | + |____\| 1 2 ``` -TODO fix these examples - With 2 bits per edge, encoding the visibility of two triangles requires 12 bits. So the visibility buffer must be 2 bytes long, with 4 bits of the second byte going unused. The diagram below shows which edge's visibility will be encoded into each pair of bits. ``` @@ -111,7 +109,9 @@ Byte 0 1 Bit 6 4 2 0 14 12 10 8 ``` -Assume that the vertical edges `0:1` and `2:3` are hard edges, the horizontal edges `3:0` and `1:2` are hidden edges, and the shared diagonal edge `0:2` is a silhouette edge. Then the corresponding visibility values would be `[2,0,1, 1,2,0]`. However, we must ensure that the visibility of the shared edge is encoded only once, so after replacing one of the redundant `1`s the visibility values are `[2,0,1, 0,2,0]`. Encoding the edge visibility produces the sequence of bytes `[18, 2]`, as illustrated below. +##### Shared silhouette edge + +Assume that the vertical edges `0:1` and `2:3` are hard edges, the horizontal edges `3:0` and `1:2` are hidden edges, and the shared diagonal edge `0:2` is a silhouette edge. Then the corresponding visibility values would be `[2,0,1, 1,2,0]`. However, we must ensure that the visibility of the silhouette edge is encoded only once, so after replacing one of the redundant `1`s the visibility values are `[2,0,1, 0,2,0]`. Encoding the edge visibility produces the sequence of bytes `[18, 2]`, as illustrated below. ``` Byte 0 1 @@ -121,7 +121,16 @@ Binary │ 00│ 01│ 00│ 10│ │ 00│ 00│ 00│ 10│ Decimal 18 2 ``` -TODO add example for hard diagonal edge +##### Shared hard edge + +Assume that the vertical edges `0:1` and `2:3` and the shared diagonal edge `0:2` are all hard edges, and the horizontal edges `3:0` and `1:2` are hidden edges. Then the corresponding visibility values would be `[2,0,2, 2,2,0]`. However, we must ensure that the visibility of the shared hard edge is encoded only once as `2`, with the other occurrence encoded as `3`, resulting in visibility values `[2,0,2, 3,2,0]`. Encoding the edge visibility produces the sequence of bytes `[226, 2]`, as illustrated below. +``` +Byte 0 1 + ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ +Binary │ 11│ 10│ 00│ 10│ │ 00│ 00│ 00│ 10│ + └───┴───┴───┴───┘ └───┴───┴───┴───┘ +Decimal 226 2 +``` ### Material @@ -139,9 +148,34 @@ A hard edge encoded in `lineStrings` **MUST NOT** also be encoded in `visibility The `lineStrings` property **MUST** be defined if `visibility` is not defined - otherwise, the extension would encode no edges. -#### Example +#### Examples -TODO +Consider the following simple triangle fan with triangles described by the index list `[0,1,2, 0,2,3, 0,3,4, 0,4,5]`: +``` + 2______3______4 + |\ | /| + | \ | / | + |____\|/____| + 1 0 5 +``` + +Assume that the top and bottom edges are hard edges, to be encoded as a pair of line strings with indices `[2,3,4]` and `[1,0,5]`, respectively; and the vertical edges are hard edges, to be encoded as simple line segments. Then the `visibility` bitfield should encode only the vertical edges, with visibility values `[0,2,0, 0,0,2, 3,0,0, 0,2,0]`. With 2 bits per edge, encoding the visibility of four triangles requires 24 bits (4 bytes, with no bits to spare). The diagram below shows which edge's visibility will be encoded into each pair of bits. +``` +Byte 0 1 2 + ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ + │0:2│2:0│1:2│0:1│ │3:4│0:3│3:0│2:3│ │5:0│4:5│0:4│4:0│ + └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ +Bit 6 4 2 0 14 12 10 8 22 20 18 16 +``` + +With 2 bits per edge, encoding the visibility of four triangles requires 24 bits (4 bytes, with no bits to spare). +``` +Byte 0 1 + ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ +Binary │ 00│ 01│ 00│ 10│ │ 00│ 00│ 00│ 10│ + └───┴───┴───┴───┘ └───┴───┴───┴───┘ +Decimal 18 2 +``` ### Silhouette Mates From 35c328b0e0e51c66a84f2a70a988553406d6bb5e Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 16:52:19 -0400 Subject: [PATCH 10/39] lineStrings examples --- .../README.md | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 7918b29cb0..dc6004c444 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -159,22 +159,37 @@ Consider the following simple triangle fan with triangles described by the index 1 0 5 ``` -Assume that the top and bottom edges are hard edges, to be encoded as a pair of line strings with indices `[2,3,4]` and `[1,0,5]`, respectively; and the vertical edges are hard edges, to be encoded as simple line segments. Then the `visibility` bitfield should encode only the vertical edges, with visibility values `[0,2,0, 0,0,2, 3,0,0, 0,2,0]`. With 2 bits per edge, encoding the visibility of four triangles requires 24 bits (4 bytes, with no bits to spare). The diagram below shows which edge's visibility will be encoded into each pair of bits. +Assume that the top and bottom edges are hard edges, to be encoded as a pair of line strings with indices `[2,3,4]` and `[1,0,5]`, respectively; and the vertical edges are hard edges, to be encoded as simple line segments. Then the `visibility` bitfield should encode only the vertical edges, with visibility values `[0,2,0, 0,0,2, 3,0,0, 0,2,0]`. + +##### Single material + +Assume that the top and bottom edges are to be drawn using the same material, different from the material used to draw the vertical edges, and the indices are encoded as unsigned bytes. Then the index buffer would look like `[2,3,4, 255, 1,0,5]`, where `255` is the primitive restart value separating the two line strings. The `lineStrings` property would look something like this: ``` -Byte 0 1 2 - ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ - │0:2│2:0│1:2│0:1│ │3:4│0:3│3:0│2:3│ │5:0│4:5│0:4│4:0│ - └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ -Bit 6 4 2 0 14 12 10 8 22 20 18 16 +{ + "lineStrings": [ + { + "indices": 5, + "material": 1 + } + ] +} ``` -With 2 bits per edge, encoding the visibility of four triangles requires 24 bits (4 bytes, with no bits to spare). +##### Two materials + +Assume that the top edge is to be drawn using the same material as the vertical edges, and the bottom edge is to be drawn using a different material. Then there would be two index buffers - `[2,3,4]` and `[1,0,5]` - and the `lineStrings` property would look something like this: ``` -Byte 0 1 - ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ -Binary │ 00│ 01│ 00│ 10│ │ 00│ 00│ 00│ 10│ - └───┴───┴───┴───┘ └───┴───┴───┴───┘ -Decimal 18 2 +{ + "lineStrings": [ + { + "indices": 5 + }, + { + "indices": 6, + "material": 1 + } + ] +} ``` ### Silhouette Mates From 102f154726d1dfb997a4d5f6a645ebacd12524c6 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:05:09 -0400 Subject: [PATCH 11/39] WIP update schema --- .../README.md | 9 ++--- .../schema/lineString.schema.json | 40 +++++++++++++++++++ ...mitive.BENTLEY_edge_visibility.schema.json | 8 ++-- 3 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index dc6004c444..7c72da7ce0 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -198,13 +198,9 @@ A silhouette edge represents the edge between two vertices shared by a pair of a The extension's `silhouetteMates` property specifices the index of an accessor of `SCALAR` type and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding indices into the triangle mesh's vertex attribute array. Each index refers to the unshared vertex of the other triangle adjacent to the `nth` silhouette edge encoded in `visibility`. The accessor's `count` **MUST** be equal to the number of edges encoded with visibility value `1`. -Engines **MUST** render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. - The `silhouetteMates` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. -#### Example - -TODO +Engines **MUST** render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. ## TODO @@ -212,7 +208,8 @@ Engines **MUST** render all edges according to their specified visibility values ## JSON Schema -[./schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json](primitive.EXT_mesh_primitive_edge_visibility.schema.json) +- [primitive.EXT_mesh_primitive_edge_visibility.schema.json](./schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json) +- [lineString.schema.json](./schema/lineString.schema.json) ## Implementation Notes diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json new file mode 100644 index 0000000000..59fb51275e --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema", + "$id": "lineString", + "title": "Line String", + "type": "object", + "description": "TODO", + "allOf": [ + { + "$ref": "glTFProperty.schema.json" + } + ], + "properties": { + "primitives": { + "type": "array", + "items": { + "$ref": "glTFid.schema.json" + }, + "uniqueItems": true, + "minItems": 1, + "description": "The indices of the primitives that will be combined and drawn using a single set of indices", + "gltf_detailedDescription": "The indices of the primitives that will be combined and drawn using a single set of indices. All properties except for `indices` (e.g., `material`, `mode`, and `attributes`) will be obtained from the first primitive in the list, and all other primitives in the list **MUST** have identical values for those properties. The `mode` must be TRIANGLE_FAN, TRIANGLE_STRIP, LINE_LOOP, or LINE_STRIP." + }, + "indices": { + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ], + "description": "The index of the accessor that contains the vertex indices", + "gltf_detailedDescription": "The index of the accessor that contains the vertex indices. The accessor **MUST** have `SCALAR` type and an unsigned integer component type. The indices are permitted to include the maximal index value for the component type, indicating the start of a new primitive." + }, + "name": {}, + "extensions": {}, + "extras": {} + }, + "required": [ + "primitives", + "indices" + ] +} \ No newline at end of file diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json index da28c83a2e..441bab5798 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json @@ -16,18 +16,18 @@ } ], "description": "The index of the accessor encoding the visibility of each triangle edge as a bitfield with two bits per edge.", - "gltf_detailedDescription": "An edge visibility of 0 indicates the edge is never drawn; 1 indicates the edge is only drawn in silhouette; 2 indicates the edge is always drawn; and 3 indicates the edge is always drawn and can be drawn by a primitive defined in `primitives`." + "gltf_detailedDescription": "An edge visibility of 0 indicates the edge is never drawn; 1 indicates the edge is only drawn in silhouette; 2 indicates a 'hard' (always-drawn) edge; and 3 indicates a repeated occurrence of a 'hard' edge." }, - "silhouetteNormals": { + "silhouetteMates": { "allOf": [ { "$ref": "glTFid.schema.json" } ], - "description": "The index of the accessor encoding the pair of outward-facing normals for the two faces sharing each silhouette edge (visibility 1) encoded in `visibility`.", + "description": "The index of the accessor encoding the index of the fourth vertex for the pair of triangles sharing each silhouette edge (visibility 1) encoded in `visibility`.", "gltf_detailedDescription": "This property **MUST** be defined if and only if `visibility` encodes at least one edge with visibility value `1`." }, - "primitives": { + "lineStrings": { "type": "array", "description": "An array of primitives defining geometry that renders all of the edges encoded as `3` in `visibility`.", "gltf_detailedDescription": "This property **MUST** be defined if and only if `visibility` encodes at least one edge with visibility value `3`", From 35301856152ee013336355e43bde68e3689a01f2 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:15:00 -0400 Subject: [PATCH 12/39] update schema --- .../schema/lineString.schema.json | 26 +++++++++---------- ...mitive.BENTLEY_edge_visibility.schema.json | 20 +++++++++----- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json index 59fb51275e..539c69eda3 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json @@ -3,38 +3,36 @@ "$id": "lineString", "title": "Line String", "type": "object", - "description": "TODO", + "description": "Represents one or more line strings to be drawn as edges of a triangle mesh", "allOf": [ { "$ref": "glTFProperty.schema.json" } ], "properties": { - "primitives": { - "type": "array", - "items": { - "$ref": "glTFid.schema.json" - }, - "uniqueItems": true, - "minItems": 1, - "description": "The indices of the primitives that will be combined and drawn using a single set of indices", - "gltf_detailedDescription": "The indices of the primitives that will be combined and drawn using a single set of indices. All properties except for `indices` (e.g., `material`, `mode`, and `attributes`) will be obtained from the first primitive in the list, and all other primitives in the list **MUST** have identical values for those properties. The `mode` must be TRIANGLE_FAN, TRIANGLE_STRIP, LINE_LOOP, or LINE_STRIP." - }, "indices": { "allOf": [ { "$ref": "glTFid.schema.json" } ], - "description": "The index of the accessor that contains the vertex indices", - "gltf_detailedDescription": "The index of the accessor that contains the vertex indices. The accessor **MUST** have `SCALAR` type and an unsigned integer component type. The indices are permitted to include the maximal index value for the component type, indicating the start of a new primitive." + "description": "The index of the accessor that contains the vertex indices for the line strings", + "gltf_detailedDescription": "The index of the accessor that contains the vertex indices. The accessor **MUST** have `SCALAR` type and an unsigned integer component type. The indices are permitted to include the maximal index value for the component type, indicating the start of a new line string." + }, + "material": { + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ], + "description": "The index of a glTF material with which the edges are to be drawn.", + "gltf_detailedDescription": "The index of a glTF material with which the edges are to be drawn. If omitted, the edges are drawn using the same material as the edges encoded by the extension's `visibility` field, or else that of the triangle mesh primitive." }, "name": {}, "extensions": {}, "extras": {} }, "required": [ - "primitives", "indices" ] } \ No newline at end of file diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json index 441bab5798..929a3e9732 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json @@ -18,6 +18,15 @@ "description": "The index of the accessor encoding the visibility of each triangle edge as a bitfield with two bits per edge.", "gltf_detailedDescription": "An edge visibility of 0 indicates the edge is never drawn; 1 indicates the edge is only drawn in silhouette; 2 indicates a 'hard' (always-drawn) edge; and 3 indicates a repeated occurrence of a 'hard' edge." }, + "material": { + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ], + "description": "The index of a glTF material with which the edges are to be drawn.", + "gltf_detailedDescription": "The index of a glTF material with which the edges are to be drawn. If omitted, the edges are drawn using the same material as the triangle mesh primitive." + }, "silhouetteMates": { "allOf": [ { @@ -29,17 +38,14 @@ }, "lineStrings": { "type": "array", - "description": "An array of primitives defining geometry that renders all of the edges encoded as `3` in `visibility`.", - "gltf_detailedDescription": "This property **MUST** be defined if and only if `visibility` encodes at least one edge with visibility value `3`", + "description": "An array of edges encoded as line strings.", + "gltf_detailedDescription": "Edges encoded as line strings **MUST NOT** also be encoded by the `visibility` property.", "items": { - "$ref": "mesh.primitive.schema.json" + "$ref": "lineString.schema.json" }, "minItems": 1 }, "extensions": {}, "extras": {} - }, - "required": [ - "visibility" - ] + } } \ No newline at end of file From 81491902fa5910fe8252324ecbfd31c716883162 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:16:35 -0400 Subject: [PATCH 13/39] /s/client/engine --- .../Vendor/EXT_mesh_primitive_edge_visibility/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 7c72da7ce0..3149ad84f2 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -80,9 +80,9 @@ The visibility of a single edge is specified using 2 bits as one of the followin The extension's `visibility` property specifies the index of an accessor of `SCALAR` type and component type 5121 (unsigned byte) that encodes as a bitfield the visibility of every edge of every triangle in the mesh. The ordering of triangles and vertices is as described by [Section 3.7.2.1](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the glTF 2.0 specification. For each triangle `(v0, v1, v2)`, the bitfield encodes three visibility values for the edges `(v0:v1, v1:v2, v2:v0)` in that order. Therefore, the accessor's `count` **MUST** be `6 * N / 8` rounded up to the nearest whole number, where `N` is the number of triangles in the primitive. Any unused bits in the last byte **MUST** be set to zero. -Edges are often shared between multiple adjacent triangles, which could lead to redunant encoding of their visibilities. For example, if the shared edge between two triangles was encoded as `2` twice, a client might produce two graphical representations of the edge when one would suffice, or be forced to manually detect and rectify the redundancy. The following rules enable clients to handle shared edges more efficiently: +Edges are often shared between multiple adjacent triangles, which could lead to redunant encoding of their visibilities. For example, if the shared edge between two triangles was encoded as `2` twice, an engine might produce two graphical representations of the edge when one would suffice, or be forced to manually detect and rectify the redundancy. The following rules enable engines to handle shared edges more efficiently: - A hard edge **MUST** be encoded as `2` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `3`. -- A silhouette edge **MUST** be encoded as `1` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `0`. Silhouette edges are shared by definition, and clients can directly access the opposite triangle via the [silhouetteMates](#silhouette-mates) property. +- A silhouette edge **MUST** be encoded as `1` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `0`. Silhouette edges are shared by definition, and engines can directly access the opposite triangle via the [silhouetteMates](#silhouette-mates) property. The bitfield **MUST** contain at least one non-zero visibility value. @@ -138,7 +138,7 @@ The extension's `material` property optionally specifies the index of a glTF mat ### Line Strings -The representation of hard edges in the `visibility` property - while compact - presents some drawbacks for certain use cases: it requires that all of the encoded edges draw using the same material, and it does not capture the connectivity between individual line segments. For example, the perimeters of the top and bottom faces of the cylinder in Figure 1 are each logically a single continuous circle. Perhaps the outline of the top face should be drawn in a different color than that of the bottom face, which would require a way to specify a different material. Or perhaps the outlines are to be drawn with a dash pattern along their entire lengths, or as wide lines into which a client might insert additional triangles to smooth out the joints between line segments - both of which require connectivity information. +The representation of hard edges in the `visibility` property - while compact - presents some drawbacks for certain use cases: it requires that all of the encoded edges draw using the same material, and it does not capture the connectivity between individual line segments. For example, the perimeters of the top and bottom faces of the cylinder in Figure 1 are each logically a single continuous circle. Perhaps the outline of the top face should be drawn in a different color than that of the bottom face, which would require a way to specify a different material. Or perhaps the outlines are to be drawn with a dash pattern along their entire lengths, or as wide lines into which an engine might insert additional triangles to smooth out the joints between line segments - both of which require connectivity information. The extension's `lineStrings` property provides an alternate representation for some or all of the hard edges that would otherwise be encoded in `visibility`. It is an array in which each entry is an object with the following properties: - `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as a series of two or more indices. Multiple line strings can be encoded by inserting a "primitive restart" value (that is, the maximal possible value for the accessor's component type, like 255 for `UNSIGNED_BYTE`) between each line string. The `indices` buffer **MUST NOT** begin or end with a primitive restart value, and **MUST NOT** contain consecutive primitive restart values. @@ -194,7 +194,7 @@ Assume that the top edge is to be drawn using the same material as the vertical ### Silhouette Mates -A silhouette edge represents the edge between two vertices shared by a pair of adjacent triangles. A client can directly obtain the indices of one of those triangles based on the `visibility` encoding, but to conditionally render the silhouette it also needs to know the third (unshared) vertex of the other triangle so that it can determine each triangle's facing relative to the camera. +A silhouette edge represents the edge between two vertices shared by a pair of adjacent triangles. An engine can directly obtain the indices of one of those triangles based on the `visibility` encoding, but to conditionally render the silhouette it also needs to know the third (unshared) vertex of the other triangle so that it can determine each triangle's facing relative to the camera. The extension's `silhouetteMates` property specifices the index of an accessor of `SCALAR` type and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding indices into the triangle mesh's vertex attribute array. Each index refers to the unshared vertex of the other triangle adjacent to the `nth` silhouette edge encoded in `visibility`. The accessor's `count` **MUST** be equal to the number of edges encoded with visibility value `1`. From 7b23b6688d92def47675ff1fbe2befd43089119b Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:22:24 -0400 Subject: [PATCH 14/39] rendering requirements --- .../EXT_mesh_primitive_edge_visibility/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 3149ad84f2..71ba53a25f 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -202,9 +202,12 @@ The `silhouetteMates` property **MUST** be defined *if and only if* at least one Engines **MUST** render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. -## TODO +## Rendering requirements -Engines **MUST** render all edges according to their specified visibility values. The edges **MUST** draw in front of their corresponding triangles with no depth-fighting. Engines may choose a material with which to draw the edges - e.g., they could apply the primitive's material to the edges, or apply some uniform material to the edges of all primitives in the scene, or some other consistent option. If engines choose to render the edges specified by the `primitives` property, then they **MUST** render those primitives according to the glTF 2.0 specification (e.g., using the specified material) and they **MUST NOT** also produce their own rendering of the edges with visibility value `3`. +- Engines **MUST** render all edges according to their specified visibility values, though some engines may permit the user to toggle the display of edges on and off. +- The edges **MUST** draw in front of their corresponding triangles with no depth-fighting. +- The edges **MUST** draw using the materials specified by the extension by default, though some engines may provide options for the user to override those materials. +- Each silhouette edge **MUST** be rendered unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. ## JSON Schema @@ -213,6 +216,6 @@ Engines **MUST** render all edges according to their specified visibility values ## Implementation Notes -The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` and `silhouetteNormals` buffers. +The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` buffer. -iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It draws edges in a separate pass from surfaces to [ensure](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426) the mitigate z-fighting. +iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It also draws edges in a separate pass from surfaces to [ensure](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426) the mitigate z-fighting. From a5fb7238952ff26eee382c6384872bd380ed8dc3 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Sun, 11 May 2025 12:40:47 -0400 Subject: [PATCH 15/39] rename schema --- ...n => primitive.EXT_mesh_primitive_edge_visibility.schema.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/{primitive.BENTLEY_edge_visibility.schema.json => primitive.EXT_mesh_primitive_edge_visibility.schema.json} (100%) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json similarity index 100% rename from extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.BENTLEY_edge_visibility.schema.json rename to extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json From fff86b455ba39ec6b61aae1aaae345161748669c Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Mon, 12 May 2025 11:34:43 -0400 Subject: [PATCH 16/39] grammar --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 71ba53a25f..d1436a59be 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -218,4 +218,4 @@ Engines **MUST** render a silhouette edge unless both adjacent triangles are fro The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` buffer. -iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It also draws edges in a separate pass from surfaces to [ensure](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426) the mitigate z-fighting. +iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It also draws edges in a separate pass from surfaces to [mitigate z-fighting](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426). From c449bbf10467fb38595cfbb34f08749bf1ca96e2 Mon Sep 17 00:00:00 2001 From: Paul Connelly <22944042+pmconne@users.noreply.github.com> Date: Wed, 28 May 2025 18:30:20 -0400 Subject: [PATCH 17/39] Expand title Co-authored-by: Aaron Franke --- .../primitive.EXT_mesh_primitive_edge_visibility.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json index 929a3e9732..d15cf48b4b 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema", - "title": "EXT_mesh_primitive_edge_visibility glTF extension", + "title": "EXT_mesh_primitive_edge_visibility glTF Mesh Primitive Extension", "type": "object", "description": "glTF extension encoding the visibility of the edges of a triangles primitive", "allOf": [ From 5df0dd9858d91004115cb3fdbcf119f22b59be48 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Fri, 30 May 2025 10:56:59 -0400 Subject: [PATCH 18/39] fix winding order --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index d1436a59be..ff74d6cfc9 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -150,7 +150,7 @@ The `lineStrings` property **MUST** be defined if `visibility` is not defined - #### Examples -Consider the following simple triangle fan with triangles described by the index list `[0,1,2, 0,2,3, 0,3,4, 0,4,5]`: +Consider the following simple triangle fan with triangles described by the index list `[0,2,1, 0,3,2, 0,4,3, 0,5,4]`: ``` 2______3______4 |\ | /| @@ -159,7 +159,7 @@ Consider the following simple triangle fan with triangles described by the index 1 0 5 ``` -Assume that the top and bottom edges are hard edges, to be encoded as a pair of line strings with indices `[2,3,4]` and `[1,0,5]`, respectively; and the vertical edges are hard edges, to be encoded as simple line segments. Then the `visibility` bitfield should encode only the vertical edges, with visibility values `[0,2,0, 0,0,2, 3,0,0, 0,2,0]`. +Assume that the top and bottom edges are hard edges, to be encoded as a pair of line strings with indices `[2,3,4]` and `[1,0,5]`, respectively; and the vertical edges are hard edges, to be encoded as simple line segments. Then the `visibility` bitfield should encode only the vertical edges, with visibility values `[0,2,0, 2,0,0, 0,0,3, 0,2,0]`. ##### Single material From 96aa219b4099574a4f98dbfdbc52ba7c755e04ab Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Fri, 30 May 2025 11:01:28 -0400 Subject: [PATCH 19/39] update json schema --- .../schema/lineString.schema.json | 4 ++-- .../primitive.EXT_mesh_primitive_edge_visibility.schema.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json index 539c69eda3..2c541f738d 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "lineString", "title": "Line String", "type": "object", @@ -35,4 +35,4 @@ "required": [ "indices" ] -} \ No newline at end of file +} diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json index d15cf48b4b..9ce0684702 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "EXT_mesh_primitive_edge_visibility glTF Mesh Primitive Extension", "type": "object", "description": "glTF extension encoding the visibility of the edges of a triangles primitive", @@ -48,4 +48,4 @@ "extensions": {}, "extras": {} } -} \ No newline at end of file +} From 9746de89847695cd8b9f05b3544f7a3e3c2a7599 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Thu, 31 Jul 2025 13:29:30 -0400 Subject: [PATCH 20/39] silhouette mates => silhouette normals --- .../EXT_mesh_primitive_edge_visibility/README.md | 10 ++++------ ...tive.EXT_mesh_primitive_edge_visibility.schema.json | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index ff74d6cfc9..dc65398a1e 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -82,7 +82,7 @@ The extension's `visibility` property specifies the index of an accessor of `SCA Edges are often shared between multiple adjacent triangles, which could lead to redunant encoding of their visibilities. For example, if the shared edge between two triangles was encoded as `2` twice, an engine might produce two graphical representations of the edge when one would suffice, or be forced to manually detect and rectify the redundancy. The following rules enable engines to handle shared edges more efficiently: - A hard edge **MUST** be encoded as `2` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `3`. -- A silhouette edge **MUST** be encoded as `1` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `0`. Silhouette edges are shared by definition, and engines can directly access the opposite triangle via the [silhouetteMates](#silhouette-mates) property. +- A silhouette edge **MUST** be encoded as `1` exactly once in `visibility`. All other occurrences of the same edge **MUST** be encoded as `0` The bitfield **MUST** contain at least one non-zero visibility value. @@ -192,13 +192,11 @@ Assume that the top edge is to be drawn using the same material as the vertical } ``` -### Silhouette Mates +### Silhouette Normals -A silhouette edge represents the edge between two vertices shared by a pair of adjacent triangles. An engine can directly obtain the indices of one of those triangles based on the `visibility` encoding, but to conditionally render the silhouette it also needs to know the third (unshared) vertex of the other triangle so that it can determine each triangle's facing relative to the camera. +The extension's `silhouetteNormals` property specifies the index of an accessor of `SCALAR` type and component type 5123 (unsigned short) providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using the "oct16" encoding described [here](https://jcgt.org/published/0003/02/01/). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. -The extension's `silhouetteMates` property specifices the index of an accessor of `SCALAR` type and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding indices into the triangle mesh's vertex attribute array. Each index refers to the unshared vertex of the other triangle adjacent to the `nth` silhouette edge encoded in `visibility`. The accessor's `count` **MUST** be equal to the number of edges encoded with visibility value `1`. - -The `silhouetteMates` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. +The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. Engines **MUST** render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json index 9ce0684702..0f9a4ecf26 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json @@ -27,13 +27,13 @@ "description": "The index of a glTF material with which the edges are to be drawn.", "gltf_detailedDescription": "The index of a glTF material with which the edges are to be drawn. If omitted, the edges are drawn using the same material as the triangle mesh primitive." }, - "silhouetteMates": { + "silhouetteNormals": { "allOf": [ { "$ref": "glTFid.schema.json" } ], - "description": "The index of the accessor encoding the index of the fourth vertex for the pair of triangles sharing each silhouette edge (visibility 1) encoded in `visibility`.", + "description": "The index of the accessor encoding the pair of outward-facing normals for the two faces sharing each silhouette edge (visibility 1) encoded in `visibility`.", "gltf_detailedDescription": "This property **MUST** be defined if and only if `visibility` encodes at least one edge with visibility value `1`." }, "lineStrings": { @@ -48,4 +48,4 @@ "extensions": {}, "extras": {} } -} +} \ No newline at end of file From bf816db25226376fc9cce03df98d04238b58b95d Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:25:55 -0400 Subject: [PATCH 21/39] oct16 --- .../README.md | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index dc65398a1e..998f299b8a 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -194,7 +194,72 @@ Assume that the top edge is to be drawn using the same material as the vertical ### Silhouette Normals -The extension's `silhouetteNormals` property specifies the index of an accessor of `SCALAR` type and component type 5123 (unsigned short) providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using the "oct16" encoding described [here](https://jcgt.org/published/0003/02/01/). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. +The extension's `silhouetteNormals` property specifies the index of an accessor of `SCALAR` type and component type 5123 (unsigned short) providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using an "oct16" encoding. The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. + +### oct16 encoding + +Octahedral normal encoding projects a normal vector onto an octahedron and folds it in half, mapping to two signed normalized components in [-1, 1]. The "oct16" form of this encoding maps each component to the integer range [0, 255], permitting a normal vector to be encoded into two bytes. + +TypeScript implementation of oct16 encoding and decoding: + +```ts +// A three-dimensional vector. +interface Vec3d { x: number; y: number; z: number; } + +// Normalize a 3d vector to a length of 1. +function normalize(vec: Vec3d): void { + const magnitude = Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z); + const scale = magnitude === 0 ? 0 : 1 / magnitude; + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; +} + +// Map a signed normalized value in [-1, 1] to the integer range [0, 255]. +function toSNorm(val: number): number { + const clamped = Math.min(Math.max(val, -1), 1); + return Math.round((clamped * 0.5 + 0.5) * 255); +} + +// Return -1 for a negative input, otherwise 1. +function signNotZero(val: number): number { + return val < 0.0 ? -1.0 : 1.0; +} + +// Encode a normalized 3d vector into a 16-bit unsigned integer. +function oct16EncodeNormal(n: Vec3d): number { + const denom = Math.abs(n.x) + Math.abs(n.y) + Math.abs(n.z); + let rx = n.x / denom; + let ry = n.y / denom; + if (n.z < 0) { + const x = rx; + const y = ry; + rx = (1 - Math.abs(y)) * signNotZero(x); + ry = (1 - Math.abs(x)) * signNotZero(y); + } + + return toSNorm(ry) << 8 | toSNorm(rx); +} + +// Decode a 16-bit unsigned integer into a normalized 3d vector. +function oct16DecodeNormal(oct16: number): Vec3d { + let x = oct16 & 0xff; + let y = oct16 >> 8; + x = x / 255.0 * 2.0 - 1.0; + y = y / 255.0 * 2.0 - 1.0; + const z = 1 - (Math.abs(x) + Math.abs(y)); + + const n: Vec3d = { x, y, z }; + if (z < 0) { + n.x = (1 - Math.abs(y)) * signNotZero(x); + n.y = (1 - Math.abs(x)) * signNotZero(y); + } + + normalize(n); + return n; +} + +``` The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. @@ -212,8 +277,16 @@ Engines **MUST** render a silhouette edge unless both adjacent triangles are fro - [primitive.EXT_mesh_primitive_edge_visibility.schema.json](./schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json) - [lineString.schema.json](./schema/lineString.schema.json) +## Known implementations + +- [iTwin.js](https://github.com/iTwin/itwinjs-core/pull/8366) + ## Implementation Notes The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` buffer. iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It also draws edges in a separate pass from surfaces to [mitigate z-fighting](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426). + +## References + +- The oct16 normal vector encoding is described in detail in [A Survey of Efficient Representations for Independent Unit Vectors](https://jcgt.org/published/0003/02/01/). From e3e4cc5ca14a85a67251a5779033b3eab4700efe Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Wed, 13 Aug 2025 09:47:03 -0400 Subject: [PATCH 22/39] update silhouette normal encoding --- .../README.md | 73 +------------------ 1 file changed, 1 insertion(+), 72 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 998f299b8a..bcb208c2e1 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -194,77 +194,10 @@ Assume that the top edge is to be drawn using the same material as the vertical ### Silhouette Normals -The extension's `silhouetteNormals` property specifies the index of an accessor of `SCALAR` type and component type 5123 (unsigned short) providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. Each normal vector is compressed into 16 bits using an "oct16" encoding. The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. - -### oct16 encoding - -Octahedral normal encoding projects a normal vector onto an octahedron and folds it in half, mapping to two signed normalized components in [-1, 1]. The "oct16" form of this encoding maps each component to the integer range [0, 255], permitting a normal vector to be encoded into two bytes. - -TypeScript implementation of oct16 encoding and decoding: - -```ts -// A three-dimensional vector. -interface Vec3d { x: number; y: number; z: number; } - -// Normalize a 3d vector to a length of 1. -function normalize(vec: Vec3d): void { - const magnitude = Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z); - const scale = magnitude === 0 ? 0 : 1 / magnitude; - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; -} - -// Map a signed normalized value in [-1, 1] to the integer range [0, 255]. -function toSNorm(val: number): number { - const clamped = Math.min(Math.max(val, -1), 1); - return Math.round((clamped * 0.5 + 0.5) * 255); -} - -// Return -1 for a negative input, otherwise 1. -function signNotZero(val: number): number { - return val < 0.0 ? -1.0 : 1.0; -} - -// Encode a normalized 3d vector into a 16-bit unsigned integer. -function oct16EncodeNormal(n: Vec3d): number { - const denom = Math.abs(n.x) + Math.abs(n.y) + Math.abs(n.z); - let rx = n.x / denom; - let ry = n.y / denom; - if (n.z < 0) { - const x = rx; - const y = ry; - rx = (1 - Math.abs(y)) * signNotZero(x); - ry = (1 - Math.abs(x)) * signNotZero(y); - } - - return toSNorm(ry) << 8 | toSNorm(rx); -} - -// Decode a 16-bit unsigned integer into a normalized 3d vector. -function oct16DecodeNormal(oct16: number): Vec3d { - let x = oct16 & 0xff; - let y = oct16 >> 8; - x = x / 255.0 * 2.0 - 1.0; - y = y / 255.0 * 2.0 - 1.0; - const z = 1 - (Math.abs(x) + Math.abs(y)); - - const n: Vec3d = { x, y, z }; - if (z < 0) { - n.x = (1 - Math.abs(y)) * signNotZero(x); - n.y = (1 - Math.abs(x)) * signNotZero(y); - } - - normalize(n); - return n; -} - -``` +The extension's `silhouetteNormals` property specifies the index of an accessor of type `VEC3` providing normal vectors used to determine the visibility of silhouette edges at display time. For each edge encoded as a silhouette (visibility value `1`) in `visibility`, the silhouette normals buffer provides the two outward-facing normal vectors of the pair of triangles sharing the edge. The normal vectors **MUST** be normalized and use component type 5126 (float), 5120 (byte), or 5122 (short). The ordering of the normal vector pairs corresponds to the ordering of the edges in `visibility`; that is, the first pair of normals corresponds to the first edge encoded with visibility `1`, the second pair to the second occurrence of visibility `1`; and so on. The accessor's `count` **MUST** be twice the number of edges encoded with visibility value `1`. The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`. -Engines **MUST** render a silhouette edge unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. - ## Rendering requirements - Engines **MUST** render all edges according to their specified visibility values, though some engines may permit the user to toggle the display of edges on and off. @@ -286,7 +219,3 @@ Engines **MUST** render a silhouette edge unless both adjacent triangles are fro The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` buffer. iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It also draws edges in a separate pass from surfaces to [mitigate z-fighting](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426). - -## References - -- The oct16 normal vector encoding is described in detail in [A Survey of Efficient Representations for Independent Unit Vectors](https://jcgt.org/published/0003/02/01/). From 07b8d8d2f0b53442b606c819c734a5a4522b8dc5 Mon Sep 17 00:00:00 2001 From: pmconne <22944042+pmconne@users.noreply.github.com> Date: Wed, 13 Aug 2025 09:50:46 -0400 Subject: [PATCH 23/39] implementation note re silhouette normals --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index bcb208c2e1..b74b5d6a5b 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -219,3 +219,5 @@ The `silhouetteNormals` property **MUST** be defined *if and only if* at least o The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informed the design of this extension provides [an iterator](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/common/imdl/CompactEdges.ts#L42) over the `visibility` buffer. iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It also draws edges in a separate pass from surfaces to [mitigate z-fighting](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426). + +Assets containing silhouette edges tend to contain *a lot* of them. Using component type 5120 (byte) to encode the silhouette normal vectors is recommended to minimize their impact on the size of the glTF asset. Using `EXT_meshopt_compression` can further reduce the transmission size. From 3c7fc9d16a443602542c5c91ebca225c23d01f59 Mon Sep 17 00:00:00 2001 From: Adam Morris Date: Mon, 6 Oct 2025 15:20:57 -0500 Subject: [PATCH 24/39] Removed the copyright text in EXT_mesh_primitive_edge_visibility README --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index b74b5d6a5b..156e10982b 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -1,8 +1,3 @@ - - # EXT_mesh_primitive_edge_visibility ## Contributors From 37038bd8d89c48c5831c38f7ce28025126edd6e7 Mon Sep 17 00:00:00 2001 From: Adam Morris Date: Mon, 6 Oct 2025 16:48:22 -0500 Subject: [PATCH 25/39] Included a CC-BY-4.0 SPDX copyright line at the top of the file. --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 156e10982b..18f92ee4a2 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -1,3 +1,8 @@ + + # EXT_mesh_primitive_edge_visibility ## Contributors From a9ce053c84301899a0b5d3d24d37901ca7d0734d Mon Sep 17 00:00:00 2001 From: Mark Schlosser <47000437+markschlosseratbentley@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:02:00 -0400 Subject: [PATCH 26/39] Add SVG versions of original ASCII art figures. --- .../README.md | 58 ++++++++----------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 18f92ee4a2..5db34dfb6d 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -91,46 +91,36 @@ The `visibility` property **MUST** be defined if `lineStrings` is not defined - #### Examples Consider the simple example of a pair of adjacent triangles described by the index list `[0,1,2, 0,2,3]`: -``` - 0______3 - |\ | - | \ | - |____\| - 1 2 -``` + +
    + +
    Figure 6 A pair of adjacent triangles
    +
    With 2 bits per edge, encoding the visibility of two triangles requires 12 bits. So the visibility buffer must be 2 bytes long, with 4 bits of the second byte going unused. The diagram below shows which edge's visibility will be encoded into each pair of bits. -``` -Byte 0 1 - ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ - │0:2│2:0│1:2│0:1│ │ │ │3:0│2:3│ - └───┴───┴───┴───┘ └───┴───┴───┴───┘ -Bit 6 4 2 0 14 12 10 8 -``` +
    + +
    Figure 7 Visibility buffer bit arrangement
    +
    ##### Shared silhouette edge Assume that the vertical edges `0:1` and `2:3` are hard edges, the horizontal edges `3:0` and `1:2` are hidden edges, and the shared diagonal edge `0:2` is a silhouette edge. Then the corresponding visibility values would be `[2,0,1, 1,2,0]`. However, we must ensure that the visibility of the silhouette edge is encoded only once, so after replacing one of the redundant `1`s the visibility values are `[2,0,1, 0,2,0]`. Encoding the edge visibility produces the sequence of bytes `[18, 2]`, as illustrated below. -``` -Byte 0 1 - ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ -Binary │ 00│ 01│ 00│ 10│ │ 00│ 00│ 00│ 10│ - └───┴───┴───┴───┘ └───┴───┴───┴───┘ -Decimal 18 2 -``` +
    + +
    Figure 8 Sequence of bytes with an encoded shared silhouette edge
    +
    ##### Shared hard edge Assume that the vertical edges `0:1` and `2:3` and the shared diagonal edge `0:2` are all hard edges, and the horizontal edges `3:0` and `1:2` are hidden edges. Then the corresponding visibility values would be `[2,0,2, 2,2,0]`. However, we must ensure that the visibility of the shared hard edge is encoded only once as `2`, with the other occurrence encoded as `3`, resulting in visibility values `[2,0,2, 3,2,0]`. Encoding the edge visibility produces the sequence of bytes `[226, 2]`, as illustrated below. -``` -Byte 0 1 - ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ -Binary │ 11│ 10│ 00│ 10│ │ 00│ 00│ 00│ 10│ - └───┴───┴───┴───┘ └───┴───┴───┴───┘ -Decimal 226 2 -``` + +
    + +
    Figure 9 Sequence of bytes with an encoded shared hard edge
    +
    ### Material @@ -151,13 +141,11 @@ The `lineStrings` property **MUST** be defined if `visibility` is not defined - #### Examples Consider the following simple triangle fan with triangles described by the index list `[0,2,1, 0,3,2, 0,4,3, 0,5,4]`: -``` - 2______3______4 - |\ | /| - | \ | / | - |____\|/____| - 1 0 5 -``` + +
    + +
    Figure 10 A simple triangle fan containing four triangles
    +
    Assume that the top and bottom edges are hard edges, to be encoded as a pair of line strings with indices `[2,3,4]` and `[1,0,5]`, respectively; and the vertical edges are hard edges, to be encoded as simple line segments. Then the `visibility` bitfield should encode only the vertical edges, with visibility values `[0,2,0, 2,0,0, 0,0,3, 0,2,0]`. From 8de356fba8f885193b96e8f520ff285c3a266009 Mon Sep 17 00:00:00 2001 From: Mark Schlosser <47000437+markschlosseratbentley@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:02:22 -0400 Subject: [PATCH 27/39] Add SVG versions of original ASCII art figures. --- .../figures/adjacent-triangles.svg | 12 +++++ .../figures/shared-hard-edge-encoding.svg | 48 ++++++++++++++++++ .../figures/shared-sil-edge-encoding.svg | 48 ++++++++++++++++++ .../figures/triangle-fan.svg | 31 ++++++++++++ .../figures/vis-buffer-bytes.svg | 50 +++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/adjacent-triangles.svg create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-hard-edge-encoding.svg create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-sil-edge-encoding.svg create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/triangle-fan.svg create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/vis-buffer-bytes.svg diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/adjacent-triangles.svg b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/adjacent-triangles.svg new file mode 100644 index 0000000000..fe232a4977 --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/adjacent-triangles.svg @@ -0,0 +1,12 @@ + + + + + + + + 0 + 1 + 2 + 3 + \ No newline at end of file diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-hard-edge-encoding.svg b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-hard-edge-encoding.svg new file mode 100644 index 0000000000..a140fec099 --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-hard-edge-encoding.svg @@ -0,0 +1,48 @@ + + + + + + + + Byte + 0 + 1 + + + Binary + + + + + + + + + + + + + + + 11 + 10 + 00 + 10 + + + 00 + 00 + 00 + 10 + + + Decimal + 226 + 2 + \ No newline at end of file diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-sil-edge-encoding.svg b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-sil-edge-encoding.svg new file mode 100644 index 0000000000..afd4da1eab --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/shared-sil-edge-encoding.svg @@ -0,0 +1,48 @@ + + + + + + + + Byte + 0 + 1 + + + Binary + + + + + + + + + + + + + + + 00 + 01 + 00 + 10 + + + 00 + 00 + 00 + 10 + + + Decimal + 18 + 2 + \ No newline at end of file diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/triangle-fan.svg b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/triangle-fan.svg new file mode 100644 index 0000000000..d9ae3b720f --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/triangle-fan.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + \ No newline at end of file diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/vis-buffer-bytes.svg b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/vis-buffer-bytes.svg new file mode 100644 index 0000000000..9f56330f36 --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/vis-buffer-bytes.svg @@ -0,0 +1,50 @@ + + + + + + + + Byte + 0 + 1 + + + + + + + + + + + + + + + 0:2 + 2:0 + 1:2 + 0:1 + + + 3:0 + 2:3 + + + Bit + 6 + 4 + 2 + 0 + + 14 + 12 + 10 + 8 + \ No newline at end of file From 94aeded7c84adcaa9f807b88b99161d7e93147d4 Mon Sep 17 00:00:00 2001 From: Mark Schlosser <47000437+markschlosseratbentley@users.noreply.github.com> Date: Thu, 23 Oct 2025 08:19:42 -0400 Subject: [PATCH 28/39] add CesiumJS known implementation --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 5db34dfb6d..ec3b1e8557 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -201,6 +201,7 @@ The `silhouetteNormals` property **MUST** be defined *if and only if* at least o ## Known implementations - [iTwin.js](https://github.com/iTwin/itwinjs-core/pull/8366) +- [CesiumJS](https://github.com/CesiumGS/cesium/pull/12859) ## Implementation Notes From 8f80e894a2df91bfbe0a54d5735dda738fe73878 Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Wed, 12 Nov 2025 14:40:16 -0500 Subject: [PATCH 29/39] Update README.md --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index ec3b1e8557..89af814775 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -8,6 +8,7 @@ SPDX-License-Identifier: CC-BY-4.0 ## Contributors * Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne) +* Daniel Zhong, Bentley Systems, [@danielzhong](https://github.com/danielzhong) ## Status From 8384b27a989f0c0f67c42a0be83e1f21fa7c2d3f Mon Sep 17 00:00:00 2001 From: Mark Schlosser <47000437+markschlosseratbentley@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:17:11 -0500 Subject: [PATCH 30/39] added diagrams, so editing README contributors --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 89af814775..3d3f06c5d0 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -9,6 +9,7 @@ SPDX-License-Identifier: CC-BY-4.0 * Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne) * Daniel Zhong, Bentley Systems, [@danielzhong](https://github.com/danielzhong) +* Mark Schlosser, Bentley Systems, [@markschlosseratbentley](https://github.com/markschlosseratbentley) ## Status From 679bc5df968dd77f21b56ab923a3530449b0aa5e Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Mon, 6 Apr 2026 15:15:42 -0400 Subject: [PATCH 31/39] optimize PNG images --- .../figures/depth-fighting.png | Bin 5436 -> 3179 bytes .../figures/hard-edges.png | Bin 7178 -> 4371 bytes .../figures/outline.png | Bin 4976 -> 2937 bytes .../figures/visible-edges.png | Bin 7231 -> 4407 bytes .../figures/wiremesh.png | Bin 25349 -> 16150 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/depth-fighting.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/depth-fighting.png index ba8c91d3e9111f58b6dd8af78e101926ebb90410..b6076d30a147de28c0a6f694dc263090fd43cc83 100644 GIT binary patch literal 3179 zcmW-jcQ_Re7sroPX6SXTD1?L(WyCKlS5~>kC`LzW{RpvjE=#W&oxErT|U>69D4?V*uX(M*q(r1{eYu1o#T@1z-Sx1n?Q4AD|C_ z2tWYn1?U0j2IvCl1n2;02WSIm1!w_i2518K1kec308kH52T%)815gc61yBi40Zk?CA>O#pT~80JQ7sDoXlZ(8ok|#jIpZnv_@tU-saZvklhN$=KCORkH?s8QRn_xO`r3|(`CXtN zKdWbDfZmT}Hwdtys4GW&X zWA{*Qd_)OXjh@NS5agArMrlvaWusTlLK{0=H)reKrnqiW5Sg&msm}H#jnqZR9Qxk{V zZ25zfPH#o|%c`pOj2P-KiVS@9n{^)?Gl=C;mr|9S_%oC*UoxspUr$3{Ttuoov11EC z74Q933@Fs*s1(#R*(^M6ra}#cupkE~etYqCpPXw7`E$^1I~v^?Ec3&d=Zl<6^HI>H zKWb?E=qgs)g`Bldm|%@`6DmY)hi}{m=Tz0GMguVRsbcioq^KC9C&%s-I1lG)CQa|H z?<-cJG5#f%CDoN1jvGPgLRi_sB3F(LEO5$PrCS>IGMqSTbyh9ifr|S6uuY?VtO`_i z;JNxV7)VLVbWe)qDLq+22GY*cyqOM^?%5=0JC`_^uy|ESF}Cj~2njQEkChdd6E}VM z?7wSa6ipKM7@RLLybwb{%tdCjYl`7NfrbL&_B=gC_}96L=7R=z$X=N9g2SiOdU~^I zFC--`#%4hX$HkqfOAsmAQX%@`-!GpdnrYk`poy#^Y>jG^B<5*U-6}}Z(Al>Bj{K)T zlg&p~%!kuVOP}fI5NA0~OjNO0Iee}&elCQ-)I}F!gT$G0jW^fcW)xLdW`F%_|7fG} zb|{%;*7m#pu$p5TnnCn}gX9cV>*%HCfS(yUisDm20;>~N=ePz;1;+X=`FD|K-Ue^_ z5(9We=gdu(+vW&LNx?PrUOm57T<{hP&8~DrJqMk%@rr=F8ks3;TLWb!)&E55a$?#n z4BfcL9X_0tmD)kzSLE6 zSD=10>&kgkWcG#S!(-1*5I5=%lKoNb>Tq+5CoNm$Gn5|t6eX5?nGF>6K5};nB$+a5waZT#Wv_8{!jhf!1+ zr(tIBS-DDK-u%J3XX~DwUo7XX|(F~?rP_WF8?78`Db>|^=(xamZnrW zu5h_691uSwM;FGN(0vT~urMP>Tb(UAPRI+#<4$-Zn)%}-F}aT0T0!BhoQjS})qEVf z-c*&-DetVK!4j0Q__<^*+ncCDuV!k%ReN2STSIW|eT`>7=|+3%Bk$b#OOq5a*Kw?h z*G`tUI)~EPF&_I{XZN?I^;$k9{oGR?H#wW0b+0+WV8}EbdzhPxSN!Vd{c-5@)F4v4 z=(E0)(Ue`#ymj@jW3@$g050oi>9s9;*$=MR@NB6xgtw|&T}R92!>vD8x=b$;cC5F# zgPH~oiO8)RDAb$nY%9-4bAiTn=dJvBj}~kc%k9&g&AXm*-@oeLw=KGWMoM6?e}qAN zVwot&%p4S$0{Xmf_w06gx6qL#r-f!|!QNE}&LEq$V&qVG~HN zsDmH9vq;NuYDrD?qiWf0_qHNAx#&=`6#2}OA~T0N$k8#oJ0!Nu-|1GLVi>R29VChB zD>Xg3%hO8ej(I>+ytc(~qc)Rst?og4tBKE~Xs+^ut5v@$=7+6ruogYo8_JtGh zrJ=nu+yUAnJr3*RP@IFX#gn^OZ)UN>2oY|b25rQkxJUzlHDwa|^a m)r4DTl_IE)37tInjW{OfWw?DJZ27+rSRMI5rBvDC)qep2Q`D;f literal 5436 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 literal 7178 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 M07*qoM6N<$f{Rq)@Bjb+ diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/outline.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/outline.png index 8740901041057b605644efe53b0b403738f02437..eac7b9980c6196d67d6ace6274baa49eb515668c 100644 GIT binary patch literal 2937 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 BVp9MB literal 4976 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 diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/visible-edges.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/visible-edges.png index cb2f8cd3f6fecf4f72e4834b7eb8bf696a822642..014531aee818436108216ac4cda4f14f50591003 100644 GIT binary patch literal 4407 zcmW+)cQh4_AN?Sam7P^W@{ls0kz`eN!h5_YM9ALjNj4dgWRJ-9?9m|Gmy8!#*-^6h z&i-|NpL6d0ob&nT-h1wUpKxuh2hO z+1c6Y>FLSI$?@^=(b3W2;o-r-!Qa1s_xJbr_V#vncXxJnwzs#pwzf7mH#asm*4Nk9 z*49>6S65b6mY0{8mX;P57Z(;5=I7_<=H_N+XJ=++U0XhJ_0<;6P0sIHh3eW=34A2D72+#mf4^Rj21)vt72H-Qm zCxDLt9{{QWssJhhDgep>$^c3MN&t!hiU9Bcg#ZNr`2cwUxd1r;*#KDp?*TFaG63EI zqywY@qynS>Bm*P?BmyJ=!~?|rD*^25LIMCTdZ{Wa=y{D1hC)&VB^gPcEP8eQjj#wM z<99tus;6>wtNQWj-KbaV+i$3(?Rf;!IofFz%jrIRiMn^gGR@)bwEY!@rM2ga25CJP zw1!#I)oC-?tv`>U0rh1>W|^VmZ6O}q4F)pm^1Bf7K4Srn;#?7 zRqb25zK6?3!ez#k{yt>O2fS6t=1N_IbNIqIgvpMWnuXflU-R&217kkp@uB8V!_PiO zOq{>mX;YvRYMBxmhrcm87@Z$H@P_<5?wKYMooxEGT;-fPRqF<;B~HiiXKg;IY<&+| zH?5ar{VU_UZc>p>N1fX~*Vr!D3BVB50cR#`)!iX^V=?&ewgn@mQ zNBQS2irvc`6YM9wD<(KqOdMieLz9Eo-V(!P!P1cYzTNlbXFIQW`pv-SUpideYO;iD z9Sc4a91eMFEG$F$lWQE?rIVABIT^*?{$}hDWGgfqTz5SGJUx}aRG+xPy~I-_aFpI= zo^R@6T7y)E$+D_hE=B+B*6f{WrMs5Z1c6U_8N%Az0ayitKV2=mzg{2Hx<5zP;QVgi zrAbSfvdTm=dEkh)-*CQn=(UkF$nZWRcK>x9&xigf?}97)WtNN@@*v2Orm?7(xNvp0 zsXxC|YG5h!PUr=jRD)Nj9R;s@_ilafu{MP~E8piCvFZPanTme?ou4!&X+XXP%csSo z&zdU^PBfT$%gy4lPCZ}V6(2MG+}wv7rYkaJL6w<%9DmSe@@GYqyr7BRd3QBWN5N>~ zKZGpChFkdI9{ka)&`r%GHWq`;tH7%V#S+*&v}p7ar1cEQK$koSe_hN{OlU zngia^4hVK0clbXci@T*L`AARIjmLwf9+8Jpa}2oU8GzmUWWu??OP5Nm zshE+H&Fa)wgF>Kd`?}bOJg(x+jS`8hQ~U8J=rTz4pN|Z{T9O`bbCP^a;iX3+{m*1g zVm~iq@yCltQEbMhyv z>({zDDXLmO;)4#i*)dICSM=UV^V={?Uo4UE&P8=hVpP0yPA85GU#{Q~X3sbE)S87i zzY5U1mHd`7xDbB$!tcoc;8xf(Jv-Ke7x4>fcuXngW+lRR+ZNc_*%1#bV!+T)X;#1S zB;9&r-O>^37!st9#e#`@RWmj3&_L{c*W?qk=MsA>g%Kd&r68obh*(Pl>BhkobFRVSnK z6ghNKZx;Kg>+qO`vxHlBomJ`RezPXK=l81b!LqpT`eTdUH*u?18)O@_v^D#yt^4On z_oIvKizKERqeb48N6)lhV`D>%mfBbimK=Tbsr&SJz%A@rVOVH=agb4buSK5yikoh& zC%5`H%usRN!`Nyi`TiS$5e$`#1o4uO8-<61W9@NGo1cZ%T0ou7uyElDJhh8BlMcMVE2~%~tQc)G^ zmne3(#nds7RyX$)wTw?o`jAaBSh7>1_`a_C7fQZ(Kav~E%{Cb&rG&N~8f3IBEBr7r zG^!-9W>z7{otcWHMU#`h)*i!B!)~0KA{NUf zQzgY#jCxRlEv4N1u8A#Wj$3=Cc%mCNXXKbT0T)KY2_zIYH#J1mx=Rmt3r2LLZe8N@ zNJ^l|#`#|N$rCm{8ILCm>twnz*cO#3W4W#MS9`J|bnj8y1>SiAJ-@&#P0NA z@`b*QWmw7XbcSJdUHPZZX~XocOw)4RooKFrsoV!jOpz70m=uAb!`>6%8eB;$6Aq79S1%2oQ-{l zzddbFwA7VihPQKQ8j3l`Z&O}Tb+}5=s6Q(#_HU{Xgib$lG4jB#K6kmUMj(C*M{-jf zCyra|TjEQ4qTT0np4VOWQQy;9Ue+)DNHs$!32xoY=-j0X>Cid<1ifB4Q4`5QDq@NW zWUEO1uU0*ZfDN-cI55<#_S-5AS)hH(%njvIs+o~aeey-->u^R;Y!P+65#(wng`y+l zaqpyp9}n5OwbaKc|CGz&xTdAN_TFFOx!_^xZr_bwT~Y0QBqlB>`+@dL+^?*}klZ3B z7k4AZJ!3D8h?P|CK6*4?1i9ePxx*$>0Wu6}sw?F((Tb4hk0RZQrF8DjWQ;9`fF~t) z|8v=xkc?N?Vt;canC2eJ;al#7mPqORoc7Vw+gq+>D@~whmLzWS6j^RW934rc z7EyqZJ-;ny`l^o-&mF=?@OLVHqPF-_E0-(T0>^=wpn{_a?NAeMpEr8cMRUMIr#W>H?_fFSPB(Qs!@?p=aggf zW70sl&Dn}7Q8`~{h=&1nPPDkE^qa9PHVDxfpg+qpc`MKfpIt9w&3&U)EydY>v%i9? zfI{^qv&Q3QhkCxf%&%VV?kfc@H-a=<%V%ZUYw(xFT5RqVzgh}@YKU)7vzd1oeP!#q zJbdBH2kFo|1qK5M=C8!Lxx_j)l1icR&bfOb41p0Ui3^y|m zweYnMTvn!5h`UNuIg+-tuJP0~B~bAZwKumcS?OLt6FvI(*ykZZzM?#Wh?lACHgQv% zC11K4Z}5QD*3ww#tFA7CpVDbJR!90>(wQv_TWbuwCgySef`3J682d66v`m=&(Qe%q zVHL?MDgj<&$u!qvJ3=2msZx{6Em+W-PYaJD{^EgZ4?J+iRf?c`N9;dMD$<&&qj`od zwYAt?v%u5t)Vao@igX~Myi)#g2G#8o!gQ?psG{x#jt0rJEIhs5E!m6RC6#wym}iKj zU?wsjdhco+x3KVP(b7eI)Tt?jdl86u0dKBYl*luIr_zb{B1Ig$&X-pCeL%d?oVgps zl*eonA;fs+VHgE}qMP>I<}fAYoYnQ^9qY0T(qsDEWR(GBMbOgN_@!g;SpqJsvE^L^mjair4qdaV>gp@1 z^J#vMMqcbsx(U!x5LGT^>deKd54H));8E+y5KELtk*%3HMzEKAQSsQSU4?B@%OCBh zpTl#(NI=lmnV|uW4MxEp9T%) z1%D!Tz0aG+W^kE>$AUWJ0lfprF=f*|A-iwrM13<{*_}+L#8-r}fz2SCAWt$itG*x} z8S|P*#VnheXN&kmGi~K2p%BMj^T>UeVt~>|`tJKXdE3YKYbu^W-KSobQ{fyda)jl} zO6_{RLW%CA4e4(tsezNrGxEo>rJVocHJx@n20Vo|bZ{;*-Gt0k0Z4ga7~l literal 7231 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 N002ovPDHLkV1hhV0@VNj diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/wiremesh.png b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/figures/wiremesh.png index 7d0e3264a2b81e8e538ef62df51dca3d8f0f09a6..ec46fa04013489c2dc76c3f3f40555630a32faa1 100644 GIT binary patch literal 16150 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 From 56863b63c8ab49814400bc80a5f55c529531c98f Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:21:56 -0400 Subject: [PATCH 32/39] Update README.md --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 3d3f06c5d0..56794ac8a0 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -21,7 +21,7 @@ Written against the glTF 2.0 spec. ## Overview -3D modeling and computer-aided drafting environments like SketchUp, MicroStation, and Revit provide non-photorealistic visualizations that render 3D objects with their edges visible. The edges can improve the readability of complex models and convey semantics of the underlying topology. The `EXT_mesh_primitive_edge_visibility` extension augments a triangle mesh primitive with sufficient information to enable engines to produce such visualizations. The information is encoded in a compact form to minimize the impact on the size of the glTF asset. +3D modeling and computer-aided drafting environments often provide non-photorealistic visualizations that render 3D objects with their edges visible. The edges can improve the readability of complex models and convey semantics of the underlying topology. The `EXT_mesh_primitive_edge_visibility` extension augments a triangle mesh primitive with sufficient information to enable engines to produce such visualizations. The information is encoded in a compact form to minimize the impact on the size of the glTF asset. Figure 1 illustrates a typical rendering of a cylinder with its edges. The width of the edges has been exaggerated for emphasis. From 7bdecf4dfffecc3cf74abd84326628cb9020976d Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:53:26 -0400 Subject: [PATCH 33/39] Update README.md --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 56794ac8a0..731f783d2e 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -191,8 +191,8 @@ The `silhouetteNormals` property **MUST** be defined *if and only if* at least o ## Rendering requirements - Engines **MUST** render all edges according to their specified visibility values, though some engines may permit the user to toggle the display of edges on and off. -- The edges **MUST** draw in front of their corresponding triangles with no depth-fighting. -- The edges **MUST** draw using the materials specified by the extension by default, though some engines may provide options for the user to override those materials. +- The edges **MUST** be drawn in front of their corresponding triangles with no depth-fighting. +- The edges **MUST** be drawn using the materials specified by the extension by default, though some engines may provide options for the user to override those materials. - Each silhouette edge **MUST** be rendered unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. ## JSON Schema From 97cc3650928f3a738905308e7d215e170368c90e Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:55:00 -0400 Subject: [PATCH 34/39] Update lineString.schema.json --- .../schema/lineString.schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json index 2c541f738d..6f260c4082 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/schema/lineString.schema.json @@ -28,7 +28,6 @@ "description": "The index of a glTF material with which the edges are to be drawn.", "gltf_detailedDescription": "The index of a glTF material with which the edges are to be drawn. If omitted, the edges are drawn using the same material as the edges encoded by the extension's `visibility` field, or else that of the triangle mesh primitive." }, - "name": {}, "extensions": {}, "extras": {} }, From e91be75c5ee72fba1f37871dbed49f9cb960fdb8 Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:14:07 -0400 Subject: [PATCH 35/39] Update README.md --- .../Vendor/EXT_mesh_primitive_edge_visibility/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 731f783d2e..12dbe2f92a 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -133,10 +133,12 @@ The extension's `material` property optionally specifies the index of a glTF mat The representation of hard edges in the `visibility` property - while compact - presents some drawbacks for certain use cases: it requires that all of the encoded edges draw using the same material, and it does not capture the connectivity between individual line segments. For example, the perimeters of the top and bottom faces of the cylinder in Figure 1 are each logically a single continuous circle. Perhaps the outline of the top face should be drawn in a different color than that of the bottom face, which would require a way to specify a different material. Or perhaps the outlines are to be drawn with a dash pattern along their entire lengths, or as wide lines into which an engine might insert additional triangles to smooth out the joints between line segments - both of which require connectivity information. The extension's `lineStrings` property provides an alternate representation for some or all of the hard edges that would otherwise be encoded in `visibility`. It is an array in which each entry is an object with the following properties: -- `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as a series of two or more indices. Multiple line strings can be encoded by inserting a "primitive restart" value (that is, the maximal possible value for the accessor's component type, like 255 for `UNSIGNED_BYTE`) between each line string. The `indices` buffer **MUST NOT** begin or end with a primitive restart value, and **MUST NOT** contain consecutive primitive restart values. +- `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as a series of two or more indices. Multiple line strings can be encoded by inserting a primitive restart value between each line string. The primitive restart value is always the maximal possible value for the accessor's component type, such as 255 for `UNSIGNED_BYTE`, and **MUST** be interpreted as a restart marker rather than as a vertex index. Therefore, the accessor referenced by `indices` **MUST NOT** begin or end with a primitive restart value, **MUST NOT** contain consecutive primitive restart values, and **MUST NOT** contain any vertex index equal to the primitive restart value. Writers **MUST** choose a component type large enough that all referenced vertex indices are strictly less than the primitive restart value. - `material` (optional): the index of the material with which the edges encoded by `indices` are to be drawn. If omitted, the extension's own `material` property is used, if defined; otherwise, the material of the triangle mesh primitive is used. -A hard edge encoded in `lineStrings` **MUST NOT** also be encoded in `visibility`. +Each pair of adjacent non-restart indices in `lineStrings` **MUST** identify a hard edge of the triangle mesh primitive. + +A hard edge encoded in `lineStrings` **MUST NOT** also be encoded in `visibility` or in any other entry of `lineStrings`. The `lineStrings` property **MUST** be defined if `visibility` is not defined - otherwise, the extension would encode no edges. @@ -193,7 +195,7 @@ The `silhouetteNormals` property **MUST** be defined *if and only if* at least o - Engines **MUST** render all edges according to their specified visibility values, though some engines may permit the user to toggle the display of edges on and off. - The edges **MUST** be drawn in front of their corresponding triangles with no depth-fighting. - The edges **MUST** be drawn using the materials specified by the extension by default, though some engines may provide options for the user to override those materials. -- Each silhouette edge **MUST** be rendered unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. +- Each silhouette edge **MUST** be rendered unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. This determination **MUST** be independent of whether the corresponding material is double-sided. ## JSON Schema From 20946bbc009be4dfc882c1bfcf79ff5b4698a09f Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:19:45 -0400 Subject: [PATCH 36/39] Update README.md --- .../Vendor/EXT_mesh_primitive_edge_visibility/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 12dbe2f92a..f94e59def6 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -126,7 +126,9 @@ Assume that the vertical edges `0:1` and `2:3` and the shared diagonal edge `0:2 ### Material -The extension's `material` property optionally specifies the index of a glTF material with which the edges are to be drawn. If unspecified, the edges should be drawn using the same material as the triangle mesh primitive. +The extension's `material` property optionally specifies the index of a glTF material describing the appearance of the edges. If unspecified, the edges inherit the appearance of the triangle mesh primitive material and may therefore be visually indistinguishable from the triangles. Authors who require a distinct edge appearance **SHOULD** specify an explicit material. + +Material properties that require surface normals, tangents, bitangents, or other surface-local shading inputs do not apply to edge rendering and **MUST** be ignored when drawing edges. ### Line Strings @@ -134,7 +136,7 @@ The representation of hard edges in the `visibility` property - while compact - The extension's `lineStrings` property provides an alternate representation for some or all of the hard edges that would otherwise be encoded in `visibility`. It is an array in which each entry is an object with the following properties: - `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as a series of two or more indices. Multiple line strings can be encoded by inserting a primitive restart value between each line string. The primitive restart value is always the maximal possible value for the accessor's component type, such as 255 for `UNSIGNED_BYTE`, and **MUST** be interpreted as a restart marker rather than as a vertex index. Therefore, the accessor referenced by `indices` **MUST NOT** begin or end with a primitive restart value, **MUST NOT** contain consecutive primitive restart values, and **MUST NOT** contain any vertex index equal to the primitive restart value. Writers **MUST** choose a component type large enough that all referenced vertex indices are strictly less than the primitive restart value. -- `material` (optional): the index of the material with which the edges encoded by `indices` are to be drawn. If omitted, the extension's own `material` property is used, if defined; otherwise, the material of the triangle mesh primitive is used. +- `material` (optional): the index of the material with which the edges encoded by `indices` are to be drawn. If omitted, the extension's own `material` property is used, if defined; otherwise, the material of the triangle mesh primitive is used. This material is applied to edges subject to the constraints described in the Material section above. Each pair of adjacent non-restart indices in `lineStrings` **MUST** identify a hard edge of the triangle mesh primitive. @@ -194,7 +196,7 @@ The `silhouetteNormals` property **MUST** be defined *if and only if* at least o - Engines **MUST** render all edges according to their specified visibility values, though some engines may permit the user to toggle the display of edges on and off. - The edges **MUST** be drawn in front of their corresponding triangles with no depth-fighting. -- The edges **MUST** be drawn using the materials specified by the extension by default, though some engines may provide options for the user to override those materials. +- The edges **MUST** be drawn using the applicable properties of the materials specified by the extension by default, though some engines may provide options for the user to override those materials. - Each silhouette edge **MUST** be rendered unless both adjacent triangles are front-facing or both are back-facing, as determined by their normal vectors. This determination **MUST** be independent of whether the corresponding material is double-sided. ## JSON Schema From 64ed5bea4ccffb2242d2cf86e212388f63b61603 Mon Sep 17 00:00:00 2001 From: danielzhong <32878167+danielzhong@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:30:54 -0400 Subject: [PATCH 37/39] Update README.md --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index f94e59def6..3724a8d3da 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -135,12 +135,14 @@ Material properties that require surface normals, tangents, bitangents, or other The representation of hard edges in the `visibility` property - while compact - presents some drawbacks for certain use cases: it requires that all of the encoded edges draw using the same material, and it does not capture the connectivity between individual line segments. For example, the perimeters of the top and bottom faces of the cylinder in Figure 1 are each logically a single continuous circle. Perhaps the outline of the top face should be drawn in a different color than that of the bottom face, which would require a way to specify a different material. Or perhaps the outlines are to be drawn with a dash pattern along their entire lengths, or as wide lines into which an engine might insert additional triangles to smooth out the joints between line segments - both of which require connectivity information. The extension's `lineStrings` property provides an alternate representation for some or all of the hard edges that would otherwise be encoded in `visibility`. It is an array in which each entry is an object with the following properties: -- `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as a series of two or more indices. Multiple line strings can be encoded by inserting a primitive restart value between each line string. The primitive restart value is always the maximal possible value for the accessor's component type, such as 255 for `UNSIGNED_BYTE`, and **MUST** be interpreted as a restart marker rather than as a vertex index. Therefore, the accessor referenced by `indices` **MUST NOT** begin or end with a primitive restart value, **MUST NOT** contain consecutive primitive restart values, and **MUST NOT** contain any vertex index equal to the primitive restart value. Writers **MUST** choose a component type large enough that all referenced vertex indices are strictly less than the primitive restart value. +- `indices` (required): the index of an accessor of type `SCALAR` and component type `UNSIGNED_BYTE` (5121), `UNSIGNED_SHORT` (5123), or `UNSIGNED_INT` (5125) encoding one or more line strings as indices into the triangle mesh's vertex attribute array. Each line string is encoded as an ordered sequence of two or more non-restart vertex indices, with each pair of adjacent indices defining one line segment. Multiple line strings can be encoded by inserting a primitive restart value between each line string. The primitive restart value is always the maximal possible value for the accessor's component type, such as 255 for `UNSIGNED_BYTE`, and **MUST** be interpreted as a restart marker rather than as a vertex index. Therefore, the accessor referenced by `indices` **MUST NOT** begin or end with a primitive restart value, **MUST NOT** contain consecutive primitive restart values, and **MUST NOT** contain any vertex index equal to the primitive restart value. Writers **MUST** choose a component type large enough that all referenced vertex indices are strictly less than the primitive restart value. - `material` (optional): the index of the material with which the edges encoded by `indices` are to be drawn. If omitted, the extension's own `material` property is used, if defined; otherwise, the material of the triangle mesh primitive is used. This material is applied to edges subject to the constraints described in the Material section above. +Each sequence of non-restart indices between primitive restart values **MUST** contain at least two indices. A vertex index **MAY** appear more than once in a line string, but adjacent indices **MUST** be distinct. + Each pair of adjacent non-restart indices in `lineStrings` **MUST** identify a hard edge of the triangle mesh primitive. -A hard edge encoded in `lineStrings` **MUST NOT** also be encoded in `visibility` or in any other entry of `lineStrings`. +A hard edge encoded in `lineStrings` **MUST NOT** also be encoded in `visibility` or in any other entry of `lineStrings`. The same hard edge, regardless of direction, **MUST NOT** be identified more than once within `lineStrings`. The `lineStrings` property **MUST** be defined if `visibility` is not defined - otherwise, the extension would encode no edges. From 998412ba8b03c79f4a5796201c8b912253701edb Mon Sep 17 00:00:00 2001 From: Mark Schlosser <47000437+markschlosseratbentley@users.noreply.github.com> Date: Thu, 28 May 2026 14:49:07 -0400 Subject: [PATCH 38/39] Update extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md Co-authored-by: Adam Morris --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 3724a8d3da..1a77a354a4 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -1,5 +1,5 @@ From df29fa46a7249b07f247ae49e5236d89c5363c6a Mon Sep 17 00:00:00 2001 From: Mark Schlosser <47000437+markschlosseratbentley@users.noreply.github.com> Date: Thu, 28 May 2026 14:50:59 -0400 Subject: [PATCH 39/39] license --- .../2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md index 1a77a354a4..01622a92cb 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md @@ -218,3 +218,7 @@ The [pull request](https://github.com/iTwin/itwinjs-core/pull/5581) that informe iTwin.js [implements](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/Edge.ts#L107) conditional display of silhouette edges. It also draws edges in a separate pass from surfaces to [mitigate z-fighting](https://github.com/iTwin/itwinjs-core/blob/03b760e1e91bde5221aa7370ea45c52f966e3368/core/frontend/src/internal/render/webgl/glsl/FeatureSymbology.ts#L426). Assets containing silhouette edges tend to contain *a lot* of them. Using component type 5120 (byte) to encode the silhouette normal vectors is recommended to minimize their impact on the size of the glTF asset. Using `EXT_meshopt_compression` can further reduce the transmission size. + +## License + +This work is licensed under CC BY 4.0. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/