@yangyongtao/gaea 1.0.0

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/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # gaea-3dview
2
+
3
+ Gaea 3D 可视化组件库,包含天气控制、沿线飞行、台风标注、点标注等功能。
4
+
5
+ ## 前置条件
6
+
7
+ 使用前需在 HTML 中加载 Gaea 运行库:
8
+
9
+ ```html
10
+ <script src="path/to/Gaea.js"></script>
11
+ ```
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ npm install @your-scope/gaea-3dview
17
+ ```
18
+
19
+ ## 快速开始
20
+
21
+ ```js
22
+ import { GaeaApp, setConfig } from '@your-scope/gaea-3dview';
23
+
24
+ // 1. 配置全局参数(初始化前调用)
25
+ setConfig({
26
+ serveLocal: 'http://localhost:9012', // 静态资源地址
27
+ serveUrl: 'http://192.168.1.1:8088/gaeaExplorerServer/',
28
+ });
29
+
30
+ // 2. 创建实例
31
+ const app = new GaeaApp();
32
+
33
+ // 3. 初始化基础环境
34
+ app.initDefaultEnvironment();
35
+
36
+ // 4. 在指定坐标放置图标
37
+ await app.drawPointIcon({ lat: 27.3809, lng: 120.4499 });
38
+
39
+ // 5. 沿线飞行
40
+ await app.flyAlongPath([
41
+ { x: 27.22, y: 120.45 },
42
+ { x: 27.35, y: 120.55 },
43
+ ]);
44
+ ```
45
+
46
+ ## API 文档
47
+
48
+ ### GaeaApp 类
49
+
50
+ #### `setConfig(config)`
51
+ 初始化前设置全局配置,必传 `serveLocal`。
52
+
53
+ #### `drawPointIcon(opts)` / `drawPointIcons(list)`
54
+ 在指定经纬度放置图标标记。
55
+
56
+ ```js
57
+ await app.drawPointIcon({ lat: 27.22, lng: 120.45, iconSize: 150 });
58
+ ```
59
+
60
+ #### `clearPointIcons()`
61
+ 清除所有点标注。
62
+
63
+ #### `flyAlongPath(lineData, opts)`
64
+ 沿路径飞行。
65
+
66
+ ```js
67
+ await app.flyAlongPath(
68
+ [{ x: 27.22, y: 120.45 }, { x: 27.35, y: 120.55 }],
69
+ { height: 150, duration: 20 }
70
+ );
71
+ ```
72
+
73
+ #### `stopFlyAlongPath()`
74
+ 停止飞行。
75
+
76
+ #### `enableRain(params)` / `restoreNormalWeather()`
77
+ 天气控制。
78
+
79
+ ```js
80
+ app.enableRain({ direction: 0, speed: 5, count: 30 });
81
+ app.restoreNormalWeather();
82
+ ```
83
+
84
+ #### `cameraReset()`
85
+ 重置相机到默认视角。
86
+
87
+ #### `screenToLatLng(event)`
88
+ 屏幕坐标 → 经纬度。
89
+
90
+ ## Vue 组件
91
+
92
+ ```vue
93
+ <script setup>
94
+ import { GaeaApp } from '@your-scope/gaea-3dview';
95
+ import { WeatherControl, FlyAlongRoute } from '@your-scope/gaea-3dview/components';
96
+
97
+ const app = new GaeaApp();
98
+ </script>
99
+
100
+ <template>
101
+ <WeatherControl :appInstance="app" @close="..." />
102
+ <FlyAlongRoute :appInstance="app" @close="..." @fly="..." />
103
+ </template>
104
+ ```
105
+
106
+ ## 静态资源
107
+
108
+ 发布时需要随包分发以下文件:
109
+ - `public/static/1111.res` — 点标注图标
110
+ - `public/static/SkyDay.png` — 天空纹理(可选)
111
+ - `public/static/gltf/*.gltf` — 3D 模型文件
112
+
113
+ ## License
114
+
115
+ MIT
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@yangyongtao/gaea",
3
+ "version": "1.0.0",
4
+ "description": "Gaea 3D 可视化组件库,包含天气控制、沿线飞行、台风标注、模型加载等功能",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "module": "src/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./src/index.js",
11
+ "default": "./src/index.js"
12
+ },
13
+ "./components": {
14
+ "import": "./src/components/index.js",
15
+ "default": "./src/components/index.js"
16
+ },
17
+ "./methods": {
18
+ "import": "./src/GaeaMethods.js",
19
+ "default": "./src/GaeaMethods.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "src/",
24
+ "public/",
25
+ "README.md"
26
+ ],
27
+ "scripts": {
28
+ "test": "echo \"No tests yet\""
29
+ },
30
+ "keywords": [
31
+ "gaea",
32
+ "3d",
33
+ "viewer",
34
+ "weather",
35
+ "typhoon",
36
+ "flight",
37
+ "vue",
38
+ "component"
39
+ ],
40
+ "peerDependencies": {
41
+ "element-plus": "^2.6.0",
42
+ "pinia": "^2.1.0",
43
+ "vue": "^3.4.0"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "element-plus": {
47
+ "optional": true
48
+ },
49
+ "pinia": {
50
+ "optional": true
51
+ }
52
+ },
53
+ "dependencies": {
54
+ "@tweenjs/tween.js": "^25.0.0"
55
+ },
56
+ "license": "MIT",
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "author": "yangyongtao",
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "https://github.com/yangyongtao/gaea.git"
64
+ }
65
+ }
Binary file
Binary file
@@ -0,0 +1,391 @@
1
+ /**
2
+ * gaea-3dview npm package
3
+ *
4
+ * 使用前必须在项目 HTML 中加载 Gaea CDN:
5
+ * <script src="path/to/Gaea.js"></script>
6
+ *
7
+ * 配置初始化:
8
+ * import { setConfig } from '@your-scope/gaea-3dview';
9
+ * setConfig({ serveUrl: '...', serveLocal: '...' });
10
+ *
11
+ * 使用 GaeaApp:
12
+ * import { GaeaApp } from '@your-scope/gaea-3dview';
13
+ * const app = new GaeaApp();
14
+ * await app.drawPointIcon({ lat: 27.22, lng: 120.45 });
15
+ */
16
+
17
+ // ==================== 基础工具函数 ====================
18
+
19
+ /** 加载资源到 Gaea */
20
+ function loadResource(url, name) {
21
+ return new Promise((res, rej) => {
22
+ if (!window.Gaea || !window.Gaea.GaeaResourceLoader) {
23
+ rej(new Error('Gaea 未初始化,请先加载 Gaea CDN'));
24
+ return;
25
+ }
26
+ window.Gaea.GaeaResourceLoader.Instance.LoadResourceFromUrl1(
27
+ url, name,
28
+ (code, msg) => {
29
+ if (code === 200) res(true);
30
+ else { console.warn(`加载资源失败: ${name}`, msg); rej(false); }
31
+ }
32
+ );
33
+ });
34
+ }
35
+
36
+ /** 获取文件名 */
37
+ function getFileName(url) {
38
+ return url.split('/').pop().split('?')[0];
39
+ }
40
+
41
+ /** RGB 字符串转 Gaea Color */
42
+ function rgbToGaeaColor(rgb) {
43
+ if (typeof rgb !== 'string') return null;
44
+ const match = rgb.match(/\((.+?)\)/);
45
+ if (!match) return null;
46
+ const arr = match[1].split(',').map(Number);
47
+ return new window.Gaea.Color(arr[0] / 255, arr[1] / 255, arr[2] / 255, 1);
48
+ }
49
+
50
+
51
+ // ==================== GaeaApp 类 ====================
52
+
53
+ import { Tween, Group, Easing } from '@tweenjs/tween.js';
54
+ import config from './config.js';
55
+
56
+ export class GaeaApp {
57
+ constructor(options = {}) {
58
+ this.config = { ...config, ...options };
59
+
60
+ // ---- 飞行相关 ----
61
+ this._flyAnimation = null;
62
+ this._flyAnimationPlayer = null;
63
+ this._flyModelInstance = null;
64
+ this._flyEntityView = null;
65
+
66
+ // ---- 点标注相关 ----
67
+ this._pointIconElements = [];
68
+ this._pointIconLoaded = false;
69
+
70
+ // ---- 天气相关 ----
71
+ this._weatherInited = false;
72
+ this._fullScreenQuad = null;
73
+ this._sky = null;
74
+ this._sun = null;
75
+ this._environment = null;
76
+ this._defaultSkyMtl = null;
77
+ this._rainSkyMtl = null;
78
+ this._rainSkyTexture = null;
79
+
80
+ // ---- Tween 动画组 ----
81
+ this._tweenGroup = new Group();
82
+ }
83
+
84
+ // ==================== 点标注 ====================
85
+
86
+ /**
87
+ * 在指定经纬度放置图标标记
88
+ * @param {Object} opts - { lat, lng, alt?, iconSize?, enablePick? }
89
+ */
90
+ async drawPointIcon(opts = {}) {
91
+ const { lat, lng, alt = 0, iconSize = 150, enablePick = true } = opts;
92
+ if (lat == null || lng == null) {
93
+ console.error('drawPointIcon: lat 和 lng 不能为空');
94
+ return null;
95
+ }
96
+
97
+ const Gaea = window.Gaea;
98
+ const resName = 'pointIcon.res';
99
+
100
+ if (!this._pointIconLoaded) {
101
+ const resUrl = `${this.config.serveLocal}/static/1111.res`;
102
+ const loaded = await loadResource(resUrl, resName);
103
+ if (!loaded) return null;
104
+ this._pointIconLoaded = true;
105
+ }
106
+
107
+ const texId = Gaea.GaeaResourceLoader.Instance.GetResource(resName);
108
+ const tex = new Gaea.Texture(false);
109
+ tex.InstanceID = texId + '';
110
+
111
+ const pos = new Gaea.Vector3(lat, lng, alt);
112
+ const shape = Gaea.Geometry.Create1(Gaea.GeometryType.Point, [pos]);
113
+
114
+ const element = new Gaea.PointGeometryElement();
115
+ element.Shape = shape;
116
+
117
+ const iconSymbol = new Gaea.IconMarkSymbol();
118
+ iconSymbol.MarkSize = iconSize;
119
+ iconSymbol.Icon = tex;
120
+ element.Symbol = iconSymbol;
121
+ element.EnablePick = enablePick;
122
+ element.EnableEdit = false;
123
+
124
+ Gaea.World.Instance.RenderableObjectList.AddLast(element);
125
+ this._pointIconElements.push(element);
126
+ return element;
127
+ }
128
+
129
+ /**
130
+ * 批量添加图标标记
131
+ * @param {Array} pointList - [{ lat, lng, alt?, iconSize? }]
132
+ */
133
+ async drawPointIcons(pointList = []) {
134
+ const elements = [];
135
+ for (const p of pointList) {
136
+ const el = await this.drawPointIcon(p);
137
+ if (el) elements.push(el);
138
+ }
139
+ return elements;
140
+ }
141
+
142
+ /** 清除所有点标注 */
143
+ clearPointIcons() {
144
+ const Gaea = window.Gaea;
145
+ this._pointIconElements.forEach(el => {
146
+ Gaea.World.Instance.RenderableObjectList.Remove(el);
147
+ });
148
+ this._pointIconElements = [];
149
+ }
150
+
151
+ // ==================== 沿线飞行 ====================
152
+
153
+ /**
154
+ * 沿路径飞行
155
+ * @param {Array} lineData - [{ x: lat, y: lng }, ...]
156
+ * @param {Object} opts - { height, step, duration, xrotation, lockEye, minZoom, maxZoom }
157
+ */
158
+ async flyAlongPath(lineData, opts = {}) {
159
+ const Gaea = window.Gaea;
160
+ const {
161
+ height = 150, step = 1000, duration = 20,
162
+ xrotation = -30, lockEye = true, minZoom = 0, maxZoom = 400
163
+ } = opts;
164
+
165
+ if (!lineData || lineData.length < 2) {
166
+ return Promise.reject(new Error('至少需要2个路径点'));
167
+ }
168
+
169
+ try {
170
+ this._cleanupFlyResources();
171
+
172
+ const bezier = new Gaea.UniformCubicSpline();
173
+ for (let i = 0; i < lineData.length; i++) {
174
+ const ctrl = Gaea.GaeaMath.SphericalToCartesianDeg(
175
+ new Gaea.Vector3(lineData[i].x, lineData[i].y, height)
176
+ );
177
+ bezier.AddValue(i, ctrl);
178
+ }
179
+ bezier.Step = step;
180
+ bezier.Interpolate(true);
181
+ bezier.CalculateLengthAfterInterpolation();
182
+
183
+ const startPos = Gaea.GaeaMath.SphericalToCartesianDeg(
184
+ new Gaea.Vector3(lineData[0].x, lineData[0].y, height)
185
+ );
186
+
187
+ const mesh = new Gaea.CubeMesh();
188
+ mesh.Size = new Gaea.Vector3(1, 1, 1);
189
+ const mtl = new Gaea.SpatialMaterial();
190
+ mtl.AlbedoColor = new Gaea.Color(1, 0, 0, 1);
191
+ mesh.SurfaceSetMaterial(0, mtl);
192
+
193
+ this._flyModelInstance = new Gaea.MeshInstance();
194
+ this._flyModelInstance.Name = 'flyModel';
195
+ this._flyModelInstance.Visible = false;
196
+ this._flyModelInstance.Translation = startPos;
197
+ Gaea.World.Instance.AddChild(this._flyModelInstance, false);
198
+
199
+ this._flyEntityView = new Gaea.EntityView();
200
+ this._flyEntityView.Offset3D = new Gaea.Vector3(
201
+ -1, 0, Math.tan(xrotation / 180 * Math.PI)
202
+ );
203
+ this._flyEntityView.LockEye = lockEye;
204
+ this._flyEntityView.MinMaxZoom3D = new Gaea.Vector2(minZoom, maxZoom);
205
+ this._flyEntityView.Init(this._flyModelInstance);
206
+ Gaea.World.Instance.CameraController.LockedEntity = this._flyEntityView;
207
+
208
+ this._flyAnimation = bezier.ToAnimationByTime(
209
+ 'flyAnim', true, duration, new Gaea.Vector3(1, 1, 1)
210
+ );
211
+ this._flyAnimation.TrackSetPath(0, '/root/World/flyModel');
212
+
213
+ this._flyAnimationPlayer = new Gaea.GaeaAnimationPlayer();
214
+ this._flyAnimationPlayer.AddAnimation('flyAnim', this._flyAnimation);
215
+ Gaea.World.Instance.AddChild(this._flyAnimationPlayer, false);
216
+
217
+ return new Promise((resolve) => {
218
+ this._flyAnimationPlayer.PlayWithCallback(
219
+ 'flyAnim', Gaea.CallbackType.AnimatioFinished, () => resolve()
220
+ );
221
+ });
222
+ } catch (e) {
223
+ this._cleanupFlyResources();
224
+ return Promise.reject(e);
225
+ }
226
+ }
227
+
228
+ /** 停止飞行 */
229
+ stopFlyAlongPath() {
230
+ try {
231
+ if (this._flyAnimationPlayer) {
232
+ this._flyAnimationPlayer.Stop('flyAnim');
233
+ }
234
+ } catch (e) { /* ignore */ }
235
+ this._cleanupFlyResources();
236
+ }
237
+
238
+ _cleanupFlyResources() {
239
+ const Gaea = window.Gaea;
240
+ try {
241
+ if (this._flyAnimationPlayer) {
242
+ Gaea.World.Instance.RemoveChild(this._flyAnimationPlayer);
243
+ this._flyAnimationPlayer = null;
244
+ }
245
+ this._flyEntityView = null;
246
+ if (this._flyModelInstance) {
247
+ Gaea.World.Instance.RemoveChild(this._flyModelInstance);
248
+ this._flyModelInstance = null;
249
+ }
250
+ Gaea.World.Instance.CameraController.LockedEntity = null;
251
+ this._flyAnimation = null;
252
+ } catch (e) { /* ignore */ }
253
+ }
254
+
255
+ /** 重置相机视角 */
256
+ cameraReset() {
257
+ const Gaea = window.Gaea;
258
+ const camera = Gaea.World.Instance.DefaultCamera;
259
+ camera && camera.Reset();
260
+ }
261
+
262
+ // ==================== 天气控制 ====================
263
+
264
+ _initWeather() {
265
+ if (this._weatherInited) return;
266
+ const Gaea = window.Gaea;
267
+ this._fullScreenQuad = Gaea.World.Instance.FullScreenQuad;
268
+ this._sky = Gaea.MeshInstance.ConvertBy(Gaea.World.Instance.GetNode('Sky'));
269
+ this._sun = Gaea.Sun.ConvertBy(Gaea.World.Instance.GetNode('Sun'));
270
+ this._environment = Gaea.Environment.ConvertBy(
271
+ Gaea.GaeaResourceLoader.Instance.LoadLoaclResource('res://default_env.tres')
272
+ );
273
+ this._defaultSkyMtl = this._sky.MaterialOverride;
274
+ this._weatherInited = true;
275
+ }
276
+
277
+ /** 开启下雨 */
278
+ enableRain(params = {}) {
279
+ this._initWeather();
280
+ const Gaea = window.Gaea;
281
+ const fsq = this._fullScreenQuad;
282
+ if (!fsq) return;
283
+
284
+ const {
285
+ direction = 0, speed = 5, count = 30
286
+ } = params;
287
+
288
+ fsq.Visible = true;
289
+ fsq.NeedAtmosphere = true;
290
+
291
+ this._sun.TimeString = '2022-09-15 00:00:00';
292
+ this._sun.LightColor = new Gaea.Color(1, 1, 1, 1);
293
+ this._sun.LightEnergy = 0.05;
294
+ this._environment.BackgroundEnergy = 0.11;
295
+ this._environment.GlowBlendMode = 1;
296
+
297
+ fsq.RainyCount = count;
298
+ fsq.Speed = speed;
299
+ fsq.WindDirection = direction;
300
+ fsq.Rainy = true;
301
+ }
302
+
303
+ /** 更新雨参数 */
304
+ updateRain(params = {}) {
305
+ const Gaea = window.Gaea;
306
+ const fsq = this._fullScreenQuad;
307
+ if (!fsq) return;
308
+ if (params.direction !== undefined) fsq.WindDirection = params.direction;
309
+ if (params.speed !== undefined) fsq.Speed = params.speed;
310
+ if (params.count !== undefined) fsq.RainyCount = params.count;
311
+ }
312
+
313
+ /** 恢复正常天气 */
314
+ restoreNormalWeather() {
315
+ this._initWeather();
316
+ const Gaea = window.Gaea;
317
+ const fsq = this._fullScreenQuad;
318
+ if (!fsq) return;
319
+
320
+ fsq.Rainy = false;
321
+ fsq.Visible = false;
322
+ fsq.NeedAtmosphere = false;
323
+
324
+ this._sun.TimeString = '2022-09-15 12:00:00';
325
+ this._sun.LightColor = new Gaea.Color(1, 226 / 255, 164 / 255, 1);
326
+ this._sun.LightEnergy = 3.5;
327
+ this._environment.BackgroundEnergy = 0.2;
328
+ this._environment.GlowBlendMode = 2;
329
+
330
+ if (this._defaultSkyMtl && this._sky) {
331
+ this._sky.MaterialOverride = this._defaultSkyMtl;
332
+ }
333
+ }
334
+
335
+ /** 初始化基础环境(关闭默认雾、设置默认光照等) */
336
+ initDefaultEnvironment() {
337
+ const Gaea = window.Gaea;
338
+
339
+ // 关闭默认雾
340
+ const fogEnv = Gaea.Environment.ConvertBy(
341
+ Gaea.GaeaResourceLoader.Instance.LoadLoaclResource('res://default_env.tres')
342
+ );
343
+ fogEnv.FogEnabled = false;
344
+
345
+ // 基础环境光
346
+ const env = Gaea.Environment.ConvertBy(
347
+ Gaea.GaeaResourceLoader.Instance.LoadLoaclResource('res://default_env.tres')
348
+ );
349
+ env.BackgroundEnergy = 0.6;
350
+
351
+ const sun = Gaea.Sun.ConvertBy(Gaea.World.Instance.GetNode('Sun'));
352
+ sun.Power = 1.5;
353
+
354
+ Gaea.World.Instance.ForceCloudVisalbe = true;
355
+ Gaea.World.Instance.CloudVisalbe = false;
356
+
357
+ window.Gaea = Gaea;
358
+ }
359
+
360
+ /** 设置静止资源 Base URL */
361
+ setAssetHost(assetHost) {
362
+ const Gaea = window.Gaea;
363
+ const cfg = `[Engine]\nDefaultGlobalOverview="Global2.jpg"\nAssetHost="${assetHost}"`;
364
+ Gaea.World.Instance.EngineConfigString = cfg;
365
+ }
366
+
367
+ // ==================== 工具方法 ====================
368
+
369
+ /** 屏幕坐标 → 经纬度 */
370
+ screenToLatLng(event) {
371
+ const Gaea = window.Gaea;
372
+ if (!Gaea || !Gaea.GaeaMath) return null;
373
+ const result = Gaea.GaeaMath.ProjectPosition(
374
+ new Gaea.Vector2(event.offsetX, event.offsetY)
375
+ );
376
+ const lonlat = Gaea.GaeaMath.CartesianToSphericalDeg(result);
377
+ return { lat: lonlat.x, lng: lonlat.y, alt: lonlat.z };
378
+ }
379
+
380
+ /** Vector3 快捷创建 */
381
+ vec3(x = 0, y = 0, z = 0) {
382
+ return new window.Gaea.Vector3(x, y, z);
383
+ }
384
+
385
+ /** Color 快捷创建 (0-1) */
386
+ color(r, g, b, a = 1) {
387
+ return new window.Gaea.Color(r, g, b, a);
388
+ }
389
+ }
390
+
391
+ export default GaeaApp;