@es3d/es3d 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/es3d-stage.min.js +258 -244
- package/docs/SPEC.md +1 -1
- package/package.json +1 -1
- package/src/render/frame-builder.js +38 -3
package/docs/SPEC.md
CHANGED
|
@@ -195,7 +195,7 @@ The reference reader keeps up to four levels. At quality 1 the segment count is
|
|
|
195
195
|
|
|
196
196
|
A `segments` attribute (or both torus segment counts) requests one fixed tessellation, which is part of the shape. A `segments="6"` cylinder is a hexagonal prism at every distance. Exact shapes (box, plane, polygon, hairline, authored mesh) have one level and a bound of 0.
|
|
197
197
|
|
|
198
|
-
Each level stores its bound. A frame may draw a level only while `bound × scale × pixelsPerUnit` is at most 1. `scale` is the longest column of the instance matrix. `pixelsPerUnit` is the projected size of
|
|
198
|
+
Each level stores its bound. A frame may draw a level only while `bound × scale × pixelsPerUnit` is at most 1. `scale` is the longest column of the instance matrix. `pixelsPerUnit` is the largest projected size, in CSS pixels, of a unit length at the centre of the instance's bounding sphere. For a perspective view it is F·d/z², where F = viewportHeight / (2·tan(verticalFov/2)), d is the distance from the eye to the centre and z is its depth along the view axis; d²/z² is limited to its value at the corners of the view, and that limit is used when z ≤ 0. For an orthographic view it is viewportHeight divided by the view's height. The reference reader picks the coarsest level that satisfies that test. A renderer `lodBias` above 1 (default 1) divides the one-pixel budget; it is not a document attribute. While a level is still being generated, a renderer may draw a coarser one in its place for a few frames (progressive refinement), as long as it keeps drawing until every level passes the test. For a unit sphere on the view axis the switches fall near 59, 15 and 7 pixels of the sphere's own radius.
|
|
199
199
|
|
|
200
200
|
Closed shapes must produce closed, outward-facing surfaces. Open or flat shapes (planes, polygons, paths, open revolves, `caps="false"`) are rendered double-sided.
|
|
201
201
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@es3d/es3d",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "ES3D (Extensible Scalable 3D): a declarative, SVG-inspired 3D scene format with a WebGPU/WebGL2 reference renderer, editor and converter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,6 +21,41 @@ function length3(x, y, z) {
|
|
|
21
21
|
return Math.sqrt(x * x + y * y + z * z);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Per-frame constants for `instancePixelsPerUnit`.
|
|
26
|
+
* `ppuFactor` is F = viewportHeight / (2 tan(verticalFov / 2)) for a perspective
|
|
27
|
+
* camera, or the constant scale for an orthographic one. `cornerStretch` is
|
|
28
|
+
* 1/cos^2 of the angle to the frustum's corner.
|
|
29
|
+
*/
|
|
30
|
+
export function lodFrameConstants(camera, viewportHeight) {
|
|
31
|
+
const aspect = camera.aspect || 1;
|
|
32
|
+
const tanV = Math.tan(camera.verticalFov(aspect) / 2);
|
|
33
|
+
return {
|
|
34
|
+
orthographic: camera.type === 'orthographic',
|
|
35
|
+
ppuFactor: camera.pixelsPerUnit(1, viewportHeight),
|
|
36
|
+
view: camera.view,
|
|
37
|
+
cornerStretch: 1 + tanV * tanV * (1 + aspect * aspect),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Largest projected size, in CSS pixels, of a unit length at the centre
|
|
43
|
+
* (cx, cy, cz) of an instance's bounding sphere; `d` is the distance from the
|
|
44
|
+
* eye to that centre. A flat image plane stretches a displacement at angle a
|
|
45
|
+
* from the view axis by up to 1/cos^2(a) = d^2/z^2 (z = depth along the axis)
|
|
46
|
+
* radially. Inside the view that never exceeds its value at the frustum's
|
|
47
|
+
* corners, which is also used when the centre is at or behind the eye plane
|
|
48
|
+
* while the sphere is still visible. Orthographic views do not stretch.
|
|
49
|
+
* Allocation-free: it runs once per instance.
|
|
50
|
+
*/
|
|
51
|
+
export function instancePixelsPerUnit(k, cx, cy, cz, d) {
|
|
52
|
+
if (k.orthographic) return k.ppuFactor;
|
|
53
|
+
const V = k.view;
|
|
54
|
+
const z = -(V[2] * cx + V[6] * cy + V[10] * cz + V[14]);
|
|
55
|
+
const stretch = z > 0 ? Math.min((d * d) / (z * z), k.cornerStretch) : k.cornerStretch;
|
|
56
|
+
return (k.ppuFactor * stretch) / Math.max(d, 1e-6);
|
|
57
|
+
}
|
|
58
|
+
|
|
24
59
|
/**
|
|
25
60
|
* The level to draw while `lod` is not generated yet: the nearest coarser
|
|
26
61
|
* level that exists, else the nearest finer one, else the coarsest level
|
|
@@ -159,8 +194,8 @@ export class FrameBuilder {
|
|
|
159
194
|
const vh = p.viewportHeight;
|
|
160
195
|
const bias = options.lodBias || 1;
|
|
161
196
|
const fixedLod = options.autoLod === false ? options.fixedLod || 0 : -1;
|
|
162
|
-
// pixels per world unit
|
|
163
|
-
const
|
|
197
|
+
// pixels per world unit: depth based for perspective, constant for orthographic
|
|
198
|
+
const lodK = lodFrameConstants(camera, vh);
|
|
164
199
|
|
|
165
200
|
for (let i = 0; i < n; i++) {
|
|
166
201
|
keys[i] = -1;
|
|
@@ -189,7 +224,7 @@ export class FrameBuilder {
|
|
|
189
224
|
else {
|
|
190
225
|
const dx = cx - camPos[0], dy = cy - camPos[1], dz = cz - camPos[2];
|
|
191
226
|
const d = length3(dx, dy, dz);
|
|
192
|
-
const ppu =
|
|
227
|
+
const ppu = instancePixelsPerUnit(lodK, cx, cy, cz, d);
|
|
193
228
|
lod = selectLod(entry.lodDeviation, entry.lods, axisScale(scene.matrices, i), ppu, bias);
|
|
194
229
|
}
|
|
195
230
|
}
|