tosijs-3d 0.10.7 → 0.10.9
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/CHANGELOG.md +32 -0
- package/dist/b3d-planet.d.ts +34 -0
- package/dist/b3d-planet.d.ts.map +1 -1
- package/dist/b3d-planet.js +202 -57
- package/dist/b3d-planet.js.map +1 -1
- package/dist/b3d-terrain.d.ts +26 -2
- package/dist/b3d-terrain.d.ts.map +1 -1
- package/dist/b3d-terrain.js +131 -13
- package/dist/b3d-terrain.js.map +1 -1
- package/dist/b3d-utils.d.ts +5 -0
- package/dist/b3d-utils.d.ts.map +1 -1
- package/dist/b3d-utils.js +17 -0
- package/dist/b3d-utils.js.map +1 -1
- package/dist/dial-art.d.ts +3 -0
- package/dist/dial-art.d.ts.map +1 -0
- package/dist/dial-art.js +4 -0
- package/dist/dial-art.js.map +1 -0
- package/dist/dials.d.ts +76 -0
- package/dist/dials.d.ts.map +1 -0
- package/dist/dials.js +253 -0
- package/dist/dials.js.map +1 -0
- package/dist/gas-giant-material.d.ts +26 -0
- package/dist/gas-giant-material.d.ts.map +1 -1
- package/dist/gas-giant-material.js +45 -1
- package/dist/gas-giant-material.js.map +1 -1
- package/dist/gas-giant.d.ts +1 -0
- package/dist/gas-giant.d.ts.map +1 -1
- package/dist/gas-giant.js +57 -0
- package/dist/gas-giant.js.map +1 -1
- package/dist/icon-data.d.ts +1 -0
- package/dist/icon-data.d.ts.map +1 -1
- package/dist/icon-data.js +1 -0
- package/dist/icon-data.js.map +1 -1
- package/dist/scene-schemas.d.ts +16 -0
- package/dist/scene-schemas.d.ts.map +1 -1
- package/dist/scene-schemas.js +189 -0
- package/dist/scene-schemas.js.map +1 -1
- package/dist/sphere-map.d.ts +13 -1
- package/dist/sphere-map.d.ts.map +1 -1
- package/dist/sphere-map.js +38 -14
- package/dist/sphere-map.js.map +1 -1
- package/dist/tosi-b3d.d.ts.map +1 -1
- package/dist/tosi-b3d.js +8 -0
- package/dist/tosi-b3d.js.map +1 -1
- package/dist/widgets3d.d.ts.map +1 -1
- package/dist/widgets3d.js +44 -5
- package/dist/widgets3d.js.map +1 -1
- package/package.json +2 -1
- package/static/attributes.json +20 -0
- package/static/attributes.txt +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,38 @@ All notable changes to **tosijs-3d**. This project is pre-1.0 (`0.x`), so minor
|
|
|
6
6
|
versions may carry breaking peer-dependency changes — each is called out in a
|
|
7
7
|
**⚠️ Breaking** block in its version section below, with what a consumer must do.
|
|
8
8
|
|
|
9
|
+
## 0.10.9
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **A compass and a watch** (`import … from 'tosijs-3d/dials'`, not in the barrel: the artwork is 36 KB). Both are drawn by Tonio, and the code only moves their named parts. The compass needle points north as you turn (`sceneCompass(b3d)` follows the active camera, flat or in a headset; north is +Z). On the watch, a dot on the ring is the time of day (noon at the top, midnight at the bottom) and a dot on the bar is how fast it passes, from still to an hour a second. Drag either: `skyWatch(skybox)` reads and writes the sky's `timeOfDay` and `realtimeScale`. Each goes on the system bar as a `<tosi-b3d-panel icon title>`. Feather's `clock` icon is added. `bun run dial-art` re-embeds the SVGs after they are redrawn.
|
|
14
|
+
- **A planet and its sphere terrain are one world** (#3344). Tonio: "if you pick a point in the middle of what looks like the ocean, coast, mountain range on a planet you end up with corresponding terrain in the sphere terrain and vice versa." Either can be the source, stated by one attribute:
|
|
15
|
+
- **`<tosi-b3d-terrain planet="#p">`**: the ground takes its large shape (sea, coasts, mountain ranges) from the planet, scaled to its `radius` with the sea at 0. Its own detail layer and landforms ride on top, and it rebuilds when the planet changes (`planet.shapeKey`). Measured at 200 random places: the ground sits within the detail layer of the globe's height (mean 2.5 m, worst 3.9 m with 5 m of detail), and sea or land agrees except at the waterline.
|
|
16
|
+
- **`<tosi-b3d-planet terrain="#t">`**: the globe is drawn from the ground's own height function, and again whenever the ground's shape changes. This replaces the hand-written `planet.surface = terrain.planetSurface()` loop, and the sphere-map demo now uses it.
|
|
17
|
+
|
|
18
|
+
Linked, not automatic: a gas giant in a moon's sky, or every body of a star system, must not become the ground you stand on. `planet.relativeHeightAt(direction)` is the globe's height above its sea as a fraction of its radius. Both attributes are in the schemas.
|
|
19
|
+
- **Planetary rings catch shadows** (#3320). Both are worked out in the shaders for a sphere and a flat ring lit by one star, with no shadow maps. **The planet's shadow falls across its rings** (the dark wedge behind Saturn): each ring fragment asks whether the ray toward the star passes through the planet, softened slightly at the edge. This applies to any `<tosi-b3d-planet>` with `rings`. **The rings' shadow bands a gas giant**: each fragment finds where the ray toward the star crosses the ring plane and dims by how much ring is there, so the shadow has the ring's own gaps (a lighter stripe where the Cassini gap lets light through). The ring is now one GLSL function (`RING_GLSL` in `gas-giant`) that the ring draws itself from and the planet reads, and `createGasGiantMaterial` takes `ring: () => RingShadow | null` for a planet built by hand. Not yet: the rings' shadow on a ROCKY planet's surface.
|
|
20
|
+
|
|
21
|
+
Size: barrel (min, packages external) 1114.2 KB raw / 379.4 KB gz, +0.46% on 0.10.8.
|
|
22
|
+
|
|
23
|
+
## 0.10.8
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **`planetSchema()`** (`sceneSchemas.planet`): `<tosi-b3d-planet>` described like the other scene primitives, with sections (World, Surface, Sea, Air, Clouds, Craters, Rings, Gas giant, Maps), `heightMap`/`colorMap` marked as fetched, and the `world`/`gas` lists taken from the preset tables so they cannot drift. **`x-preset-written`** names the attributes a `world` preset writes into the planet. A consumer that applies schema defaults on bind should show them but not write them back over the preset (#103, ensemble).
|
|
28
|
+
- **`lat` / `lon` on `<tosi-b3d-terrain>`**: where this ground is on its planet, the latitude and longitude of the terrain's origin in degrees, so "this ground is at 12° N, 40° E" is one attribute pair, in markup and in `terrainSchema()`. `worldU`/`worldV` are now JS properties over them (`worldU = lon / 360`, `worldV = (lat + 90) / 180`). Before, they were plain properties that markup could not set and that needed a `regenerate()`. Changing `lat`/`lon` (or either view of them) rebuilds the ground. The defaults (-45, 0) are the old 0.25 / 0, so nothing moves (#103).
|
|
29
|
+
- **`seamBlend` / `seamWeight`** (`sphere-map`): make a layer laid out in flat metres periodic round a world, mixed with its copy one circumference away near the far meridian and untouched elsewhere.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **A widget bound to a tosijs box lets go of it** (#100). `toggle3d`, `slider3d` and `select3d` subscribed to a bound box and threw the unsubscribe away. Every bound row left a listener in tosijs's module-level list that held the row, its panel and its host, so ensemble's editor grew by 125 listeners and 5,360 nodes per round of panel rebuilds. Each now unsubscribes in its `dispose()`. `B3d` already disposes the widget lists it rebuilds, and **`panel3d(…)` has a `dispose()`** that disposes its rows, for a consumer that drops its own panels. Not reaped by `isConnected`: an in-scene panel's SVG is never in the document.
|
|
34
|
+
- **Removing the last instance of a library model no longer turns every later one white** (#102). `disposeMeshTree` disposed a material when no mesh in the scene used it, but a library's source meshes live in its AssetContainer, out of that list, so the guard could not see that the library still needed the material. Ensemble's editor lost the colour of a whole kit on its first rebuild, and a game would see it when the last enemy of a type despawned and the next one spawned white. It now disposes only materials the scene owns (`scene.materials`); a container's are the container's.
|
|
35
|
+
- **A frozen or paused scene builds its system bar** (#3342). The bar and flat panels were built only on running frames, so a scene that started frozen (Manta freezes on every menu) showed the HTML lozenge, and a `<tosi-b3d-panel icon title>` got no button until it unfroze. Stopped frames keep the scene's chrome current now, as they already did its fog.
|
|
36
|
+
- **A sphere terrain joins at the far meridian** (#3318). Its noise is read on the sphere and always repeated; its profile fields (`blendProfiles`, `profileField`) and landforms (`craterField`, …) read flat metres and did not. The ground opposite the terrain's origin (longitude 180 at the default `worldU`) was two different places: a 116 m step on Manta's cratered moon, 19.5 m without the craters. Within 5% of a turn of that meridian they are now mixed with their copy one turn away, so the two sides meet. Elsewhere the ground is exactly as before, and a place one whole turn round is the same place. Spheres only: a cylinder is used as a big flat world with things placed in metres.
|
|
37
|
+
- **A globe of a world with no sea is drawn at its size** (#3319). A planet drawn from a `PlanetSurface` measured every height from `seaLevel`, so a dry world that says "no sea" with a sea level far below its ground (Manta's -10000 m) gained that depth as ground. A globe meant to be 40 units across came out at 2476. The globe now measures from the higher of the sea and the lowest ground. A world with a real sea is unchanged, and on a dry world the lowest ground sits on the base radius. The convention for "no sea" is a `seaLevel` below all of the ground.
|
|
38
|
+
|
|
39
|
+
Size: barrel (min, packages external) 1109.1 KB raw / 377.6 KB gz, +0.38% on 0.10.7.
|
|
40
|
+
|
|
9
41
|
## 0.10.7
|
|
10
42
|
|
|
11
43
|
**What you will see on upgrading** (nothing exported in 0.10.6 is removed or renamed, but a `^0.10` range picks this up unasked):
|
package/dist/b3d-planet.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare class B3dPlanet extends B3dChild {
|
|
|
32
32
|
ringOuter: number;
|
|
33
33
|
ringOpacity: number;
|
|
34
34
|
ringColor: string;
|
|
35
|
+
terrain: string;
|
|
35
36
|
world: string;
|
|
36
37
|
craters: number;
|
|
37
38
|
seaShallow: string;
|
|
@@ -115,6 +116,35 @@ export declare class B3dPlanet extends B3dChild {
|
|
|
115
116
|
private _builtSea;
|
|
116
117
|
private _seaKey;
|
|
117
118
|
private _surfaceFromMap;
|
|
119
|
+
/** The sea level of the last build, in this planet's units. */
|
|
120
|
+
private _seaAt;
|
|
121
|
+
/** Bumped by every build: what `shapeKey` reports. */
|
|
122
|
+
private _shapeGen;
|
|
123
|
+
/**
|
|
124
|
+
* Changes whenever this planet's SHAPE does (every build). A terrain that
|
|
125
|
+
* takes its shape from this planet (`planet` on the terrain) compares it to
|
|
126
|
+
* know when to rebuild.
|
|
127
|
+
*/
|
|
128
|
+
get shapeKey(): string;
|
|
129
|
+
/**
|
|
130
|
+
* HOW HIGH THE GROUND IS ABOVE THE SEA in a direction from the centre, as
|
|
131
|
+
* a fraction of the planet's radius: what this globe draws there, with
|
|
132
|
+
* its sea at 0 (on a world with no sea, its lowest ground). A terrain
|
|
133
|
+
* with `planet` set multiplies it by its own radius, which is how the
|
|
134
|
+
* ground under a pin is the place the globe shows. Null before the
|
|
135
|
+
* planet is built, and for a gas giant.
|
|
136
|
+
*/
|
|
137
|
+
relativeHeightAt(d: {
|
|
138
|
+
x: number;
|
|
139
|
+
y: number;
|
|
140
|
+
z: number;
|
|
141
|
+
}): number | null;
|
|
142
|
+
/** The terrain `terrain` names, when it is a sphere. */
|
|
143
|
+
private _linkedTerrain;
|
|
144
|
+
private _terrainWarned;
|
|
145
|
+
private _terrainBuilt;
|
|
146
|
+
private _surfaceFromTerrain;
|
|
147
|
+
private _followTerrain;
|
|
118
148
|
/**
|
|
119
149
|
* Fetch `heightMap` (once per URL and sea setting) and rebuild from it. The
|
|
120
150
|
* planet shows as a smooth ball, in its colours, until the heights arrive.
|
|
@@ -138,6 +168,10 @@ export declare class B3dPlanet extends B3dChild {
|
|
|
138
168
|
* buildPlanet). Kept as a named step so the build order still reads.
|
|
139
169
|
*/
|
|
140
170
|
private buildOcean;
|
|
171
|
+
/** The ring's shape, for its shadow on the planet (null: no ring). */
|
|
172
|
+
private _ring;
|
|
173
|
+
/** The ring as the gas giant material's shadow needs it, now. */
|
|
174
|
+
private _ringShadow;
|
|
141
175
|
private buildRings;
|
|
142
176
|
/** The rocky world's look, when `world` names one (and it is not a gas giant). */
|
|
143
177
|
private _look;
|
package/dist/b3d-planet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"b3d-planet.d.ts","sourceRoot":"","sources":["../src/b3d-planet.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"b3d-planet.d.ts","sourceRoot":"","sources":["../src/b3d-planet.ts"],"names":[],"mappings":"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"}
|
package/dist/b3d-planet.js
CHANGED
|
@@ -167,6 +167,7 @@ tosi-b3d {
|
|
|
167
167
|
| `seaGlint` | `1` | The sun's glint on the sea, `0…1`: a patch of sparkle, broken up by fine wave texture, that slides over the water as you move |
|
|
168
168
|
| `seaIce` | `0` | Sea ice toward the poles, `0…1` |
|
|
169
169
|
| `inlandWater` | `'off'` | `'on'` guesses LAKES from a `colorMap`: where the picture is blue, or nearly black (how NASA's Blue Marble paints deep lakes), it is drawn as water: the map's colour, with the glint. The height map only knows the sea, and a lake is above it. A guess: blue ice or black lava can pass for a lake |
|
|
170
|
+
| `terrain` | `''` | A selector for the sphere `<tosi-b3d-terrain>` this is a GLOBE OF (`'#moon-ground'`): drawn from that ground's own height function, and again whenever its shape changes, so a place on the globe is the place on the ground. (The other way round, a terrain's `planet` gives the ground this planet's shape) |
|
|
170
171
|
| `relief` | `1` | With a `heightMap` or a `surface` set: how much its true relief is exaggerated. `1` is to scale, which on a globe is flat to the eye; 20 to 60 reads as a relief map |
|
|
171
172
|
| `world` | `''` | A ROCKY WORLD preset: `'moon'`, `'mercury'`, `'mars'`, `'venus'`, `'io'`, `'titan'` or `'earth'` (see [rocky-world](/rocky-world/)). Choosing one writes its sea, air, clouds, relief, craters and climate into this planet's own settings, which stay yours to tune; `worldOverrides` (a property) changes the preset for this planet |
|
|
172
173
|
| `craters` | `0` | How cratered, `0…1` (see landform's `sphereCraters`) |
|
|
@@ -201,7 +202,7 @@ tosi-b3d {
|
|
|
201
202
|
/*{ "parent": "space", "order": 40 }*/
|
|
202
203
|
import { Color } from 'tosijs';
|
|
203
204
|
import { isOff, B3dChild, sceneDelta, sceneSeconds } from './b3d-utils.js';
|
|
204
|
-
import { GAS_GIANT_GLSL, gasGiantRgb, resolveGasGiant } from './gas-giant.js';
|
|
205
|
+
import { GAS_GIANT_GLSL, RING_GLSL, gasGiantRgb, resolveGasGiant, } from './gas-giant.js';
|
|
205
206
|
import { climateForGlobe } from './sphere-map.js';
|
|
206
207
|
import { loadPlanetMap } from './planet-map.js';
|
|
207
208
|
import { createPlanetClouds } from './planet-clouds.js';
|
|
@@ -274,6 +275,14 @@ export class B3dPlanet extends B3dChild {
|
|
|
274
275
|
ringOuter: 0,
|
|
275
276
|
ringOpacity: -1,
|
|
276
277
|
ringColor: '',
|
|
278
|
+
/*
|
|
279
|
+
THE GROUND THIS IS A GLOBE OF (#3344): a selector for a sphere
|
|
280
|
+
`<tosi-b3d-terrain>` ('#moon-ground'). The planet draws from it, and
|
|
281
|
+
draws again whenever the ground's shape changes, so the globe and the
|
|
282
|
+
ground are one world. '' for none. (A terrain can take its shape from
|
|
283
|
+
a planet instead: its `planet` attribute.)
|
|
284
|
+
*/
|
|
285
|
+
terrain: '',
|
|
277
286
|
// A ROCKY WORLD's look (rocky-world): 'moon', 'mercury', 'mars',
|
|
278
287
|
// 'venus', 'earth'. A preset: choosing one writes its sea, air, clouds,
|
|
279
288
|
// relief, craters and climate into this planet's own settings, which
|
|
@@ -407,6 +416,7 @@ export class B3dPlanet extends B3dChild {
|
|
|
407
416
|
}
|
|
408
417
|
}
|
|
409
418
|
update() {
|
|
419
|
+
this._followTerrain();
|
|
410
420
|
this._followGroundClimate();
|
|
411
421
|
if (this.rootNode == null)
|
|
412
422
|
return;
|
|
@@ -444,7 +454,9 @@ export class B3dPlanet extends B3dChild {
|
|
|
444
454
|
turbulence: attrs.turbulence,
|
|
445
455
|
storm: attrs.storm,
|
|
446
456
|
colors: attrs.bandColors,
|
|
447
|
-
})
|
|
457
|
+
}),
|
|
458
|
+
// The rings' shadow on the bands (#3320).
|
|
459
|
+
{ ring: () => this._ringShadow() });
|
|
448
460
|
mesh.parent = this.rootNode;
|
|
449
461
|
this.planetMesh = mesh;
|
|
450
462
|
this.vertexHeights = null;
|
|
@@ -510,6 +522,31 @@ export class B3dPlanet extends B3dChild {
|
|
|
510
522
|
as data (uv.x), so the shader can tell land from water per pixel and
|
|
511
523
|
knows how deep the water is. There is no sea sphere.
|
|
512
524
|
*/
|
|
525
|
+
/*
|
|
526
|
+
A SEA BELOW ALL THE GROUND IS NO SEA (board #3319). Heights from a
|
|
527
|
+
surface are measured from its `seaLevel`, so a dry world that says "no
|
|
528
|
+
sea" with a sea far down (Manta's moon: -10000 m under ground near 320 m)
|
|
529
|
+
gained ten kilometres of ground and was drawn hundreds of times too big.
|
|
530
|
+
The datum is the higher of the sea and the lowest ground: with a real sea
|
|
531
|
+
nothing changes, and on a dry world the lowest ground sits on the base
|
|
532
|
+
radius.
|
|
533
|
+
*/
|
|
534
|
+
if (this.surface != null) {
|
|
535
|
+
let lowest = Infinity;
|
|
536
|
+
for (let i = 0; i < totalVerts; i++)
|
|
537
|
+
if (heights[i] < lowest)
|
|
538
|
+
lowest = heights[i];
|
|
539
|
+
if (lowest > 0 && Number.isFinite(lowest)) {
|
|
540
|
+
for (let i = 0; i < totalVerts; i++) {
|
|
541
|
+
heights[i] -= lowest;
|
|
542
|
+
const vi = i * 3;
|
|
543
|
+
const r = radius + heights[i];
|
|
544
|
+
positions[vi] = normals[vi] * r;
|
|
545
|
+
positions[vi + 1] = normals[vi + 1] * r;
|
|
546
|
+
positions[vi + 2] = normals[vi + 2] * r;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
513
550
|
let highest = 1e-3;
|
|
514
551
|
let deepest = 1e-3;
|
|
515
552
|
this.vertexHeights = heights;
|
|
@@ -517,6 +554,8 @@ export class B3dPlanet extends B3dChild {
|
|
|
517
554
|
const seaAt = this.surface != null || !hasSea
|
|
518
555
|
? 0
|
|
519
556
|
: this.heightPercentile(this._val('ocean'));
|
|
557
|
+
this._seaAt = seaAt;
|
|
558
|
+
this._shapeGen++;
|
|
520
559
|
const heightData = new Float32Array(totalVerts * 2);
|
|
521
560
|
for (let i = 0; i < totalVerts; i++) {
|
|
522
561
|
const above = heights[i] - seaAt;
|
|
@@ -696,6 +735,97 @@ export class B3dPlanet extends B3dChild {
|
|
|
696
735
|
return `${this._val('ocean')}|${attrs.flatSea}|${attrs.world}`;
|
|
697
736
|
}
|
|
698
737
|
_surfaceFromMap = false;
|
|
738
|
+
/** The sea level of the last build, in this planet's units. */
|
|
739
|
+
_seaAt = 0;
|
|
740
|
+
/** Bumped by every build: what `shapeKey` reports. */
|
|
741
|
+
_shapeGen = 0;
|
|
742
|
+
/**
|
|
743
|
+
* Changes whenever this planet's SHAPE does (every build). A terrain that
|
|
744
|
+
* takes its shape from this planet (`planet` on the terrain) compares it to
|
|
745
|
+
* know when to rebuild.
|
|
746
|
+
*/
|
|
747
|
+
get shapeKey() {
|
|
748
|
+
return `${this.seed}|${this._shapeGen}`;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* HOW HIGH THE GROUND IS ABOVE THE SEA in a direction from the centre, as
|
|
752
|
+
* a fraction of the planet's radius: what this globe draws there, with
|
|
753
|
+
* its sea at 0 (on a world with no sea, its lowest ground). A terrain
|
|
754
|
+
* with `planet` set multiplies it by its own radius, which is how the
|
|
755
|
+
* ground under a pin is the place the globe shows. Null before the
|
|
756
|
+
* planet is built, and for a gas giant.
|
|
757
|
+
*/
|
|
758
|
+
relativeHeightAt(d) {
|
|
759
|
+
const attrs = this;
|
|
760
|
+
if (this.noise == null || attrs.gas)
|
|
761
|
+
return null;
|
|
762
|
+
const len = Math.hypot(d.x, d.y, d.z) || 1;
|
|
763
|
+
const s = this.surface;
|
|
764
|
+
if (s != null) {
|
|
765
|
+
const h = s.heightAt({ x: d.x / len, y: d.y / len, z: d.z / len });
|
|
766
|
+
return (h - s.seaLevel) / s.radius;
|
|
767
|
+
}
|
|
768
|
+
return ((this.heightAt(d.x / len, d.y / len, d.z / len) - this._seaAt) /
|
|
769
|
+
attrs.radius);
|
|
770
|
+
}
|
|
771
|
+
/** The terrain `terrain` names, when it is a sphere. */
|
|
772
|
+
_linkedTerrain() {
|
|
773
|
+
const sel = String(this.terrain ?? '').trim();
|
|
774
|
+
if (sel === '' || this.owner == null)
|
|
775
|
+
return null;
|
|
776
|
+
let el;
|
|
777
|
+
try {
|
|
778
|
+
el =
|
|
779
|
+
this.owner.querySelector(sel) ??
|
|
780
|
+
document.querySelector(sel);
|
|
781
|
+
}
|
|
782
|
+
catch {
|
|
783
|
+
if (!this._terrainWarned) {
|
|
784
|
+
this._terrainWarned = true;
|
|
785
|
+
console.warn(`b3d-planet: terrain "${sel}" is not a valid selector`);
|
|
786
|
+
}
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
789
|
+
const t = el;
|
|
790
|
+
if (t == null || typeof t.planetSurface !== 'function')
|
|
791
|
+
return null;
|
|
792
|
+
return t;
|
|
793
|
+
}
|
|
794
|
+
_terrainWarned = false;
|
|
795
|
+
_terrainBuilt = '';
|
|
796
|
+
_surfaceFromTerrain = false;
|
|
797
|
+
/*
|
|
798
|
+
FOLLOW THE GROUND (#3344). Tonio: "if you pick a point in the middle of
|
|
799
|
+
what looks like the ocean, coast, mountain range on a planet you end up
|
|
800
|
+
with corresponding terrain in the sphere terrain and vice versa." The
|
|
801
|
+
globe is drawn from the terrain's own height function, again whenever
|
|
802
|
+
its `generationKey` changes. Not when that terrain takes ITS shape from
|
|
803
|
+
this planet: each would follow the other.
|
|
804
|
+
*/
|
|
805
|
+
_followTerrain() {
|
|
806
|
+
const t = this._linkedTerrain();
|
|
807
|
+
if (t == null) {
|
|
808
|
+
if (this._surfaceFromTerrain) {
|
|
809
|
+
this._surfaceFromTerrain = false;
|
|
810
|
+
this._terrainBuilt = '';
|
|
811
|
+
this.surface = null;
|
|
812
|
+
this.regenerate();
|
|
813
|
+
}
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
if (typeof t.planetSource === 'function' && t.planetSource() === this)
|
|
817
|
+
return;
|
|
818
|
+
const key = t.generationKey;
|
|
819
|
+
if (!key || key === this._terrainBuilt)
|
|
820
|
+
return;
|
|
821
|
+
const surface = t.planetSurface();
|
|
822
|
+
if (surface == null)
|
|
823
|
+
return;
|
|
824
|
+
this._terrainBuilt = key;
|
|
825
|
+
this._surfaceFromTerrain = true;
|
|
826
|
+
this.surface = surface;
|
|
827
|
+
this.regenerate();
|
|
828
|
+
}
|
|
699
829
|
/**
|
|
700
830
|
* Fetch `heightMap` (once per URL and sea setting) and rebuild from it. The
|
|
701
831
|
* planet shows as a smooth ball, in its colours, until the heights arrive.
|
|
@@ -946,7 +1076,25 @@ export class B3dPlanet extends B3dChild {
|
|
|
946
1076
|
buildOcean() {
|
|
947
1077
|
this.oceanMesh = null;
|
|
948
1078
|
}
|
|
1079
|
+
/** The ring's shape, for its shadow on the planet (null: no ring). */
|
|
1080
|
+
_ring = null;
|
|
1081
|
+
/** The ring as the gas giant material's shadow needs it, now. */
|
|
1082
|
+
_ringShadow() {
|
|
1083
|
+
const ring = this._ring;
|
|
1084
|
+
const root = this.rootNode;
|
|
1085
|
+
if (ring == null || root == null)
|
|
1086
|
+
return null;
|
|
1087
|
+
const c = root.getAbsolutePosition();
|
|
1088
|
+
const up = BABYLON.Vector3.TransformNormal(BABYLON.Axis.Y, root.getWorldMatrix()).normalize();
|
|
1089
|
+
return {
|
|
1090
|
+
...ring,
|
|
1091
|
+
center: { x: c.x, y: c.y, z: c.z },
|
|
1092
|
+
radius: this.radius * Math.abs(root.absoluteScaling.x),
|
|
1093
|
+
normal: { x: up.x, y: up.y, z: up.z },
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
949
1096
|
buildRings() {
|
|
1097
|
+
this._ring = null;
|
|
950
1098
|
if (this.owner == null || this.rootNode == null)
|
|
951
1099
|
return;
|
|
952
1100
|
const attrs = this;
|
|
@@ -956,27 +1104,34 @@ export class B3dPlanet extends B3dChild {
|
|
|
956
1104
|
const scene = this.owner.scene;
|
|
957
1105
|
const radius = attrs.radius;
|
|
958
1106
|
// Register ring shader once
|
|
959
|
-
if (!BABYLON.Effect.ShadersStore['
|
|
960
|
-
BABYLON.Effect.ShadersStore['
|
|
1107
|
+
if (!BABYLON.Effect.ShadersStore['planetRing7VertexShader']) {
|
|
1108
|
+
BABYLON.Effect.ShadersStore['planetRing7VertexShader'] = `
|
|
961
1109
|
precision highp float;
|
|
962
1110
|
attribute vec3 position;
|
|
963
1111
|
attribute vec2 uv;
|
|
1112
|
+
uniform mat4 world;
|
|
964
1113
|
uniform mat4 worldViewProjection;
|
|
965
1114
|
varying vec2 vUV;
|
|
1115
|
+
varying vec3 vWorld;
|
|
966
1116
|
void main() {
|
|
967
1117
|
gl_Position = worldViewProjection * vec4(position, 1.0);
|
|
1118
|
+
vWorld = (world * vec4(position, 1.0)).xyz;
|
|
968
1119
|
vUV = uv;
|
|
969
1120
|
}
|
|
970
1121
|
`;
|
|
971
|
-
BABYLON.Effect.ShadersStore['
|
|
1122
|
+
BABYLON.Effect.ShadersStore['planetRing7FragmentShader'] = `
|
|
972
1123
|
#extension GL_OES_standard_derivatives : enable
|
|
973
1124
|
precision highp float;
|
|
974
1125
|
varying vec2 vUV;
|
|
1126
|
+
varying vec3 vWorld;
|
|
1127
|
+
uniform vec4 ringLight;
|
|
1128
|
+
uniform vec4 ringPlanet;
|
|
975
1129
|
uniform vec3 ringColor;
|
|
976
1130
|
uniform float ringOpacity;
|
|
977
1131
|
uniform float ringInner;
|
|
978
1132
|
uniform float seed;
|
|
979
1133
|
|
|
1134
|
+
${RING_GLSL}
|
|
980
1135
|
float hash(float p) {
|
|
981
1136
|
return fract(sin(p * 127.1) * 43758.5453);
|
|
982
1137
|
}
|
|
@@ -986,49 +1141,13 @@ export class B3dPlanet extends B3dChild {
|
|
|
986
1141
|
vec2 uv = vUV * 2.0 - 1.0;
|
|
987
1142
|
float r = length(uv);
|
|
988
1143
|
if (r < ringInner || r > 1.0) discard;
|
|
989
|
-
|
|
990
|
-
// Normalize to ring band
|
|
991
1144
|
float t = (r - ringInner) / (1.0 - ringInner);
|
|
992
|
-
|
|
993
|
-
// SHARP, as rings are, and irregular: the density along the
|
|
994
|
-
// radius is 1D noise at several scales, TERRACED, so it holds
|
|
995
|
-
// plateaus of different widths and steps between them with edges
|
|
996
|
-
// about a pixel wide. Only detail finer than a pixel fades to its
|
|
997
|
-
// average (drawn, it shimmered into moire and flickered).
|
|
998
1145
|
float px = max(fwidth(t), 1e-5);
|
|
999
1146
|
float theta = atan(uv.y, uv.x);
|
|
1000
|
-
|
|
1001
|
-
//
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
float gapB = hash(seed + 1.0) * 0.15 + 0.8;
|
|
1005
|
-
float near = abs(t - gapB);
|
|
1006
|
-
t += 0.004 * sin(theta * 36.0) * exp(-near / 0.02);
|
|
1007
|
-
|
|
1008
|
-
float n = 0.0;
|
|
1009
|
-
float a = 0.5;
|
|
1010
|
-
float f = 6.0;
|
|
1011
|
-
float norm = 0.0;
|
|
1012
|
-
for (float i = 0.0; i < 5.0; i++) {
|
|
1013
|
-
float x = t * f + seed * 13.1 + i * 7.3;
|
|
1014
|
-
float c = floor(x);
|
|
1015
|
-
float k = fract(x);
|
|
1016
|
-
float vn = mix(hash(c), hash(c + 1.0), k * k * (3.0 - 2.0 * k));
|
|
1017
|
-
float fade = 1.0 - smoothstep(0.3, 0.6, f * px);
|
|
1018
|
-
n += a * fade * (vn - 0.5);
|
|
1019
|
-
norm += a;
|
|
1020
|
-
a *= 0.55;
|
|
1021
|
-
f *= 2.7;
|
|
1022
|
-
}
|
|
1023
|
-
n = clamp(n / norm * 1.6 + 0.5, 0.0, 1.0);
|
|
1024
|
-
float steps = n * 6.0;
|
|
1025
|
-
float sw = max(fwidth(steps), 1e-4);
|
|
1026
|
-
float terrace = (floor(steps) + smoothstep(1.0 - sw, 1.0, fract(steps))) / 6.0;
|
|
1027
|
-
float density = mix(n, terrace, 0.75);
|
|
1028
|
-
|
|
1029
|
-
// The broad profile: faint inside, brightest a little out.
|
|
1030
|
-
float profile = 0.35 + 0.65 * sin(3.1416 * pow(t, 0.75));
|
|
1031
|
-
float bands = profile * (0.3 + 1.1 * density) * 1.3;
|
|
1147
|
+
// The ring itself is one function, shared with the planet that
|
|
1148
|
+
// reads its shadow (gas-giant RING_GLSL).
|
|
1149
|
+
vec2 ring = b3dRing(t, theta, seed, px);
|
|
1150
|
+
float bands = ring.x;
|
|
1032
1151
|
|
|
1033
1152
|
// Imperfect round the ring: a faint clumping by angle and radius.
|
|
1034
1153
|
vec2 cq = uv * 9.0 + seed;
|
|
@@ -1040,18 +1159,22 @@ export class B3dPlanet extends B3dChild {
|
|
|
1040
1159
|
float h01 = hash(ci.x + (ci.y + 1.0) * 57.0);
|
|
1041
1160
|
float h11 = hash(ci.x + 1.0 + (ci.y + 1.0) * 57.0);
|
|
1042
1161
|
float clump = mix(mix(h00, h10, cf.x), mix(h01, h11, cf.x), cf.y);
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
// cut clean, a pixel soft.
|
|
1047
|
-
float g1 = abs(t - hash(seed) * 0.4 - 0.45);
|
|
1048
|
-
float gap1 = smoothstep(0.022 - px, 0.022 + px, g1);
|
|
1049
|
-
float gap2 = smoothstep(0.005 - px, 0.005 + px, abs(t - gapB));
|
|
1050
|
-
bands *= mix(0.04, 1.0, gap1) * mix(0.06, 1.0, gap2);
|
|
1162
|
+
float k = 0.95 + 0.1 * clump;
|
|
1163
|
+
bands *= k;
|
|
1164
|
+
float alpha = clamp(ring.y * k, 0.0, 1.0) * ringOpacity;
|
|
1051
1165
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1166
|
+
/*
|
|
1167
|
+
THE PLANET'S SHADOW ON THE RING (#3320): the dark wedge behind
|
|
1168
|
+
the planet. From this point toward the star, does the ray pass
|
|
1169
|
+
through the planet? A sphere, so it is exact, softened over a
|
|
1170
|
+
couple of percent of the radius for the penumbra.
|
|
1171
|
+
*/
|
|
1172
|
+
vec3 toL = ringLight.w > 0.5 ? normalize(ringLight.xyz - vWorld) : normalize(ringLight.xyz);
|
|
1173
|
+
vec3 oc = ringPlanet.xyz - vWorld;
|
|
1174
|
+
float along = dot(oc, toL);
|
|
1175
|
+
float miss = length(oc - toL * along);
|
|
1176
|
+
float lit = along > 0.0 ? smoothstep(ringPlanet.w * 0.97, ringPlanet.w * 1.03, miss) : 1.0;
|
|
1177
|
+
bands *= mix(0.03, 1.0, lit);
|
|
1055
1178
|
|
|
1056
1179
|
gl_FragColor = vec4(ringColor * bands, alpha);
|
|
1057
1180
|
}
|
|
@@ -1080,10 +1203,13 @@ export class B3dPlanet extends B3dChild {
|
|
|
1080
1203
|
mesh.rotation.x = Math.PI / 2; // Flat in XZ plane
|
|
1081
1204
|
// In the equator, as rings are. It used to be tipped off it, and as
|
|
1082
1205
|
// the planet turned (the ring is its child) the ring precessed.
|
|
1083
|
-
const mat = new BABYLON.ShaderMaterial('planet-ring-mat', scene, { vertex: '
|
|
1206
|
+
const mat = new BABYLON.ShaderMaterial('planet-ring-mat', scene, { vertex: 'planetRing7', fragment: 'planetRing7' }, {
|
|
1084
1207
|
attributes: ['position', 'uv'],
|
|
1085
1208
|
uniforms: [
|
|
1209
|
+
'world',
|
|
1086
1210
|
'worldViewProjection',
|
|
1211
|
+
'ringLight',
|
|
1212
|
+
'ringPlanet',
|
|
1087
1213
|
'ringColor',
|
|
1088
1214
|
'ringOpacity',
|
|
1089
1215
|
'ringInner',
|
|
@@ -1095,8 +1221,27 @@ export class B3dPlanet extends B3dChild {
|
|
|
1095
1221
|
mat.setFloat('ringOpacity', Math.max(0, Math.min(1, opacity)));
|
|
1096
1222
|
mat.setFloat('ringInner', innerRadii / outerRadii);
|
|
1097
1223
|
mat.setFloat('seed', attrs.seed % 100);
|
|
1224
|
+
this._ring = {
|
|
1225
|
+
inner: innerRadii,
|
|
1226
|
+
outer: outerRadii,
|
|
1227
|
+
opacity: Math.max(0, Math.min(1, opacity)),
|
|
1228
|
+
seed: attrs.seed % 100,
|
|
1229
|
+
};
|
|
1098
1230
|
mat.backFaceCulling = false;
|
|
1099
1231
|
mat.alphaMode = BABYLON.Constants.ALPHA_COMBINE;
|
|
1232
|
+
// Where the star and the planet are, each frame, for the shadow.
|
|
1233
|
+
const light = new BABYLON.Vector4(0, 1, 0, 0);
|
|
1234
|
+
const planet = new BABYLON.Vector4();
|
|
1235
|
+
mat.onBind = () => {
|
|
1236
|
+
sceneLight(scene, light);
|
|
1237
|
+
mat.setVector4('ringLight', light);
|
|
1238
|
+
const root = this.rootNode;
|
|
1239
|
+
if (root != null) {
|
|
1240
|
+
const c = root.getAbsolutePosition();
|
|
1241
|
+
planet.set(c.x, c.y, c.z, radius * Math.abs(root.absoluteScaling.x));
|
|
1242
|
+
}
|
|
1243
|
+
mat.setVector4('ringPlanet', planet);
|
|
1244
|
+
};
|
|
1100
1245
|
mesh.material = mat;
|
|
1101
1246
|
mesh.parent = this.rootNode;
|
|
1102
1247
|
this.ringMesh = mesh;
|