Skip to content

Commit bf816db

Browse files
committed
oct16
1 parent 9746de8 commit bf816db

File tree

1 file changed

+74
-1
lines changed
  • extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility

1 file changed

+74
-1
lines changed

extensions/2.0/Vendor/EXT_mesh_primitive_edge_visibility/README.md

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,72 @@ Assume that the top edge is to be drawn using the same material as the vertical
194194

195195
### Silhouette Normals
196196

197-
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`.
197+
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`.
198+
199+
### oct16 encoding
200+
201+
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.
202+
203+
TypeScript implementation of oct16 encoding and decoding:
204+
205+
```ts
206+
// A three-dimensional vector.
207+
interface Vec3d { x: number; y: number; z: number; }
208+
209+
// Normalize a 3d vector to a length of 1.
210+
function normalize(vec: Vec3d): void {
211+
const magnitude = Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
212+
const scale = magnitude === 0 ? 0 : 1 / magnitude;
213+
vec.x *= scale;
214+
vec.y *= scale;
215+
vec.z *= scale;
216+
}
217+
218+
// Map a signed normalized value in [-1, 1] to the integer range [0, 255].
219+
function toSNorm(val: number): number {
220+
const clamped = Math.min(Math.max(val, -1), 1);
221+
return Math.round((clamped * 0.5 + 0.5) * 255);
222+
}
223+
224+
// Return -1 for a negative input, otherwise 1.
225+
function signNotZero(val: number): number {
226+
return val < 0.0 ? -1.0 : 1.0;
227+
}
228+
229+
// Encode a normalized 3d vector into a 16-bit unsigned integer.
230+
function oct16EncodeNormal(n: Vec3d): number {
231+
const denom = Math.abs(n.x) + Math.abs(n.y) + Math.abs(n.z);
232+
let rx = n.x / denom;
233+
let ry = n.y / denom;
234+
if (n.z < 0) {
235+
const x = rx;
236+
const y = ry;
237+
rx = (1 - Math.abs(y)) * signNotZero(x);
238+
ry = (1 - Math.abs(x)) * signNotZero(y);
239+
}
240+
241+
return toSNorm(ry) << 8 | toSNorm(rx);
242+
}
243+
244+
// Decode a 16-bit unsigned integer into a normalized 3d vector.
245+
function oct16DecodeNormal(oct16: number): Vec3d {
246+
let x = oct16 & 0xff;
247+
let y = oct16 >> 8;
248+
x = x / 255.0 * 2.0 - 1.0;
249+
y = y / 255.0 * 2.0 - 1.0;
250+
const z = 1 - (Math.abs(x) + Math.abs(y));
251+
252+
const n: Vec3d = { x, y, z };
253+
if (z < 0) {
254+
n.x = (1 - Math.abs(y)) * signNotZero(x);
255+
n.y = (1 - Math.abs(x)) * signNotZero(y);
256+
}
257+
258+
normalize(n);
259+
return n;
260+
}
261+
262+
```
198263

199264
The `silhouetteNormals` property **MUST** be defined *if and only if* at least one edge is encoded with visibility value `1` in `visibility`.
200265

@@ -212,8 +277,16 @@ Engines **MUST** render a silhouette edge unless both adjacent triangles are fro
212277
- [primitive.EXT_mesh_primitive_edge_visibility.schema.json](./schema/primitive.EXT_mesh_primitive_edge_visibility.schema.json)
213278
- [lineString.schema.json](./schema/lineString.schema.json)
214279

280+
## Known implementations
281+
282+
- [iTwin.js](https://github.com/iTwin/itwinjs-core/pull/8366)
283+
215284
## Implementation Notes
216285

217286
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.
218287

219288
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).
289+
290+
## References
291+
292+
- 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/).

0 commit comments

Comments
 (0)