@reearth/core 0.0.7-alpha.44 → 0.0.7-alpha.46
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/dist/core.js +3900 -3850
- package/dist/core.umd.cjs +65 -65
- package/package.json +1 -1
- package/src/engines/Cesium/Feature/Tileset/hooks.ts +57 -40
- package/src/engines/Cesium/core/Globe/index.tsx +73 -0
- package/src/engines/Cesium/core/Globe/useTerrainProviderPromise.ts +79 -0
- package/src/engines/Cesium/hooks/useOverrideGlobeShader/useOverrideGlobeShader.ts +95 -49
- package/src/engines/Cesium/core/Globe.tsx +0 -113
package/package.json
CHANGED
|
@@ -187,6 +187,7 @@ const useFeature = ({
|
|
|
187
187
|
onComputedFeatureFetch,
|
|
188
188
|
shouldUseFeatureIndex,
|
|
189
189
|
isTilesetReady,
|
|
190
|
+
useExternalStyle,
|
|
190
191
|
}: {
|
|
191
192
|
id?: string;
|
|
192
193
|
tilesetRef: MutableRefObject<Cesium3DTileset | undefined>;
|
|
@@ -199,6 +200,7 @@ const useFeature = ({
|
|
|
199
200
|
selectedFeatureIdsRef: MutableRefObject<string[]>;
|
|
200
201
|
shouldUseFeatureIndex?: boolean;
|
|
201
202
|
isTilesetReady: boolean;
|
|
203
|
+
useExternalStyle?: boolean;
|
|
202
204
|
}) => {
|
|
203
205
|
const cachedFeaturesRef = useRef<CachedFeature[]>([]);
|
|
204
206
|
const cachedCalculatedLayerRef = useRef(layer);
|
|
@@ -220,49 +222,51 @@ const useFeature = ({
|
|
|
220
222
|
|
|
221
223
|
const computedFeature = evalFeature(layer, { ...feature?.feature, properties });
|
|
222
224
|
|
|
223
|
-
|
|
225
|
+
if (!useExternalStyle) {
|
|
226
|
+
const style = computedFeature?.["3dtiles"];
|
|
224
227
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (name === "color") {
|
|
229
|
-
// Reset color to default so that new style could update all.
|
|
230
|
-
raw.color = DEFAULT_FEATURE_COLOR;
|
|
228
|
+
COMMON_STYLE_PROPERTIES.forEach(({ name, convert }) => {
|
|
229
|
+
const val = convertStyle(style?.[name], convert);
|
|
231
230
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
raw.color =
|
|
231
|
+
if (name === "color") {
|
|
232
|
+
// Reset color to default so that new style could update all.
|
|
233
|
+
raw.color = DEFAULT_FEATURE_COLOR;
|
|
234
|
+
|
|
235
|
+
// Apply color from style.
|
|
236
|
+
if (val !== undefined) {
|
|
237
|
+
raw.color = val;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Apply color for selected feature.
|
|
241
|
+
if (isFeatureSelected && typeof layer["3dtiles"]?.selectedFeatureColor === "string") {
|
|
242
|
+
raw.color = toColor(layer["3dtiles"]?.selectedFeatureColor) ?? val;
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
if (val !== undefined) {
|
|
246
|
+
raw[name] = val;
|
|
247
|
+
}
|
|
235
248
|
}
|
|
249
|
+
});
|
|
236
250
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
251
|
+
if (raw instanceof Cesium3DTilePointFeature) {
|
|
252
|
+
POINT_STYLE_PROPERTIES.forEach(({ name, convert }) => {
|
|
253
|
+
const val = convertStyle(style?.[name], convert);
|
|
254
|
+
if (val !== undefined) {
|
|
255
|
+
raw[name] = val;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
245
258
|
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
if (raw instanceof Cesium3DTilePointFeature) {
|
|
249
|
-
POINT_STYLE_PROPERTIES.forEach(({ name, convert }) => {
|
|
250
|
-
const val = convertStyle(style?.[name], convert);
|
|
251
|
-
if (val !== undefined) {
|
|
252
|
-
raw[name] = val;
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
259
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
if ("style" in raw) {
|
|
261
|
+
raw.style = new Cesium3DTileStyle(
|
|
262
|
+
// TODO: Convert value if it's necessary
|
|
263
|
+
MODEL_STYLE_PROPERTIES.reduce((res, { name, convert }) => {
|
|
264
|
+
const val = convertStyle(style?.[name as keyof typeof style], convert);
|
|
265
|
+
if (val === undefined) return res;
|
|
266
|
+
return { ...res, [name]: val };
|
|
267
|
+
}, {}),
|
|
268
|
+
);
|
|
269
|
+
}
|
|
266
270
|
}
|
|
267
271
|
|
|
268
272
|
attachTag(feature.raw, {
|
|
@@ -276,7 +280,7 @@ const useFeature = ({
|
|
|
276
280
|
}
|
|
277
281
|
return;
|
|
278
282
|
},
|
|
279
|
-
[evalFeature, layerId, viewer, shouldUseFeatureIndex, selectedFeatureIdsRef],
|
|
283
|
+
[evalFeature, layerId, viewer, shouldUseFeatureIndex, selectedFeatureIdsRef, useExternalStyle],
|
|
280
284
|
);
|
|
281
285
|
|
|
282
286
|
const handleTilesetLoad = useCallback(
|
|
@@ -615,6 +619,7 @@ export const useHooks = ({
|
|
|
615
619
|
selectedFeatureIdsRef,
|
|
616
620
|
shouldUseFeatureIndex,
|
|
617
621
|
isTilesetReady,
|
|
622
|
+
useExternalStyle: !!styleUrl,
|
|
618
623
|
});
|
|
619
624
|
|
|
620
625
|
const [terrainHeightEstimate, setTerrainHeightEstimate] = useState(0);
|
|
@@ -730,11 +735,23 @@ export const useHooks = ({
|
|
|
730
735
|
}
|
|
731
736
|
(async () => {
|
|
732
737
|
const res = await fetch(styleUrl);
|
|
733
|
-
if (!res.ok)
|
|
734
|
-
|
|
738
|
+
if (!res.ok) {
|
|
739
|
+
console.warn("Failed to fetch style from:", styleUrl);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
const styleData = await res.json();
|
|
743
|
+
const newStyle = new Cesium3DTileStyle(styleData);
|
|
744
|
+
setStyle(newStyle);
|
|
735
745
|
})();
|
|
736
746
|
}, [styleUrl]);
|
|
737
747
|
|
|
748
|
+
// Apply style to tileset when both external style and tileset are ready
|
|
749
|
+
useEffect(() => {
|
|
750
|
+
if (style && tilesetRef.current && isTilesetReady) {
|
|
751
|
+
tilesetRef.current.style = style;
|
|
752
|
+
}
|
|
753
|
+
}, [style, isTilesetReady]);
|
|
754
|
+
|
|
738
755
|
const googleMapPhotorealisticResource = useMemo(() => {
|
|
739
756
|
if (type !== "google-photorealistic" || !isVisible) return null;
|
|
740
757
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { Globe as CesiumGlobe } from "resium";
|
|
3
|
+
|
|
4
|
+
import type { ViewerProperty } from "../../..";
|
|
5
|
+
import { toColor } from "../../common";
|
|
6
|
+
|
|
7
|
+
import useTerrainProviderPromise from "./useTerrainProviderPromise";
|
|
8
|
+
|
|
9
|
+
export type Props = {
|
|
10
|
+
property?: ViewerProperty;
|
|
11
|
+
cesiumIonAccessToken?: string;
|
|
12
|
+
onTerrainProviderChange?: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default function Globe({
|
|
16
|
+
property,
|
|
17
|
+
cesiumIonAccessToken,
|
|
18
|
+
onTerrainProviderChange,
|
|
19
|
+
}: Props): JSX.Element | null {
|
|
20
|
+
const providerPromise = useTerrainProviderPromise({
|
|
21
|
+
terrain: property?.terrain?.enabled,
|
|
22
|
+
terrainType: property?.terrain?.type,
|
|
23
|
+
normal: property?.terrain?.normal,
|
|
24
|
+
ionAccessToken: property?.assets?.cesium?.terrain?.ionAccessToken || cesiumIonAccessToken,
|
|
25
|
+
ionAsset: property?.assets?.cesium?.terrain?.ionAsset,
|
|
26
|
+
ionUrl: property?.assets?.cesium?.terrain?.ionUrl,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const baseColor = useMemo(
|
|
30
|
+
() => toColor(property?.globe?.baseColor),
|
|
31
|
+
[property?.globe?.baseColor],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const lastResolvedProviderRef = useRef<any>(null);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
let isCancelled = false;
|
|
38
|
+
|
|
39
|
+
providerPromise
|
|
40
|
+
.then(resolvedProvider => {
|
|
41
|
+
if (isCancelled) return;
|
|
42
|
+
|
|
43
|
+
// Only trigger callback if the resolved provider is actually different
|
|
44
|
+
if (lastResolvedProviderRef.current !== resolvedProvider) {
|
|
45
|
+
lastResolvedProviderRef.current = resolvedProvider;
|
|
46
|
+
onTerrainProviderChange?.();
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
.catch(error => {
|
|
50
|
+
if (!isCancelled) {
|
|
51
|
+
console.warn("Terrain provider failed to load:", error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
isCancelled = true;
|
|
57
|
+
};
|
|
58
|
+
}, [providerPromise, onTerrainProviderChange]);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<CesiumGlobe
|
|
62
|
+
baseColor={baseColor}
|
|
63
|
+
enableLighting={!!property?.globe?.enableLighting}
|
|
64
|
+
showGroundAtmosphere={property?.globe?.atmosphere?.enabled ?? true}
|
|
65
|
+
atmosphereLightIntensity={property?.globe?.atmosphere?.lightIntensity}
|
|
66
|
+
atmosphereSaturationShift={property?.globe?.atmosphere?.saturationShift}
|
|
67
|
+
atmosphereHueShift={property?.globe?.atmosphere?.hueShift}
|
|
68
|
+
atmosphereBrightnessShift={property?.globe?.atmosphere?.brightnessShift}
|
|
69
|
+
terrainProvider={providerPromise}
|
|
70
|
+
depthTestAgainstTerrain={!!property?.globe?.depthTestAgainstTerrain}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArcGISTiledElevationTerrainProvider,
|
|
3
|
+
CesiumTerrainProvider,
|
|
4
|
+
EllipsoidTerrainProvider,
|
|
5
|
+
IonResource,
|
|
6
|
+
TerrainProvider,
|
|
7
|
+
} from "cesium";
|
|
8
|
+
import { useMemo, useRef } from "react";
|
|
9
|
+
|
|
10
|
+
import { TerrainProperty } from "../../..";
|
|
11
|
+
import { AssetsCesiumProperty } from "../../../../Map";
|
|
12
|
+
|
|
13
|
+
type TerrainType = NonNullable<TerrainProperty["type"]>;
|
|
14
|
+
|
|
15
|
+
type ProviderOpts = Pick<TerrainProperty, "normal"> &
|
|
16
|
+
AssetsCesiumProperty["terrain"] & {
|
|
17
|
+
terrain?: boolean;
|
|
18
|
+
terrainType?: TerrainType | null | undefined;
|
|
19
|
+
ionAccessToken?: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default function useTerrainProviderPromise(opts: ProviderOpts) {
|
|
23
|
+
// Cache promises so we don’t recreate providers on every toggle
|
|
24
|
+
const cacheRef = useRef(new Map<string, Promise<TerrainProvider>>());
|
|
25
|
+
const ellipsoidRef = useRef<TerrainProvider>();
|
|
26
|
+
|
|
27
|
+
return useMemo<Promise<TerrainProvider>>(() => {
|
|
28
|
+
if (!opts.terrain) {
|
|
29
|
+
// single shared ellipsoid provider
|
|
30
|
+
if (!ellipsoidRef.current) ellipsoidRef.current = new EllipsoidTerrainProvider();
|
|
31
|
+
return Promise.resolve(ellipsoidRef.current);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const kind = (opts.terrainType ?? "cesium") as TerrainType;
|
|
35
|
+
const key = makeKey(kind, opts);
|
|
36
|
+
let p = cacheRef.current.get(key);
|
|
37
|
+
if (!p) {
|
|
38
|
+
p = createProvider(kind, opts);
|
|
39
|
+
cacheRef.current.set(key, p);
|
|
40
|
+
}
|
|
41
|
+
return p;
|
|
42
|
+
}, [opts]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function makeKey(type: TerrainType, opts: ProviderOpts) {
|
|
46
|
+
// Key should change when output provider would differ
|
|
47
|
+
const asset = opts.ionAsset ?? "";
|
|
48
|
+
const url = opts.ionUrl ?? "";
|
|
49
|
+
const normal = String(!!opts.normal);
|
|
50
|
+
return `${type}|asset:${asset}|url:${url}|normal:${normal}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createProvider(type: TerrainType, opts: ProviderOpts): Promise<TerrainProvider> {
|
|
54
|
+
switch (type) {
|
|
55
|
+
case "cesium":
|
|
56
|
+
return CesiumTerrainProvider.fromUrl(
|
|
57
|
+
IonResource.fromAssetId(1, { accessToken: opts.ionAccessToken }),
|
|
58
|
+
{ requestVertexNormals: !!opts.normal, requestWaterMask: false },
|
|
59
|
+
) as Promise<TerrainProvider>;
|
|
60
|
+
|
|
61
|
+
case "arcgis":
|
|
62
|
+
return ArcGISTiledElevationTerrainProvider.fromUrl(
|
|
63
|
+
"https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
|
|
64
|
+
) as Promise<TerrainProvider>;
|
|
65
|
+
|
|
66
|
+
case "cesiumion":
|
|
67
|
+
if (!opts.ionAsset && !opts.ionUrl) {
|
|
68
|
+
// Fallback to ellipsoid when misconfigured
|
|
69
|
+
return Promise.resolve(new EllipsoidTerrainProvider());
|
|
70
|
+
}
|
|
71
|
+
return CesiumTerrainProvider.fromUrl(
|
|
72
|
+
opts.ionUrl ??
|
|
73
|
+
IonResource.fromAssetId(parseInt(String(opts.ionAsset), 10), {
|
|
74
|
+
accessToken: opts.ionAccessToken,
|
|
75
|
+
}),
|
|
76
|
+
{ requestVertexNormals: !!opts.normal },
|
|
77
|
+
) as Promise<TerrainProvider>;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -63,6 +63,12 @@ function makeGlobeShadersDirty(globe: Globe): void {
|
|
|
63
63
|
// reset surface shader source to the initial state (assuming that we never
|
|
64
64
|
// use custom material on globe).
|
|
65
65
|
// ref: https://github.com/CesiumGS/cesium/blob/1.106/packages/engine/Source/Scene/Globe.js#L562-L572
|
|
66
|
+
|
|
67
|
+
// Safety check: ensure globe's internal properties exist before manipulation
|
|
68
|
+
if (!globe || globe.isDestroyed() || !(globe as any)._surface) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
66
72
|
const material = globe.material;
|
|
67
73
|
if (material == null) {
|
|
68
74
|
globe.material = Material.fromType("Color");
|
|
@@ -73,6 +79,18 @@ function makeGlobeShadersDirty(globe: Globe): void {
|
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
|
|
82
|
+
function withUniforms(globe: PrivateCesiumGlobe, add?: Record<string, () => any>) {
|
|
83
|
+
if (!globe._surface?._tileProvider) return;
|
|
84
|
+
const mm = (globe._surface._tileProvider as any)?.materialUniformMap ?? {};
|
|
85
|
+
(globe._surface._tileProvider as any).materialUniformMap = { ...mm, ...(add ?? {}) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function removeUniforms(globe: PrivateCesiumGlobe, keys: string[]) {
|
|
89
|
+
const tp = globe._surface?._tileProvider as any;
|
|
90
|
+
if (!tp?.materialUniformMap) return;
|
|
91
|
+
for (const k of keys) delete tp.materialUniformMap[k];
|
|
92
|
+
}
|
|
93
|
+
|
|
76
94
|
const useIBL = ({
|
|
77
95
|
sphericalHarmonicCoefficients,
|
|
78
96
|
globeImageBasedLighting,
|
|
@@ -184,6 +202,18 @@ const useTerrainHeatmap = ({
|
|
|
184
202
|
return { isCustomHeatmapEnabled, shaderForTerrainHeatmap };
|
|
185
203
|
};
|
|
186
204
|
|
|
205
|
+
async function waitTerrainReady(scene: any) {
|
|
206
|
+
const t = scene.terrain;
|
|
207
|
+
if (t?.ready) return;
|
|
208
|
+
await new Promise<void>(resolve => {
|
|
209
|
+
if (!t) return resolve();
|
|
210
|
+
const off = t.readyEvent.addEventListener(() => {
|
|
211
|
+
off();
|
|
212
|
+
resolve();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
187
217
|
export const useOverrideGlobeShader = ({
|
|
188
218
|
cesium,
|
|
189
219
|
sphericalHarmonicCoefficients,
|
|
@@ -201,6 +231,8 @@ export const useOverrideGlobeShader = ({
|
|
|
201
231
|
enableLighting?: boolean;
|
|
202
232
|
terrain: TerrainProperty | undefined;
|
|
203
233
|
}) => {
|
|
234
|
+
const applyingRef = useRef(false);
|
|
235
|
+
|
|
204
236
|
const { uniformMapForIBL, isIBLEnabled, shaderForIBL } = useIBL({
|
|
205
237
|
sphericalHarmonicCoefficients,
|
|
206
238
|
globeImageBasedLighting,
|
|
@@ -223,75 +255,85 @@ export const useOverrideGlobeShader = ({
|
|
|
223
255
|
|
|
224
256
|
const needUpdateGlobeRef = useRef(false);
|
|
225
257
|
|
|
226
|
-
const handleGlobeShader = useCallback(() => {
|
|
258
|
+
const handleGlobeShader = useCallback(async () => {
|
|
227
259
|
// NOTE: Support the spherical harmonic coefficient only when the terrain normal is enabled.
|
|
228
260
|
// Because it's difficult to control the shader for the entire globe.
|
|
229
261
|
// ref: https://github.com/CesiumGS/cesium/blob/af4e2bebbef25259f049b05822adf2958fce11ff/packages/engine/Source/Shaders/GlobeFS.glsl#L408
|
|
230
262
|
if (!cesium.current?.cesiumElement || !needUpdateGlobeRef.current) return;
|
|
231
263
|
|
|
232
|
-
|
|
264
|
+
if (applyingRef.current) return;
|
|
265
|
+
applyingRef.current = true;
|
|
233
266
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
throw new Error("`globe._surfaceShaderSet` could not found");
|
|
238
|
-
}
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
267
|
+
try {
|
|
268
|
+
const { scene } = cesium.current.cesiumElement;
|
|
269
|
+
await waitTerrainReady(scene);
|
|
241
270
|
|
|
242
|
-
|
|
271
|
+
const globe = cesium.current.cesiumElement.scene.globe as PrivateCesiumGlobe;
|
|
243
272
|
|
|
244
|
-
|
|
273
|
+
// Reset shaders first so we patch the freshest base
|
|
274
|
+
makeGlobeShadersDirty(globe);
|
|
245
275
|
|
|
246
|
-
|
|
247
|
-
if (
|
|
248
|
-
|
|
276
|
+
const surfaceShaderSet = globe._surfaceShaderSet;
|
|
277
|
+
if (!surfaceShaderSet) {
|
|
278
|
+
if (import.meta.env.DEV) {
|
|
279
|
+
throw new Error("`globe._surfaceShaderSet` could not found");
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
249
282
|
}
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
283
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
matchers.push(shaderForIBL);
|
|
257
|
-
shaders.push(IBLFS);
|
|
258
|
-
}
|
|
284
|
+
const baseFragmentShaderSource = surfaceShaderSet.baseFragmentShaderSource;
|
|
285
|
+
const GlobeFS =
|
|
286
|
+
baseFragmentShaderSource?.sources[baseFragmentShaderSource.sources.length - 1];
|
|
259
287
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
288
|
+
if (!GlobeFS || !baseFragmentShaderSource) {
|
|
289
|
+
if (import.meta.env.DEV) {
|
|
290
|
+
throw new Error("GlobeFS could not find.");
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
266
294
|
|
|
267
|
-
|
|
268
|
-
|
|
295
|
+
const matchers: StringMatcher[] = [];
|
|
296
|
+
const shaders: string[] = [];
|
|
297
|
+
const terrainHasNormals = !!(globe.terrainProvider as any)?.hasVertexNormals;
|
|
269
298
|
|
|
270
|
-
|
|
299
|
+
if (isIBLEnabled && globe.enableLighting && terrainHasNormals) {
|
|
300
|
+
matchers.push(shaderForIBL);
|
|
301
|
+
shaders.push(IBLFS);
|
|
302
|
+
}
|
|
271
303
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
304
|
+
if (isCustomHeatmapEnabled && terrainHasNormals) {
|
|
305
|
+
// This will log the variables needed in the shader below.
|
|
306
|
+
// we need the minHeight, maxHeight and logarithmic
|
|
307
|
+
matchers.push(shaderForTerrainHeatmap);
|
|
308
|
+
shaders.push(HeatmapForTerrainFS);
|
|
275
309
|
}
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
310
|
|
|
279
|
-
|
|
311
|
+
// This means there is no overridden shader.
|
|
312
|
+
if (!matchers.length) return;
|
|
280
313
|
|
|
281
|
-
|
|
314
|
+
needUpdateGlobeRef.current = false;
|
|
282
315
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
316
|
+
if (!globe?._surface?._tileProvider) {
|
|
317
|
+
if (import.meta.env.DEV) {
|
|
318
|
+
throw new Error("`globe._surface._tileProvider.materialUniformMap` could not found");
|
|
319
|
+
}
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
287
322
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
323
|
+
const replacedGlobeFS = defaultMatcher.concat(...matchers).execute(GlobeFS);
|
|
324
|
+
|
|
325
|
+
withUniforms(globe, isIBLEnabled ? uniformMapForIBL : undefined);
|
|
326
|
+
|
|
327
|
+
surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
|
|
328
|
+
sources: [
|
|
329
|
+
...baseFragmentShaderSource.sources.slice(0, -1),
|
|
330
|
+
GlobeFSDefinitions + replacedGlobeFS + shaders.join(""),
|
|
331
|
+
],
|
|
332
|
+
defines: baseFragmentShaderSource.defines,
|
|
333
|
+
});
|
|
334
|
+
} finally {
|
|
335
|
+
applyingRef.current = false;
|
|
336
|
+
}
|
|
295
337
|
}, [
|
|
296
338
|
cesium,
|
|
297
339
|
isCustomHeatmapEnabled,
|
|
@@ -317,6 +359,10 @@ export const useOverrideGlobeShader = ({
|
|
|
317
359
|
|
|
318
360
|
return () => {
|
|
319
361
|
if (!globe.isDestroyed()) {
|
|
362
|
+
removeUniforms(globe, [
|
|
363
|
+
"u_reearth_sphericalHarmonicCoefficients",
|
|
364
|
+
"u_reearth_globeImageBasedLighting",
|
|
365
|
+
]);
|
|
320
366
|
// Reset customized shader to default
|
|
321
367
|
makeGlobeShadersDirty(globe);
|
|
322
368
|
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ArcGISTiledElevationTerrainProvider,
|
|
3
|
-
CesiumTerrainProvider,
|
|
4
|
-
EllipsoidTerrainProvider,
|
|
5
|
-
IonResource,
|
|
6
|
-
TerrainProvider,
|
|
7
|
-
} from "cesium";
|
|
8
|
-
import { useEffect, useMemo } from "react";
|
|
9
|
-
import { Globe as CesiumGlobe } from "resium";
|
|
10
|
-
|
|
11
|
-
import type { ViewerProperty, TerrainProperty } from "../..";
|
|
12
|
-
import { AssetsCesiumProperty } from "../../../Map";
|
|
13
|
-
import { toColor } from "../common";
|
|
14
|
-
|
|
15
|
-
export type Props = {
|
|
16
|
-
property?: ViewerProperty;
|
|
17
|
-
cesiumIonAccessToken?: string;
|
|
18
|
-
onTerrainProviderChange?: () => void;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default function Globe({
|
|
22
|
-
property,
|
|
23
|
-
cesiumIonAccessToken,
|
|
24
|
-
onTerrainProviderChange,
|
|
25
|
-
}: Props): JSX.Element | null {
|
|
26
|
-
const terrainProperty = useMemo(
|
|
27
|
-
(): TerrainProperty => ({
|
|
28
|
-
...property?.terrain,
|
|
29
|
-
}),
|
|
30
|
-
[property?.terrain],
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
const terrainProvider = useMemo((): Promise<TerrainProvider> | TerrainProvider | undefined => {
|
|
34
|
-
const opts = {
|
|
35
|
-
terrain: terrainProperty?.enabled,
|
|
36
|
-
terrainType: terrainProperty?.type,
|
|
37
|
-
normal: terrainProperty?.normal,
|
|
38
|
-
ionAccessToken: property?.assets?.cesium?.terrain?.ionAccessToken || cesiumIonAccessToken,
|
|
39
|
-
ionAsset: property?.assets?.cesium?.terrain?.ionAsset,
|
|
40
|
-
ionUrl: property?.assets?.cesium?.terrain?.ionUrl,
|
|
41
|
-
};
|
|
42
|
-
const provider = opts.terrain ? terrainProviders[opts.terrainType || "cesium"] : undefined;
|
|
43
|
-
return (typeof provider === "function" ? provider(opts) : provider) ?? defaultTerrainProvider;
|
|
44
|
-
}, [
|
|
45
|
-
terrainProperty?.enabled,
|
|
46
|
-
terrainProperty?.type,
|
|
47
|
-
terrainProperty?.normal,
|
|
48
|
-
property?.assets?.cesium?.terrain?.ionAccessToken,
|
|
49
|
-
property?.assets?.cesium?.terrain?.ionAsset,
|
|
50
|
-
property?.assets?.cesium?.terrain?.ionUrl,
|
|
51
|
-
cesiumIonAccessToken,
|
|
52
|
-
]);
|
|
53
|
-
|
|
54
|
-
const baseColor = useMemo(
|
|
55
|
-
() => toColor(property?.globe?.baseColor),
|
|
56
|
-
[property?.globe?.baseColor],
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
onTerrainProviderChange?.();
|
|
61
|
-
}, [terrainProvider, onTerrainProviderChange]);
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<CesiumGlobe
|
|
65
|
-
baseColor={baseColor}
|
|
66
|
-
enableLighting={!!property?.globe?.enableLighting}
|
|
67
|
-
showGroundAtmosphere={property?.globe?.atmosphere?.enabled ?? true}
|
|
68
|
-
atmosphereLightIntensity={property?.globe?.atmosphere?.lightIntensity}
|
|
69
|
-
atmosphereSaturationShift={property?.globe?.atmosphere?.saturationShift}
|
|
70
|
-
atmosphereHueShift={property?.globe?.atmosphere?.hueShift}
|
|
71
|
-
atmosphereBrightnessShift={property?.globe?.atmosphere?.brightnessShift}
|
|
72
|
-
terrainProvider={terrainProvider}
|
|
73
|
-
depthTestAgainstTerrain={!!property?.globe?.depthTestAgainstTerrain}
|
|
74
|
-
/>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const defaultTerrainProvider = new EllipsoidTerrainProvider();
|
|
79
|
-
|
|
80
|
-
const terrainProviders: {
|
|
81
|
-
[k in NonNullable<TerrainProperty["type"]>]:
|
|
82
|
-
| TerrainProvider
|
|
83
|
-
| ((
|
|
84
|
-
opts: Pick<TerrainProperty, "normal"> & AssetsCesiumProperty["terrain"],
|
|
85
|
-
) => Promise<TerrainProvider> | TerrainProvider | null);
|
|
86
|
-
} = {
|
|
87
|
-
cesium: ({ ionAccessToken, normal }) =>
|
|
88
|
-
CesiumTerrainProvider.fromUrl(
|
|
89
|
-
IonResource.fromAssetId(1, {
|
|
90
|
-
accessToken: ionAccessToken,
|
|
91
|
-
}),
|
|
92
|
-
{
|
|
93
|
-
requestVertexNormals: normal,
|
|
94
|
-
requestWaterMask: false,
|
|
95
|
-
},
|
|
96
|
-
),
|
|
97
|
-
arcgis: () =>
|
|
98
|
-
ArcGISTiledElevationTerrainProvider.fromUrl(
|
|
99
|
-
"https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
|
|
100
|
-
),
|
|
101
|
-
cesiumion: ({ ionAccessToken, ionAsset, ionUrl, normal }) =>
|
|
102
|
-
ionAsset
|
|
103
|
-
? CesiumTerrainProvider.fromUrl(
|
|
104
|
-
ionUrl ||
|
|
105
|
-
IonResource.fromAssetId(parseInt(ionAsset, 10), {
|
|
106
|
-
accessToken: ionAccessToken,
|
|
107
|
-
}),
|
|
108
|
-
{
|
|
109
|
-
requestVertexNormals: normal,
|
|
110
|
-
},
|
|
111
|
-
)
|
|
112
|
-
: null,
|
|
113
|
-
};
|