完善项目文档和SDK功能

- 更新 README 文档和许可证文件
- 完善编辑器使用手册,包括BIM浏览、CAD查看等功能说明
- 更新文档配置和首页内容
- 优化编辑器环境配置
- 完善 WebSocket 通信和 API 接口
- 增强 glTF 处理器和点云重建功能
- 优化 SDK 核心模块:
  - 更新 App 和 Config 配置
  - 添加 Billboard、HtmlPanel 等对象支持
  - 完善粒子系统和天气效果
  - 优化相机管理和漫游工具
  - 改进材质着色器效果
- 更新服务层 README 文档

Made-with: Cursor
This commit is contained in:
lyf
2026-03-14 00:26:51 +08:00
parent 8402f1bc2d
commit cda4999325
70 changed files with 1510 additions and 1455 deletions

View File

@@ -36,7 +36,7 @@
- 暗示与官方项目的关联性 - 暗示与官方项目的关联性
## ©️ 版权声明 ## ©️ 版权声明
2019-2025 © ErSan<mlt131220@163.com>. All rights reserved. Copyright © 2026 . All rights reserved.

View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright 2024 ErSan Copyright 2026
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,65 +1,105 @@
# NeonVision 3D Editor # NeonVision 3D Editor
[简体中文](README.md) | English [简体中文](README.md) | English
## Core Features ## Overview
- Multi-format support: 30+ model formats (GLTF/OBJ/FBX/GLB/RVT/IFC, etc.) NeonVision 3D Editor (Art Museum Digital Cockpit) is a professional Web-based 3D scene editor built on WebGL/Three.js technology. It supports multi-format 3D model import, scene editing, animation production, BIM/CAD parsing and other professional features.
- BIM Model Lightweight Display (RVT/IFC)
- CAD Drawing Analysis (DWG/DXF) ## Core Features
- Scene Subpackage Storage and Loading
- Animation Editor - **Multi-format Support**: Supports 30+ 3D model formats including GLTF, GLB, OBJ, FBX, RVT, IFC
- **BIM Model Lightweight**: Lightweight display of Revit (RVT) and IFC format models
## Extended Capabilities - **CAD Drawing Analysis**: DWG/DXF format 2D drawing parsing with 3D linkage
- **Scene Subpackage Storage**: Scene subpackage packaging and incremental loading for large-scale scenes
- Plugin System - **Animation Editor**: Keyframe animation, timeline editing, skeletal animation support
- Script Runtime - **Real-time Preview**: Instant rendering preview, WYSIWYG
- Particle System
- Weather System ## Extended Capabilities
- Cloud Storage Integration
- Resource Center - **Plugin System**: Plugin extension support for customization
- **Script Runtime**: JavaScript script injection for business logic
## Technology Stack - **Particle System**: Rich particle effect editor
- **Weather System**: Fog, rain, snow and other weather effects
- Vue 3.5+ - **Cloud Storage Integration**: Local storage and UpYun object storage support
- Three.js r176+ - **Asset Center**: Unified asset management for models, materials, textures, etc.
- Cesium 1.107+
- Naive UI 2.43+ ## Technology Stack
- UnoCSS 0.46+
| Category | Technology |
## Quick Start |----------|------------|
| Frontend Framework | Vue 3.5+ / Vue Router / Pinia |
### Prerequisites | 3D Engine | Three.js r176+ |
| GIS Engine | Cesium 1.107+ |
- Node.js ≥ 20.x | UI Components | Naive UI |
- PNPM | CSS Framework | UnoCSS / Tailwind CSS |
| Build Tool | Vite |
### Local Development | Editor | Monaco Editor |
```bash ## Project Structure
# Clone the project
git clone <your-repo-url> ```
Astral3D/
# Install dependencies ├── packages/
pnpm install │ ├── editor/ # Web editor
│ ├── sdk/ # 3D engine SDK
# Build SDK │ ├── examples/ # Example projects
pnpm run sdk:build │ └── docs/ # Documentation site
├── common/ # Shared build config
# Start dev server └── astral-service/ # Backend service (Java/SpringBoot)
pnpm run editor:dev ```
```
## Quick Start
### Production Build
### Prerequisites
```bash
pnpm run editor:build - Node.js ≥ 23.0.0
``` - PNPM ≥ 10.8.1
- Java 8+ (for backend)
## License - MySQL 5.7+ (for backend)
Open source under Apache 2.0 License. ### Local Development
--- ```bash
# Clone the project
*More content to be added* git clone http://code-server:3000/lyf/Astral3D.git
cd Astral3D
# Install dependencies
pnpm install
# Build SDK
pnpm run sdk:build
# Start dev server
pnpm run editor:dev
```
Visit http://localhost:5173
### Production Build
```bash
pnpm run editor:build
```
### Backend Service (Optional)
```bash
# Enter backend directory
cd ../astral-service
# Configure database
# Edit astral-web/src/main/resources/application-dev.yml
# Start service
mvn spring-boot:run
```
## License
Open source under Apache 2.0 License.
## Contributing
Issues and Pull Requests are welcome.

View File

@@ -1,65 +1,105 @@
# NeonVision 3D Editor # NeonVision 3D Editor
简体中文 | [English](README.en.md) 简体中文 | [English](README.en.md)
## 核心功能 ## 概述
- 多格式支持30+ 模型格式GLTF/OBJ/FBX/GLB/RVT/IFC等 NeonVision 3D Editor美术馆数字驾驶舱是一款专业的 Web 端三维场景编辑器,基于 WebGL/Three.js 技术构建支持多格式三维模型导入、场景编辑、动画制作、BIM/CAD 解析等专业功能。
- BIM模型轻量化展示RVT/IFC
- CAD图纸解析DWG/DXF ## 核心功能
- 场景分包存储与加载
- 动画编辑器 - **多格式支持**:支持 GLTF、GLB、OBJ、FBX、RVT、IFC 等 30+ 三维模型格式
- **BIM 模型轻量化**:支持 Revit(RVT) 和 IFC 格式的轻量化展示
## 扩展能力 - **CAD 图纸解析**:支持 DWG/DXF 格式的二维图纸解析与三维联动
- **场景分包存储**:支持场景分包打包、增量加载,提升大规模场景性能
- 插件系统 - **动画编辑器**:支持关键帧动画、时间轴编辑、骨骼动画
- 脚本运行时 - **实时预览**:即时渲染预览,所见即所得
- 粒子系统
- 天气系统 ## 扩展能力
- 云存储集成
- 资源中心 - **插件系统**:支持插件扩展,满足定制化需求
- **脚本运行时**:支持 JavaScript 脚本注入,实现业务逻辑
## 技术栈 - **粒子系统**:丰富的粒子效果编辑器
- **天气系统**:雾、雨、雪等天气效果模拟
- Vue 3.5+ - **云存储集成**:支持本地存储和又拍云对象存储
- Three.js r176+ - **资源中心**:统一资产管理,支持模型、材质、纹理等
- Cesium 1.107+
- Naive UI 2.43+ ## 技术栈
- UnoCSS 0.46+
| 类别 | 技术 |
## 快速开始 |------|------|
| 前端框架 | Vue 3.5+ / Vue Router / Pinia |
### 前置需求 | 3D 引擎 | Three.js r176+ |
| GIS 引擎 | Cesium 1.107+ |
- Node.js ≥ 20.x | UI 组件库 | Naive UI |
- PNPM | CSS 框架 | UnoCSS / Tailwind CSS |
| 构建工具 | Vite |
### 本地运行 | 编辑器 | Monaco Editor |
```bash ## 项目结构
# 克隆项目
git clone <your-repo-url> ```
Astral3D/
# 安装依赖 ├── packages/
pnpm install │ ├── editor/ # Web 编辑器
│ ├── sdk/ # 3D 引擎 SDK
# 构建 SDK │ ├── examples/ # 示例项目
pnpm run sdk:build │ └── docs/ # 文档站点
├── common/ # 公共构建配置
# 启动开发服务器 └── astral-service/ # 后端服务 (Java/SpringBoot)
pnpm run editor:dev ```
```
## 快速开始
### 生产构建
### 前置需求
```bash
pnpm run editor:build - Node.js ≥ 23.0.0
``` - PNPM ≥ 10.8.1
- Java 8+ (后端服务)
## 许可协议 - MySQL 5.7+ (后端服务)
基于 Apache 2.0 许可证开源。 ### 本地运行
--- ```bash
# 克隆项目
*更多内容待补充* git clone http://code-server:3000/lyf/Astral3D.git
cd Astral3D
# 安装依赖
pnpm install
# 构建 SDK
pnpm run sdk:build
# 启动开发服务器
pnpm run editor:dev
```
访问 http://localhost:5173
### 生产构建
```bash
pnpm run editor:build
```
### 后端服务(可选)
```bash
# 进入后端目录
cd ../astral-service
# 配置数据库
# 修改 astral-web/src/main/resources/application-dev.yml
# 启动服务
mvn spring-boot:run
```
## 许可协议
基于 Apache 2.0 许可证开源。
## 贡献指南
欢迎提交 Issue 和 Pull Request。

View File

@@ -1,22 +1,22 @@
{ {
"name": "@astral3d/build-vite-plugins", "name": "@astral3d/build-vite-plugins",
"author": "ErSan<mlt131220@163.com>", "author": "",
"private": true, "private": true,
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"main": "./src/index.ts", "main": "./src/index.ts",
"dependencies": { "dependencies": {
}, },
"devDependencies": { "devDependencies": {
"@types/fs-extra": "^11.0.3", "@types/fs-extra": "^11.0.3",
"dotenv": "16.3.1", "dotenv": "16.3.1",
"fs-extra": "^11.1.1", "fs-extra": "^11.1.1",
"rollup-plugin-visualizer": "catalog:", "rollup-plugin-visualizer": "catalog:",
"typescript": "catalog:", "typescript": "catalog:",
"unplugin-config": "^0.1.5", "unplugin-config": "^0.1.5",
"vite": "catalog:", "vite": "catalog:",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-enhance-log": "^0.6.2", "vite-plugin-enhance-log": "^0.6.2",
"vite-plugin-top-level-await": "^1.3.1" "vite-plugin-top-level-await": "^1.3.1"
} }
} }

View File

@@ -2,7 +2,7 @@
"name": "astral3d", "name": "astral3d",
"version": "1.0.0", "version": "1.0.0",
"description": "Astral3D Engine", "description": "Astral3D Engine",
"author": "ErSan <mlt131220@163.com>", "author": "",
"scripts": { "scripts": {
"docs:dev": "pnpm run -C packages/docs docs:dev", "docs:dev": "pnpm run -C packages/docs docs:dev",
"docs:dev-clean": "pnpm run -C packages/docs docs:dev-clean", "docs:dev-clean": "pnpm run -C packages/docs docs:dev-clean",

View File

@@ -1,24 +1,24 @@
{ {
"name": "@astral3d/docs", "name": "@astral3d/docs",
"type": "module", "type": "module",
"version": "1.0.0", "version": "1.0.0",
"description": "Astral3D 文档", "description": "Astral3D 文档",
"packageManager": "pnpm@10.8.1", "packageManager": "pnpm@10.8.1",
"author": "ErSan <mlt131220@163.com>", "author": "",
"scripts": { "scripts": {
"docs:dev": "vuepress dev src", "docs:dev": "vuepress dev src",
"docs:dev-clean": "vuepress dev src --clean-cache --clean-temp", "docs:dev-clean": "vuepress dev src --clean-cache --clean-temp",
"docs:build": "vuepress build src --clean-cache --clean-temp", "docs:build": "vuepress build src --clean-cache --clean-temp",
"docs:preview": "http-server src/.vuepress/dist", "docs:preview": "http-server src/.vuepress/dist",
"vp-update": "pnpm dlx vp-update" "vp-update": "pnpm dlx vp-update"
}, },
"devDependencies": { "devDependencies": {
"@vuepress/bundler-vite": "^2.0.0-rc.24", "@vuepress/bundler-vite": "^2.0.0-rc.24",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"sass-embedded": "^1.85.0", "sass-embedded": "^1.85.0",
"typescript": "catalog:", "typescript": "catalog:",
"vue": "catalog:", "vue": "catalog:",
"vuepress": "^2.0.0-rc.24", "vuepress": "^2.0.0-rc.24",
"vuepress-theme-plume": "^1.0.0-rc.164" "vuepress-theme-plume": "^1.0.0-rc.164"
} }
} }

View File

@@ -9,7 +9,7 @@ permalink: /astral/ib77r5fd/
![QQ qr_code](/images/qq_qrcode.jpg){width="618" height="616" style="width: 200px"} ![QQ qr_code](/images/qq_qrcode.jpg){width="618" height="616" style="width: 200px"}
您在使用过程中遇到任何问题,欢迎通过 [issue](https://github.com/mlt131220/Astral3D/issues/new) 反馈。也欢迎加入我们的 QQ 交流群一起讨论。 您在使用过程中遇到任何问题,欢迎通过项目仓库反馈。也欢迎加入我们的 QQ 交流群一起讨论。
> :fire: 4核8G 200兆服务器仅需49元/月,[点击这里购买](https://www.rainyun.com/Astral3D_) > :fire: 4核8G 200兆服务器仅需49元/月,[点击这里购买](https://www.rainyun.com/Astral3D_)
> 专属优惠码Astral3D > 专属优惠码Astral3D

View File

@@ -176,7 +176,7 @@ export default defineUserConfig({
comment: { comment: {
provider: 'Giscus', // "Artalk" | "Giscus" | "Twikoo" | "Waline" provider: 'Giscus', // "Artalk" | "Giscus" | "Twikoo" | "Waline"
comment: true, comment: true,
repo: 'mlt131220/Astral3D-Docs', repo: 'Astral3D/Docs',
repoId: 'R_kgDOLuBMGg', repoId: 'R_kgDOLuBMGg',
category: 'Announcements', category: 'Announcements',
categoryId: 'DIC_kwDOLuBMGs4Cero3', categoryId: 'DIC_kwDOLuBMGs4Cero3',

View File

@@ -11,7 +11,7 @@ export default defineThemeConfig({
appearance: true, // 配置 深色模式 appearance: true, // 配置 深色模式
social: [ social: [
{ icon: 'github', link: 'https://github.com/mlt131220/Astral3D' }, { icon: 'github', link: 'http://code-server:3000/lyf/Astral3D.git' },
], ],
// navbarSocialInclude: ['github'], // 允许显示在导航栏的 social 社交链接 // navbarSocialInclude: ['github'], // 允许显示在导航栏的 social 社交链接
// aside: true, // 页内侧边栏, 默认显示在右侧 // aside: true, // 页内侧边栏, 默认显示在右侧
@@ -30,7 +30,7 @@ export default defineThemeConfig({
/* 站点页脚 */ /* 站点页脚 */
footer: { footer: {
message: '', message: '',
copyright: 'Copyright © 2023-present <a target="_blank" href="https://github.com/mlt131220">ErSan <mlt131220@163.com></a>', copyright: 'Copyright © 2026',
}, },
/** /**

View File

@@ -18,11 +18,11 @@ config:
- -
theme: alt theme: alt
text: Demo text: Demo
link: https://editor.astraljs.com/#/ link:
- -
theme: alt theme: alt
text: Github text: Github
link: https://github.com/mlt131220/Astral3D link:
- -
type: features type: features
features: features:

View File

@@ -8,7 +8,7 @@ permalink: /guide/dju4ekca/
<ImageCard <ImageCard
image="/images/manual/editor/extras/resource.png" image="/images/manual/editor/extras/resource.png"
title="资源中心" title="资源中心"
author="ErSan" author=""
date="2025/04/17" date="2025/04/17"
/> />
::: :::

View File

@@ -10,7 +10,7 @@ permalink: /guide/rf1xgf4z/
title="BIM" title="BIM"
description="通过将BIM工程文件.rvt转换为glTF文件在保留BIM构件属性的同时压缩优化模型以达到web端解析BIM并且轻量化的目的。" description="通过将BIM工程文件.rvt转换为glTF文件在保留BIM构件属性的同时压缩优化模型以达到web端解析BIM并且轻量化的目的。"
width="300" width="300"
author="ErSan" author=""
date="2025/04/15" date="2025/04/15"
/> />
</div> </div>
@@ -41,7 +41,7 @@ permalink: /guide/rf1xgf4z/
image="/images/manual/editor/leftSidebar/bimUpload.png" image="/images/manual/editor/leftSidebar/bimUpload.png"
title="上传模型" title="上传模型"
width="400" width="400"
author="ErSan" author=""
date="2025/04/15" date="2025/04/15"
/> />
</div> </div>
@@ -62,7 +62,7 @@ permalink: /guide/rf1xgf4z/
<ImageCard <ImageCard
image="/images/manual/editor/leftSidebar/bimHistory.png" image="/images/manual/editor/leftSidebar/bimHistory.png"
title="历史记录" title="历史记录"
author="ErSan" author=""
date="2025/04/15" date="2025/04/15"
/> />
::: :::

View File

@@ -10,7 +10,7 @@ permalink: /guide/u1631nwk/
title="CAD" title="CAD"
description="编辑器支持用户上传CAD图纸(.dwg、.dxf)进行解析预览" description="编辑器支持用户上传CAD图纸(.dwg、.dxf)进行解析预览"
width="300" width="300"
author="ErSan" author=""
date="2025/04/15" date="2025/04/15"
/> />
</div> </div>
@@ -43,7 +43,7 @@ permalink: /guide/u1631nwk/
<ImageCard <ImageCard
image="/images/manual/editor/leftSidebar/cadUpload.png" image="/images/manual/editor/leftSidebar/cadUpload.png"
title="上传图纸" title="上传图纸"
author="ErSan" author=""
date="2025/04/15" date="2025/04/15"
/> />
::: :::
@@ -59,7 +59,7 @@ permalink: /guide/u1631nwk/
<ImageCard <ImageCard
image="/images/manual/editor/leftSidebar/cadHistory.png" image="/images/manual/editor/leftSidebar/cadHistory.png"
title="历史记录" title="历史记录"
author="ErSan" author=""
date="2025/04/15" date="2025/04/15"
/> />
::: :::

View File

@@ -9,14 +9,14 @@ permalink: /guide/rzqb0h0l/
image="/images/manual/editor/leftSidebar/sceneTree.png" image="/images/manual/editor/leftSidebar/sceneTree.png"
title="场景树" title="场景树"
description="场景树是 Astral3D 编辑器中用于管理场景中所有对象的工具。它以树状结构展示场景中的层级关系,便于用户快速定位和操作对象" description="场景树是 Astral3D 编辑器中用于管理场景中所有对象的工具。它以树状结构展示场景中的层级关系,便于用户快速定位和操作对象"
author="ErSan" author=""
date="2025/04/14" date="2025/04/14"
/> />
<ImageCard <ImageCard
image="/images/manual/editor/leftSidebar/sceneTreeContextMenu.png" image="/images/manual/editor/leftSidebar/sceneTreeContextMenu.png"
title="场景节点右键菜单" title="场景节点右键菜单"
description="聚焦、删除、拷贝" description="聚焦、删除、拷贝"
author="ErSan" author=""
date="2025/04/14" date="2025/04/14"
/> />
</CardGrid> </CardGrid>

View File

@@ -278,4 +278,4 @@ function beforeDestroy() {
::: :::
<iframe height=500 width=100% src="https://editor.astraljs.com/#/preview/a18eb710-c1f8-4cff-b8ab-c3a2056ccf57" frameborder=0 allowfullscreen></iframe> <iframe height=500 width=100% src="#/preview/a18eb710-c1f8-4cff-b8ab-c3a2056ccf57" frameborder=0 allowfullscreen></iframe>

View File

@@ -9,7 +9,7 @@ permalink: /guide/7eo51kfy/
image="/images/manual/editor/viewport/drawing.png" image="/images/manual/editor/viewport/drawing.png"
title="2D图纸" title="2D图纸"
description="拖入(双击)添加至场景中的图纸会自动占据中心视口一半的位置其与3D视图中间的垂直分割线可以左右拖动以改变占据视口的大小" description="拖入(双击)添加至场景中的图纸会自动占据中心视口一半的位置其与3D视图中间的垂直分割线可以左右拖动以改变占据视口的大小"
author="ErSan" author=""
date="2025/04/16" date="2025/04/16"
/> />
::: :::
@@ -53,7 +53,7 @@ permalink: /guide/7eo51kfy/
<ImageCard <ImageCard
image="/images/manual/editor/viewport/drawingLayer.png" image="/images/manual/editor/viewport/drawingLayer.png"
title="CAD图层" title="CAD图层"
author="ErSan" author=""
date="2025/04/16" date="2025/04/16"
/> />
::: :::

View File

@@ -9,7 +9,7 @@ permalink: /guide/jkg3yahd/
image="/images/manual/editor/viewport/viewer.png" image="/images/manual/editor/viewport/viewer.png"
title="3D场景" title="3D场景"
description="编辑器的核心视口,展示用户所编辑的场景内容。" description="编辑器的核心视口,展示用户所编辑的场景内容。"
author="ErSan" author=""
date="2025/04/16" date="2025/04/16"
/> />
::: :::
@@ -38,7 +38,7 @@ permalink: /guide/jkg3yahd/
<ImageCard <ImageCard
image="/images/manual/editor/viewport/viewerCamera.png" image="/images/manual/editor/viewport/viewerCamera.png"
title="场景相机/六视图" title="场景相机/六视图"
author="ErSan" author=""
date="2025/04/16" date="2025/04/16"
/> />
::: :::
@@ -49,7 +49,7 @@ permalink: /guide/jkg3yahd/
image="/images/manual/editor/viewport/viewerRenderMode.png" image="/images/manual/editor/viewport/viewerRenderMode.png"
title="渲染模式" title="渲染模式"
width="400" width="400"
author="ErSan" author=""
date="2025/04/16" date="2025/04/16"
/> />
</div> </div>
@@ -66,7 +66,7 @@ permalink: /guide/jkg3yahd/
image="/images/manual/editor/viewport/viewerTransform.png" image="/images/manual/editor/viewport/viewerTransform.png"
title="变换控制" title="变换控制"
width="400" width="400"
author="ErSan" author=""
date="2025/04/16" date="2025/04/16"
/> />
</div> </div>

View File

@@ -9,7 +9,7 @@ permalink: /guide/swepocsg/
image="/images/manual/editor/page.png" image="/images/manual/editor/page.png"
title="编辑器" title="编辑器"
description="整个编辑界面分为 `顶栏`,`左侧边栏`,`场景视口`,`扩展栏`,`右侧边栏` 五个区域" description="整个编辑界面分为 `顶栏`,`左侧边栏`,`场景视口`,`扩展栏`,`右侧边栏` 五个区域"
author="ErSan" author=""
date="2025/04/14" date="2025/04/14"
/> />
::: :::

View File

@@ -42,13 +42,13 @@ permalink: /guide/f7smai4w/
<ImageCard <ImageCard
image="/images/manual/editor/header/exportObject.png" image="/images/manual/editor/header/exportObject.png"
title="导出选中对象" title="导出选中对象"
author="ErSan" author=""
date="2025/04/14" date="2025/04/14"
/> />
<ImageCard <ImageCard
image="/images/manual/editor/header/exportScene.png" image="/images/manual/editor/header/exportScene.png"
title="导出场景" title="导出场景"
author="ErSan" author=""
date="2025/04/14" date="2025/04/14"
/> />
</CardGrid> </CardGrid>
@@ -121,7 +121,7 @@ permalink: /guide/f7smai4w/
image="/images/manual/editor/header/mainColor.png" image="/images/manual/editor/header/mainColor.png"
title="主色调" title="主色调"
description="调整编辑器的主色,系统会自动生成相应的浅色、深色色阶,用于鼠标经过以及选中等效果" description="调整编辑器的主色,系统会自动生成相应的浅色、深色色阶,用于鼠标经过以及选中等效果"
author="ErSan" author=""
date="2025/04/14" date="2025/04/14"
/> />
@@ -132,7 +132,7 @@ permalink: /guide/f7smai4w/
<ImageCard <ImageCard
image="/images/manual/editor/header/setting.png" image="/images/manual/editor/header/setting.png"
title="系统设置" title="系统设置"
author="ErSan" author=""
date="2025/04/14" date="2025/04/14"
/> />

View File

@@ -9,7 +9,7 @@ permalink: /guide/4tirtbsc/
image="/images/manual/home/assets/assets-page.jpg" image="/images/manual/home/assets/assets-page.jpg"
title="资源中心" title="资源中心"
description="多级分类,精细控制" description="多级分类,精细控制"
author="ErSan" author=""
date="2025/08/01" date="2025/08/01"
/> />
::: :::
@@ -20,7 +20,7 @@ date="2025/08/01"
image="/images/manual/home/assets/asset-upload.png" image="/images/manual/home/assets/asset-upload.png"
title="上传资源" title="上传资源"
description="多级分类,精细控制" description="多级分类,精细控制"
author="ErSan" author=""
date="2025/08/01" date="2025/08/01"
/> />
::: :::
@@ -37,7 +37,7 @@ date="2025/08/01"
image="/images/manual/home/assets/asset-preview-screenshot.png" image="/images/manual/home/assets/asset-preview-screenshot.png"
title="通过预览截图" title="通过预览截图"
description="点击截图按钮,将自动截取当前画布填充至表单缩略图字段,并关闭此预览弹窗" description="点击截图按钮,将自动截取当前画布填充至表单缩略图字段,并关闭此预览弹窗"
author="ErSan" author=""
date="2025/08/01" date="2025/08/01"
/> />
::: :::
@@ -123,7 +123,7 @@ date="2025/08/01"
image="/images/manual/home/assets/asset-preview.png" image="/images/manual/home/assets/asset-preview.png"
title="预览资源" title="预览资源"
description="点击资源卡片/资源卡片操作栏的预览按钮进行资源预览。 预览弹窗左侧为可操作画布,右侧为资源详情及操作。" description="点击资源卡片/资源卡片操作栏的预览按钮进行资源预览。 预览弹窗左侧为可操作画布,右侧为资源详情及操作。"
author="ErSan" author=""
date="2025/08/01" date="2025/08/01"
/> />
::: :::
@@ -134,7 +134,7 @@ date="2025/08/01"
image="/images/manual/home/assets/asset-edit.png" image="/images/manual/home/assets/asset-edit.png"
title="编辑资源" title="编辑资源"
description="点击资源卡片操作栏的编辑选项进行资源编辑。 编辑时无法修改资源类型和资源文件。" description="点击资源卡片操作栏的编辑选项进行资源编辑。 编辑时无法修改资源类型和资源文件。"
author="ErSan" author=""
date="2025/08/01" date="2025/08/01"
/> />
::: :::

View File

@@ -9,7 +9,7 @@ permalink: /guide/dbd366ps/
image="/images/manual/home/project/project-page.png" image="/images/manual/home/project/project-page.png"
title="项目" title="项目"
description="" description=""
author="ErSan" author=""
date="2025/08/01" date="2025/08/01"
/> />
::: :::
@@ -23,7 +23,7 @@ date="2025/08/01"
image="/images/manual/home/project/new-project.png" image="/images/manual/home/project/new-project.png"
title="新建项目" title="新建项目"
description="可以选择新建一个空项目或者依据内置模板创建" description="可以选择新建一个空项目或者依据内置模板创建"
author="ErSan" author=""
date="2025/08/01" date="2025/08/01"
/> />
::: :::

View File

@@ -5,10 +5,10 @@ VITE_PORT=8090
VITE_GLOB_APP_TITLE='Astral3D Editor' VITE_GLOB_APP_TITLE='Astral3D Editor'
# 作者 # 作者
VITE_GLOB_AUTHOR='ErSan' VITE_GLOB_AUTHOR=''
# 当前版本 # 当前版本
VITE_GLOB_VERSION='1.0.0' VITE_GLOB_VERSION='1.0.0'
# 备案号 # 备案号
VITE_GLOB_BEIAN='滇ICP备XXXXXX号' VITE_GLOB_BEIAN=''

View File

@@ -16,7 +16,7 @@ VITE_ENABLE_ANALYZE=true
VITE_ENABLE_CONFIG_GENERATE=true VITE_ENABLE_CONFIG_GENERATE=true
# 当前站点URL # 当前站点URL
VITE_GLOB_ORIGIN=https://editor.astraljs.com/ VITE_GLOB_ORIGIN=
#ws接口 #ws接口
VITE_GLOB_SOCKET_URL=wss://editor.astraljs.com/socket VITE_GLOB_SOCKET_URL=

View File

@@ -1,6 +1,6 @@
{ {
"name": "@astral3d/editor", "name": "@astral3d/editor",
"author": "ErSan<mlt131220@163.com>", "author": "",
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -1,7 +1,6 @@
/** /**
* @author MaHaiBing * @author
* @email mlt131220@163.com * @date 2026/3/13
* @date 2023/11/29 10:10
* @description websocket hook;使用store管理websocket保证全局唯一性 * @description websocket hook;使用store管理websocket保证全局唯一性
*/ */
import {unref} from 'vue'; import {unref} from 'vue';

View File

@@ -1,7 +1,6 @@
/** /**
* @author MaHaiBing * @author
* @email mlt131220@163.com * @date 2026/3/13
* @date 2024/7/28 14:54
* @description 示例场景 * @description 示例场景
*/ */
import {request} from "@/http/request"; import {request} from "@/http/request";

View File

@@ -1,46 +1,44 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com * @description
* @date 2024/9/18 22:24 */
* @description
*/ export class ListrTask {
// @ts-ignore
export class ListrTask { private title: any;
// @ts-ignore private taskFn: any;
private title: any; isFailed: boolean;
private taskFn: any;
isFailed: boolean; constructor(title, taskFn) {
this.title = title;
constructor(title, taskFn) { this.taskFn = taskFn;
this.title = title; this.isFailed = false;
this.taskFn = taskFn; }
this.isFailed = false;
} async run() {
try {
async run() { await this.taskFn(this);
try { } catch (error) {
await this.taskFn(this); this.isFailed = true;
} catch (error) {
this.isFailed = true; throw error;
}
throw error; }
} }
}
} export class Listr {
private tasks: ListrTask[];
export class Listr {
private tasks: ListrTask[]; constructor(tasks: { title:string,task:(task: any) => Promise<void> }[]) {
this.tasks = tasks.map(task => new ListrTask(task.title, task.task));
constructor(tasks: { title:string,task:(task: any) => Promise<void> }[]) { }
this.tasks = tasks.map(task => new ListrTask(task.title, task.task));
} async run() {
for (const task of this.tasks) {
async run() { await task.run();
for (const task of this.tasks) { if (task.isFailed) {
await task.run(); break; // 如果任务失败,停止执行后续任务
if (task.isFailed) { }
break; // 如果任务失败,停止执行后续任务 }
} }
}
}
} }

View File

@@ -1,249 +1,247 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com * @description glTF处理器插件
* @date 2024/9/16 23:19 */
* @description glTF处理器插件 import {h, ref} from "vue";
*/ import type {ModalReactive} from "naive-ui";
import {h, ref} from "vue"; import {t} from "@/language";
import type {ModalReactive} from "naive-ui"; import type {Plugin} from "@astral3d/engine";
import {t} from "@/language"; import GLTFHandlerComponent from "@/components/es/plugin/builtin/GLTFHandler.vue";
import type {Plugin} from "@astral3d/engine";
import GLTFHandlerComponent from "@/components/es/plugin/builtin/GLTFHandler.vue"; import { MeshoptEncoder, MeshoptSimplifier } from 'meshoptimizer';
// @ts-ignore
import { MeshoptEncoder, MeshoptSimplifier } from 'meshoptimizer'; import { ready as resampleReady, resample as resampleWASM } from 'keyframe-resample';
// @ts-ignore import { Logger,WebIO, Transform } from '@gltf-transform/core';
import { ready as resampleReady, resample as resampleWASM } from 'keyframe-resample'; import {
import { Logger,WebIO, Transform } from '@gltf-transform/core'; dedup,
import { instance,
dedup, prune,
instance, quantize,
prune, resample,
quantize, weld,
resample, meshopt,
weld, draco,
meshopt, simplify,
draco, textureCompress,
simplify, flatten,
textureCompress, join,
flatten, sparse,
join, palette,
sparse, } from '@gltf-transform/functions';
palette, import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
} from '@gltf-transform/functions'; import {Session} from "./session";
import { ALL_EXTENSIONS } from '@gltf-transform/extensions'; import {loadScript} from "@/utils/common/utils";
import {Session} from "./session"; import { optimizePNG } from "@/plugin/glTFHandler/optimizePng";
import {loadScript} from "@/utils/common/utils";
import { optimizePNG } from "@/plugin/glTFHandler/optimizePng"; //使用'micromatch',因为'contains: true'没有像预期的那样在minimatch中工作。需要确保'*'匹配的模式,如'image/png'。
export const MICROMATCH_OPTIONS = { nocase: true, contains: true };
//使用'micromatch',因为'contains: true'没有像预期的那样在minimatch中工作。需要确保'*'匹配的模式,如'image/png'。
export const MICROMATCH_OPTIONS = { nocase: true, contains: true }; export default class GLTFHandler implements Plugin{
icon= "";
export default class GLTFHandler implements Plugin{ name = "glTF处理器";
icon= ""; version = 1;
name = "glTF处理器";
version = 1; logger = new Logger(Logger.Verbosity.INFO);
io = new WebIO({credentials: 'include'}).registerExtensions(ALL_EXTENSIONS);
logger = new Logger(Logger.Verbosity.INFO);
io = new WebIO({credentials: 'include'}).registerExtensions(ALL_EXTENSIONS); modalInstance:ModalReactive | undefined = undefined;
GLTFHandlerComponentRef = ref();
modalInstance:ModalReactive | undefined = undefined; dracoScript = new Proxy({
GLTFHandlerComponentRef = ref(); encoder:false,
dracoScript = new Proxy({ decoder:false,
encoder:false, failMsg:""
decoder:false, },{
failMsg:"" set:(target: {decoder: boolean;encoder: boolean;failMsg: string;}, p: string | symbol, newValue: any): boolean => {
},{ target[p] = newValue;
set:(target: {decoder: boolean;encoder: boolean;failMsg: string;}, p: string | symbol, newValue: any): boolean => {
target[p] = newValue; if(p === "failMsg"){
window.$message?.error(newValue)
if(p === "failMsg"){ return true;
window.$message?.error(newValue) }
return true;
} if(target.encoder && target.decoder){
this.registerDependencies()
if(target.encoder && target.decoder){ }
this.registerDependencies()
} return true;
}
return true; })
}
}) async install() {
// console.log(`%c glTF处理器 %c 版本1.0.0`, 'background: #35495e; padding: 4px; border-radius: 3px 0 0 3px; color: #fff',
async install() { // 'background: #41b883; padding: 4px; border-radius: 0 3px 3px 0; color: #fff');
// console.log(`%c glTF处理器 %c 版本1.0.0`, 'background: #35495e; padding: 4px; border-radius: 3px 0 0 3px; color: #fff', }
// 'background: #41b883; padding: 4px; border-radius: 0 3px 3px 0; color: #fff');
} async run() {
// 运行时再加载draco相关js
async run() { if(!this.dracoScript.encoder){
// 运行时再加载draco相关js loadScript("/libs/draco/draco_encoder.js",false).then(() => {
if(!this.dracoScript.encoder){ this.dracoScript.encoder = true;
loadScript("/libs/draco/draco_encoder.js",false).then(() => { }).catch(() => {
this.dracoScript.encoder = true; this.dracoScript.failMsg = t("plugin.gltfHandler['Draco encoder load fail,Refresh the page and try again.']");
}).catch(() => { })
this.dracoScript.failMsg = t("plugin.gltfHandler['Draco encoder load fail,Refresh the page and try again.']"); }
}) if(!this.dracoScript.decoder) {
} loadScript("/libs/draco/draco_decoder.js", false).then(() => {
if(!this.dracoScript.decoder) { this.dracoScript.decoder = true;
loadScript("/libs/draco/draco_decoder.js", false).then(() => { }).catch(() => {
this.dracoScript.decoder = true; this.dracoScript.failMsg = t("plugin.gltfHandler['Draco decoder load fail,Refresh the page and try again.']");
}).catch(() => { })
this.dracoScript.failMsg = t("plugin.gltfHandler['Draco decoder load fail,Refresh the page and try again.']"); }
})
} this.GLTFHandlerComponentRef = ref();
this.modalInstance = window.$modal.create({
this.GLTFHandlerComponentRef = ref(); title: this.name,
this.modalInstance = window.$modal.create({ preset:"card",
title: this.name, maskClosable:false,
preset:"card", style: {
maskClosable:false, width: '90%',
style: { maxWidth: '800px'
width: '90%', },
maxWidth: '800px' onAfterLeave: () => this.finish(),
}, content: () => {
onAfterLeave: () => this.finish(), return h(GLTFHandlerComponent,{
content: () => { onOptimize: this.optimize.bind(this),
return h(GLTFHandlerComponent,{ onFinish: () => this.finish(),
onOptimize: this.optimize.bind(this), ref: this.GLTFHandlerComponentRef
onFinish: () => this.finish(), },"")
ref: this.GLTFHandlerComponentRef },
},"") })
}, }
})
} // 关闭插件
finish(){
// 关闭插件 this.modalInstance && this.modalInstance.destroy();
finish(){
this.modalInstance && this.modalInstance.destroy(); this.GLTFHandlerComponentRef = ref();
}
this.GLTFHandlerComponentRef = ref();
} uninstall(): void {}
uninstall(): void {} setLogger(log:string){
if(!this.GLTFHandlerComponentRef.value) return;
setLogger(log:string){
if(!this.GLTFHandlerComponentRef.value) return; this.GLTFHandlerComponentRef.value.addLog(log);
}
this.GLTFHandlerComponentRef.value.addLog(log);
} async registerDependencies(){
this.io.registerDependencies({
async registerDependencies(){ // @ts-ignore
this.io.registerDependencies({ 'draco3d.encoder': await new DracoEncoderModule(),
// @ts-ignore // @ts-ignore
'draco3d.encoder': await new DracoEncoderModule(), 'draco3d.decoder': await new DracoDecoderModule(),
// @ts-ignore })
'draco3d.decoder': await new DracoDecoderModule(), }
})
} /* 下面是实现的自定义的处理器方法 */
async optimize(opts:IPlugin.GLTFHandlerOptimizeModel,inputFile:File,outputFileName = ""){
/* 下面是实现的自定义的处理器方法 */ this.setLogger(`Optimize ${inputFile.name}`);
async optimize(opts:IPlugin.GLTFHandlerOptimizeModel,inputFile:File,outputFileName = ""){
this.setLogger(`Optimize ${inputFile.name}`); if(this.dracoScript.failMsg){
window.$message?.error(this.dracoScript.failMsg);
if(this.dracoScript.failMsg){ return;
window.$message?.error(this.dracoScript.failMsg); }
return; if(!this.dracoScript.encoder || !this.dracoScript.decoder){
} setTimeout(()=>this.optimize(opts,inputFile,outputFileName),1000)
if(!this.dracoScript.encoder || !this.dracoScript.decoder){ return;
setTimeout(()=>this.optimize(opts,inputFile,outputFileName),1000) }
return;
} /* 文件准备就绪,开始优化 */
/* 文件准备就绪,开始优化 */ const transforms: Transform[] = [
optimizePNG(),
const transforms: Transform[] = [ dedup()
optimizePNG(), ];
dedup()
]; if (opts.instance) transforms.push(instance({ min: opts.instanceMin }));
if (opts.instance) transforms.push(instance({ min: opts.instanceMin })); if (opts.palette) {
transforms.push(
if (opts.palette) { palette({
transforms.push( min: opts.paletteMin,
palette({ keepAttributes: !opts.prune || !opts.pruneAttributes,
min: opts.paletteMin, }),
keepAttributes: !opts.prune || !opts.pruneAttributes, );
}), }
);
} if (opts.flatten) transforms.push(flatten());
if (opts.join) transforms.push(join());
if (opts.flatten) transforms.push(flatten()); if (opts.weld) transforms.push(weld());
if (opts.join) transforms.push(join());
if (opts.weld) transforms.push(weld()); if (opts.simplify) {
transforms.push(
if (opts.simplify) { simplify({
transforms.push( simplifier: MeshoptSimplifier,
simplify({ // simplifyError 用%显示时扩大了100倍需要高精度计算减小100倍
simplifier: MeshoptSimplifier, error: opts.simplifyError / 100,
// simplifyError 用%显示时扩大了100倍需要高精度计算减小100倍 ratio: opts.simplifyRatio,
error: opts.simplifyError / 100, lockBorder: opts.simplifyLockBorder,
ratio: opts.simplifyRatio, }),
lockBorder: opts.simplifyLockBorder, );
}), }
);
} // 重新采样动画通道,无损地删除重复的关键帧以减小文件大小。重复的关键帧通常出现在由创作软件“烘焙”的动画中,以应用 IK 约束或其他软件特定功能。
transforms.push(resample({ ready: resampleReady, resample: resampleWASM }));
// 重新采样动画通道,无损地删除重复的关键帧以减小文件大小。重复的关键帧通常出现在由创作软件“烘焙”的动画中,以应用 IK 约束或其他软件特定功能。
transforms.push(resample({ ready: resampleReady, resample: resampleWASM })); if (opts.prune) {
transforms.push(
if (opts.prune) { prune({
transforms.push( keepAttributes: !opts.pruneAttributes,
prune({ keepIndices: false,
keepAttributes: !opts.pruneAttributes, keepLeaves: !opts.pruneLeaves,
keepIndices: false, keepSolidTextures: !opts.pruneSolidTextures,
keepLeaves: !opts.pruneLeaves, }),
keepSolidTextures: !opts.pruneSolidTextures, );
}), }
);
} transforms.push(sparse());
transforms.push(sparse()); if (opts.textureCompress !== 'none') {
transforms.push(
if (opts.textureCompress !== 'none') { textureCompress({
transforms.push( resize: [opts.textureSize, opts.textureSize],
textureCompress({ targetFormat: opts.textureCompress === 'auto' ? undefined : opts.textureCompress,
resize: [opts.textureSize, opts.textureSize], // limitInputPixels: options.limitInputPixels as boolean,
targetFormat: opts.textureCompress === 'auto' ? undefined : opts.textureCompress, limitInputPixels: false,
// limitInputPixels: options.limitInputPixels as boolean, }),
limitInputPixels: false, );
}), }
);
} // 最后进行网格压缩
if (opts.compress === 'draco') {
// 最后进行网格压缩 transforms.push(draco());
if (opts.compress === 'draco') { } else if (opts.compress === 'meshopt') {
transforms.push(draco()); transforms.push(meshopt({ encoder: MeshoptEncoder, level: opts.meshoptLevel }));
} else if (opts.compress === 'meshopt') { } else if (opts.compress === 'quantize') {
transforms.push(meshopt({ encoder: MeshoptEncoder, level: opts.meshoptLevel })); transforms.push(quantize());
} else if (opts.compress === 'quantize') { }
transforms.push(quantize());
} // 设置输出文件名
if(!outputFileName){
// 设置输出文件名 const format = inputFile.name.split(".").pop();
if(!outputFileName){ outputFileName = inputFile.name.replace(`.${format}`,`_astral3d.optimize.${format}`)
const format = inputFile.name.split(".").pop(); }
outputFileName = inputFile.name.replace(`.${format}`,`_astral3d.optimize.${format}`)
} // 生成临时 URL
const inputFileUrl = URL.createObjectURL(inputFile);
// 生成临时 URL
const inputFileUrl = URL.createObjectURL(inputFile); let outputFile:File | undefined = undefined
try {
let outputFile:File | undefined = undefined outputFile = await Session.create(this,inputFileUrl, inputFile.name, outputFileName)
try { .setDisplay(true)
outputFile = await Session.create(this,inputFileUrl, inputFile.name, outputFileName) .transform(...transforms);
.setDisplay(true)
.transform(...transforms); this.setLogger(`Optimize ${inputFile.name} success!`);
}catch (e:unknown){
this.setLogger(`Optimize ${inputFile.name} success!`); if (e instanceof Error) {
}catch (e:unknown){ window.$message?.error(e.message);
if (e instanceof Error) { this.setLogger(`Optimize ${inputFile.name} error: ${e.message}`);
window.$message?.error(e.message); } else {
this.setLogger(`Optimize ${inputFile.name} error: ${e.message}`); window.$message?.error(e as string);
} else { this.setLogger(`Optimize ${inputFile.name} error: ${e}`);
window.$message?.error(e as string); }
this.setLogger(`Optimize ${inputFile.name} error: ${e}`); }
}
} return outputFile;
}
return outputFile;
}
} }

View File

@@ -1,55 +1,53 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com * @description 语义化点云重建
* @date 2025/07/03 14:20 */
* @description 语义化点云重建 import { h, ref } from "vue";
*/ import type { ModalReactive } from "naive-ui";
import { h, ref } from "vue"; import type { Plugin } from "@astral3d/engine";
import type { ModalReactive } from "naive-ui"; import PointCloudReconstructorComponent from "@/components/es/plugin/builtin/PointCloudReconstructor.vue";
import type { Plugin } from "@astral3d/engine";
import PointCloudReconstructorComponent from "@/components/es/plugin/builtin/PointCloudReconstructor.vue"; export default class PointCloudReconstructor implements Plugin {
icon = "";
export default class PointCloudReconstructor implements Plugin { name = "语义化点云重建";
icon = ""; version = 0.1;
name = "语义化点云重建";
version = 0.1; modalInstance: ModalReactive | undefined = undefined;
componentRef = ref();
modalInstance: ModalReactive | undefined = undefined;
componentRef = ref(); async install() {
// console.log(`%c 语义化点云重建 %c 版本0.1.0`, 'background: #35495e; padding: 4px; border-radius: 3px 0 0 3px; color: #fff',
async install() { // 'background: #41b883; padding: 4px; border-radius: 0 3px 3px 0; color: #fff');
// console.log(`%c 语义化点云重建 %c 版本0.1.0`, 'background: #35495e; padding: 4px; border-radius: 3px 0 0 3px; color: #fff', }
// 'background: #41b883; padding: 4px; border-radius: 0 3px 3px 0; color: #fff');
} async run() {
this.componentRef = ref();
async run() { this.modalInstance = window.$modal.create({
this.componentRef = ref(); title: this.name,
this.modalInstance = window.$modal.create({ preset: "card",
title: this.name, maskClosable: false,
preset: "card", style: {
maskClosable: false, width: '90%',
style: { maxWidth: '800px'
width: '90%', },
maxWidth: '800px' onAfterLeave: () => {
}, this.componentRef.value.handleClose();
onAfterLeave: () => { this.finish();
this.componentRef.value.handleClose(); },
this.finish(); content: () => {
}, return h(PointCloudReconstructorComponent, {
content: () => { ref: this.componentRef
return h(PointCloudReconstructorComponent, { }, "")
ref: this.componentRef },
}, "") })
}, }
})
} // 关闭插件
finish() {
// 关闭插件 this.modalInstance && this.modalInstance.destroy();
finish() {
this.modalInstance && this.modalInstance.destroy(); this.componentRef = ref();
}
this.componentRef = ref();
} uninstall(): void { }
uninstall(): void { }
} }

View File

@@ -1,54 +1,52 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com * @description
* @date 2024/9/16 0:36 */
* @description import { defineStore } from 'pinia';
*/ import { store } from '@/store';
import { defineStore } from 'pinia'; import {computed, reactive, toRefs} from "vue";
import { store } from '@/store';
import {computed, reactive, toRefs} from "vue"; interface IPluginState {
// 已安装(可用)插件的列表
interface IPluginState { plugins:{[name:string]:IPlugin.Item}
// 已安装(可用)插件的列表 }
plugins:{[name:string]:IPlugin.Item}
} export const usePluginStore = defineStore("plugin",() => {
const state = reactive<IPluginState>({
export const usePluginStore = defineStore("plugin",() => { plugins:{}
const state = reactive<IPluginState>({ })
plugins:{}
}) const getPluginsList = () => computed(() => Object.values(state.plugins));
const setPlugins = (_plugins:IPlugin.Item[]) => {
const getPluginsList = () => computed(() => Object.values(state.plugins)); state.plugins = {};
const setPlugins = (_plugins:IPlugin.Item[]) => {
state.plugins = {}; _plugins.forEach(plugin => {
state.plugins[plugin.name] = {
_plugins.forEach(plugin => { name:plugin.name,
state.plugins[plugin.name] = { icon:plugin.icon
name:plugin.name, }
icon:plugin.icon })
}
}) }
const addPlugin = (plugin:IPlugin.Item) => {
} state.plugins[plugin.name] = {
const addPlugin = (plugin:IPlugin.Item) => { name:plugin.name,
state.plugins[plugin.name] = { icon:plugin.icon
name:plugin.name, }
icon:plugin.icon }
} const removePlugin = (pluginName:string) => {
} delete state.plugins[pluginName];
const removePlugin = (pluginName:string) => { }
delete state.plugins[pluginName];
} return {
...toRefs(state),
return { getPluginsList,
...toRefs(state), setPlugins,
getPluginsList, addPlugin,
setPlugins, removePlugin
addPlugin, }
removePlugin })
}
}) export function usePluginStoreWithOut() {
return usePluginStore(store);
export function usePluginStoreWithOut() {
return usePluginStore(store);
} }

View File

@@ -1,66 +1,64 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com * @description 全局常量
* @date 2024/3/26 9:57 */
* @description 全局常量 // 支持的模型格式
*/ export const MODEL_SUPPORT_TYPE = [
// 支持的模型格式 // 普通模型格式
export const MODEL_SUPPORT_TYPE = [ "gltf","glb","fbx","obj","mtl",
// 普通模型格式 "3dm","3ds",
"gltf","glb","fbx","obj","mtl", "3mf","amf",
"3dm","3ds", "dae","drc",
"3mf","amf", "kmz","ldr",
"dae","drc", "mpd","md2",
"kmz","ldr", "pcd","ply",
"mpd","md2", "stl","svg",
"pcd","ply", "usdz","vox","vtk","vtp",
"stl","svg", "wrl",
"usdz","vox","vtk","vtp", "xyz",
"wrl", "json","zip",
"xyz", // 自解析格式
"json","zip", "rvt","rfa","ifc",
// 自解析格式 "3DTiles",
"rvt","rfa","ifc", //"osgb",
"3DTiles", // "rvm",
//"osgb", // "dgn",
// "rvm", /*"jt"*/
// "dgn", // "shp",
/*"jt"*/ // "stp","step"
// "shp", ];
// "stp","step"
]; // 资源中心支持上传的格式
export const ASSET_UPLOAD_SUPPORT_TYPE = {
// 资源中心支持上传的格式 'Model': ['glb','gltf','fbx','obj'],
export const ASSET_UPLOAD_SUPPORT_TYPE = { 'Material': ['zip'],
'Model': ['glb','gltf','fbx','obj'], 'Texture': ['png','jpg','jpeg','webp'],
'Material': ['zip'], 'Billboard': ['png','jpg','jpeg','webp','svg'],
'Texture': ['png','jpg','jpeg','webp'], 'HDR': ['hdr','exr'],
'Billboard': ['png','jpg','jpeg','webp','svg'], 'Tiles': ['zip'],
'HDR': ['hdr','exr'], }
'Tiles': ['zip'],
} // 需要转换的BIM模型格式
export const NEED_CONVERT_BIM_MODEL = ["rvt", "rfa"];
// 需要转换的BIM模型格式
export const NEED_CONVERT_BIM_MODEL = ["rvt", "rfa"]; // 支持的图纸文件类型
export const DRAWING_SUPPORT_TYPE = ["dwg", "dxf", "png", "jpg", "jpeg"];
// 支持的图纸文件类型
export const DRAWING_SUPPORT_TYPE = ["dwg", "dxf", "png", "jpg", "jpeg"]; // 需要转换的图纸格式
export const NEED_CONVERT_DRAWING = ["dwg"];
// 需要转换的图纸格式
export const NEED_CONVERT_DRAWING = ["dwg"]; // 支持的文档类型
export const DOC_SUPPORT_TYPE = ["doc","docx","xls","xlsx","xlsm","ppt",'pptx',"pdf","txt"];
// 支持的文档类型
export const DOC_SUPPORT_TYPE = ["doc","docx","xls","xlsx","xlsm","ppt",'pptx',"pdf","txt"]; export const SCENE_TYPE = [
{label:"城市",value:"城市"},
export const SCENE_TYPE = [ {label:"园区",value:"园区"},
{label:"城市",value:"城市"}, {label:"工厂",value:"工厂"},
{label:"园区",value:"园区"}, {label:"楼宇",value:"楼宇"},
{label:"工厂",value:"工厂"}, {label:"设备",value:"设备"},
{label:"楼宇",value:"楼宇"}, {label:"其他",value:"其他"},
{label:"设备",value:"设备"}, ]
{label:"其他",value:"其他"},
] // 截屏占位图
export const DefaultScreenshot = "/static/images/placeholder/截屏占位图.png";
// 截屏占位图
export const DefaultScreenshot = "/static/images/placeholder/截屏占位图.png";

View File

@@ -1,358 +1,356 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com * @description 预览菜单操作
* @date 2024/8/22 */
* @description 预览菜单操作 import { useFullscreen } from '@vueuse/core'
*/ import {
import { useFullscreen } from '@vueuse/core' IPreviewOperation,
import { usePreviewOperationStoreWithOut
IPreviewOperation, } from "@/store/modules/previewOperation";
usePreviewOperationStoreWithOut import * as THREE from 'three';
} from "@/store/modules/previewOperation"; import { App,Hooks,ClippedEdgesBox,Measure,MeasureMode,ModelExplode,Roaming,MiniMap } from "@astral3d/engine";
import * as THREE from 'three'; import { t } from "@/language";
import { App,Hooks,ClippedEdgesBox,Measure,MeasureMode,ModelExplode,Roaming,MiniMap } from "@astral3d/engine";
import { t } from "@/language"; const operationStore = usePreviewOperationStoreWithOut();
const operationStore = usePreviewOperationStoreWithOut(); const { enter:enterFullscreen,exit:exitFullscreen } = useFullscreen();
const { enter:enterFullscreen,exit:exitFullscreen } = useFullscreen(); let roamPdFn, pointerlockFn;
let roamPdFn, pointerlockFn; export class MenuOperation {
// 初始化控制器状态
export class MenuOperation { static InitControlsState: string = "{}";
// 初始化控制器状态
static InitControlsState: string = "{}"; // 开始漫游前的相机位置
static lastRoadCameraPos = new THREE.Vector3();
// 开始漫游前的相机位置
static lastRoadCameraPos = new THREE.Vector3(); // 开始漫游前的相机目标位置
static lastRoadCameraTarget = new THREE.Vector3();
// 开始漫游前的相机目标位置
static lastRoadCameraTarget = new THREE.Vector3(); // 模型爆炸专用图层
static explodeLayer = 10;
// 模型爆炸专用图层
static explodeLayer = 10; // 当前爆炸的模型
static explodeModel:THREE.Object3D | null = null;
// 当前爆炸的模型
static explodeModel:THREE.Object3D | null = null; // 剖切盒子
static _clippedEdgesBox:ClippedEdgesBox | null = null;
// 剖切盒子
static _clippedEdgesBox:ClippedEdgesBox | null = null; // 测量类
static _measure:Measure | null = null;
// 测量类
static _measure:Measure | null = null; // 爆炸类
static _explode:ModelExplode | null = null;
// 爆炸类
static _explode:ModelExplode | null = null; // 漫游类
static _roaming:Roaming | null = null;
// 漫游类
static _roaming:Roaming | null = null; // 小地图
static _miniMap:MiniMap | null = null;
// 小地图
static _miniMap:MiniMap | null = null; static Init(key: string) {
if (MenuOperation[key]) {
static Init(key: string) { MenuOperation[key]();
if (MenuOperation[key]) { } else {
MenuOperation[key](); window.$message?.warning("相关模块正在开发中...")
} else { }
window.$message?.warning("相关模块正在开发中...") }
}
} static get ClippedEdgesBox(){
if(!MenuOperation._clippedEdgesBox){
static get ClippedEdgesBox(){ MenuOperation._clippedEdgesBox = new ClippedEdgesBox(window.viewer);
if(!MenuOperation._clippedEdgesBox){ }
MenuOperation._clippedEdgesBox = new ClippedEdgesBox(window.viewer);
} return MenuOperation._clippedEdgesBox;
}
return MenuOperation._clippedEdgesBox;
} static get Measure():Measure{
if(!MenuOperation._measure){
static get Measure():Measure{ MenuOperation._measure = new Measure(window.viewer,MeasureMode.Distance);
if(!MenuOperation._measure){
MenuOperation._measure = new Measure(window.viewer,MeasureMode.Distance); MenuOperation._measure.addEventListener("complete",() => {
if(!MenuOperation._measure) return;
MenuOperation._measure.addEventListener("complete",() => {
if(!MenuOperation._measure) return; // 激活清除测量按钮
if (!MenuOperation._measure.isClose && MenuOperation._measure.measureGroup.children.length > 0) {
// 激活清除测量按钮 (<{ [key: string]: IPreviewOperation }>operationStore.menuList.measure.children).clearMeasure.disabled = false;
if (!MenuOperation._measure.isClose && MenuOperation._measure.measureGroup.children.length > 0) { }
(<{ [key: string]: IPreviewOperation }>operationStore.menuList.measure.children).clearMeasure.disabled = false; })
} }
})
} return MenuOperation._measure;
}
return MenuOperation._measure;
} static get Explode():ModelExplode{
if(!MenuOperation._explode){
static get Explode():ModelExplode{ MenuOperation._explode = new ModelExplode();
if(!MenuOperation._explode){ }
MenuOperation._explode = new ModelExplode();
} return MenuOperation._explode;
}
return MenuOperation._explode;
} static get Roaming():Roaming{
if(!MenuOperation._roaming){
static get Roaming():Roaming{ MenuOperation._roaming = new Roaming(window.viewer);
if(!MenuOperation._roaming){ }
MenuOperation._roaming = new Roaming(window.viewer);
} return MenuOperation._roaming;
}
return MenuOperation._roaming;
} static get MiniMap():MiniMap{
if(!MenuOperation._miniMap){
static get MiniMap():MiniMap{ MenuOperation._miniMap = new MiniMap(window.viewer,{
if(!MenuOperation._miniMap){ mapSize: 100,
MenuOperation._miniMap = new MiniMap(window.viewer,{ mapRenderSize: 350,
mapSize: 100, followTarget: window.viewer.camera,
mapRenderSize: 350, isShow: false,
followTarget: window.viewer.camera, });
isShow: false, }
});
} return MenuOperation._miniMap;
}
return MenuOperation._miniMap;
} /**
* 还原视角
/** */
* 还原视角 static toHome() {
*/ if (MenuOperation.InitControlsState === "{}") {
static toHome() { window.$message?.warning("缺失初始视角信息")
if (MenuOperation.InitControlsState === "{}") { return;
window.$message?.warning("缺失初始视角信息") }
return;
} window.viewer.modules.controls.fromJSON(MenuOperation.InitControlsState, true);
}
window.viewer.modules.controls.fromJSON(MenuOperation.InitControlsState, true);
} /**
* 自动旋转
/** */
* 自动旋转 static autoRotate() {
*/ operationStore.menuList.autoRotate.active = !operationStore.menuList.autoRotate.active;
static autoRotate() { }
operationStore.menuList.autoRotate.active = !operationStore.menuList.autoRotate.active;
} /**
* 剖切
/** */
* 剖切 static cutting() {
*/ operationStore.menuList.cutting.active = !operationStore.menuList.cutting.active;
static cutting() {
operationStore.menuList.cutting.active = !operationStore.menuList.cutting.active; if (operationStore.menuList.cutting.active) {
MenuOperation.ClippedEdgesBox.open();
if (operationStore.menuList.cutting.active) { } else {
MenuOperation.ClippedEdgesBox.open(); MenuOperation.ClippedEdgesBox.close();
} else { }
MenuOperation.ClippedEdgesBox.close(); }
}
} // 测距
static distance() {
// 测距 // 上一个测量也许未完成
static distance() { if (!MenuOperation.Measure.isCompleted) {
// 上一个测量也许未完成 MenuOperation.Measure.complete();
if (!MenuOperation.Measure.isCompleted) { }
MenuOperation.Measure.complete();
} window.$message?.info(t("prompt['Left click to confirm the drawing point, and right click to complete the drawing.']"),{
duration: 1500
window.$message?.info(t("prompt['Left click to confirm the drawing point, and right click to complete the drawing.']"),{ })
duration: 1500
}) MenuOperation.Measure.mode = MeasureMode.Distance;
MenuOperation.Measure.open();
MenuOperation.Measure.mode = MeasureMode.Distance;
MenuOperation.Measure.open(); operationStore.menuList.measure.active = true;
}
operationStore.menuList.measure.active = true;
} // 测角度
static angle() {
// 测角度 if (!MenuOperation.Measure.isCompleted) {
static angle() { MenuOperation.Measure.complete();
if (!MenuOperation.Measure.isCompleted) { }
MenuOperation.Measure.complete();
} window.$message?.info(t("prompt['Left click to confirm the drawing point, and right click to complete the drawing.']"),{
duration: 1500
window.$message?.info(t("prompt['Left click to confirm the drawing point, and right click to complete the drawing.']"),{ })
duration: 1500
}) MenuOperation.Measure.mode = MeasureMode.Angle;
MenuOperation.Measure.open();
MenuOperation.Measure.mode = MeasureMode.Angle;
MenuOperation.Measure.open(); operationStore.menuList.measure.active = true;
}
operationStore.menuList.measure.active = true;
} // 测面积
static area() {
// 测面积 if (!MenuOperation.Measure.isCompleted) {
static area() { MenuOperation.Measure.complete();
if (!MenuOperation.Measure.isCompleted) { }
MenuOperation.Measure.complete();
} window.$message?.info(t("prompt['Left click to confirm the drawing point, and right click to complete the drawing.']"),{
duration: 1500
window.$message?.info(t("prompt['Left click to confirm the drawing point, and right click to complete the drawing.']"),{ })
duration: 1500
}) MenuOperation.Measure.mode = MeasureMode.Area;
MenuOperation.Measure.open();
MenuOperation.Measure.mode = MeasureMode.Area;
MenuOperation.Measure.open(); operationStore.menuList.measure.active = true;
}
operationStore.menuList.measure.active = true;
} // 清除测量结果
static clearMeasure() {
// 清除测量结果 (<{ [key: string]: IPreviewOperation }>operationStore.menuList.measure.children).clearMeasure.disabled = true;
static clearMeasure() {
(<{ [key: string]: IPreviewOperation }>operationStore.menuList.measure.children).clearMeasure.disabled = true; MenuOperation.Measure.clear();
MenuOperation.Measure.clear(); operationStore.menuList.measure.active = false;
}
operationStore.menuList.measure.active = false;
} // 爆炸
static explode(){
// 爆炸 if(!App.selected && !MenuOperation.explodeModel){
static explode(){ window.$message?.warning(t("prompt['No object selected.']"));
if(!App.selected && !MenuOperation.explodeModel){ return;
window.$message?.warning(t("prompt['No object selected.']")); }
return;
} operationStore.menuList.explode.active = !operationStore.menuList.explode.active;
operationStore.menuList.explode.active = !operationStore.menuList.explode.active; if (operationStore.menuList.explode.active){
if(!App.selected) return;
if (operationStore.menuList.explode.active){
if(!App.selected) return; App.selected.traverse(obj => obj.layers.set(MenuOperation.explodeLayer));
window.viewer.camera.layers.set(MenuOperation.explodeLayer);
App.selected.traverse(obj => obj.layers.set(MenuOperation.explodeLayer));
window.viewer.camera.layers.set(MenuOperation.explodeLayer); MenuOperation.Explode.explodeModel(App.selected,operationStore.explodeScalar);
MenuOperation.explodeModel = App.selected;
MenuOperation.Explode.explodeModel(App.selected,operationStore.explodeScalar); }else{
MenuOperation.explodeModel = App.selected; if(!MenuOperation.explodeModel) return;
}else{
if(!MenuOperation.explodeModel) return; MenuOperation.explodeModel.traverse(obj => obj.layers.set(0));
window.viewer.camera.layers.set(0);
MenuOperation.explodeModel.traverse(obj => obj.layers.set(0));
window.viewer.camera.layers.set(0); MenuOperation.Explode.restore();
MenuOperation.Explode.restore(); MenuOperation.explodeModel = null;
}
MenuOperation.explodeModel = null; }
}
} // 漫游
static roaming() {
// 漫游 if (!MenuOperation.Roaming) return;
static roaming() {
if (!MenuOperation.Roaming) return; operationStore.menuList.roaming.active = !operationStore.menuList.roaming.active;
operationStore.menuList.roaming.active = !operationStore.menuList.roaming.active; if (operationStore.menuList.roaming.active) {
MenuOperation.enterRoaming();
if (operationStore.menuList.roaming.active) { } else {
MenuOperation.enterRoaming(); MenuOperation.leaveRoaming();
} else { }
MenuOperation.leaveRoaming(); }
}
} // 选点进入漫游
static enterRoaming() {
// 选点进入漫游 window.$message?.info(t("preview.Please select initial position"));
static enterRoaming() {
window.$message?.info(t("preview.Please select initial position")); const canvas = window.viewer.renderer.domElement;
const canvas = window.viewer.renderer.domElement; const handlePointerDown = (e: MouseEvent) => {
const raycaster = new THREE.Raycaster();
const handlePointerDown = (e: MouseEvent) => {
const raycaster = new THREE.Raycaster(); const x = e.offsetX;
const y = e.offsetY;
const x = e.offsetX; const mouse = new THREE.Vector2();
const y = e.offsetY; mouse.x = (x / canvas.offsetWidth) * 2 - 1;
const mouse = new THREE.Vector2(); mouse.y = -(y / canvas.offsetHeight) * 2 + 1;
mouse.x = (x / canvas.offsetWidth) * 2 - 1;
mouse.y = -(y / canvas.offsetHeight) * 2 + 1; raycaster.setFromCamera(mouse, window.viewer.camera);
raycaster.firstHitOnly = true;
raycaster.setFromCamera(mouse, window.viewer.camera);
raycaster.firstHitOnly = true; const intersectObjects:THREE.Object3D[] = [MenuOperation.Roaming.group];
if(window.viewer.modules.tilesManage.mergeMesh){
const intersectObjects:THREE.Object3D[] = [MenuOperation.Roaming.group]; intersectObjects.push(window.viewer.modules.tilesManage.mergeMesh);
if(window.viewer.modules.tilesManage.mergeMesh){ }
intersectObjects.push(window.viewer.modules.tilesManage.mergeMesh); let intersects = raycaster.intersectObjects(intersectObjects, true) || [];
} if (intersects && intersects.length > 0) {
let intersects = raycaster.intersectObjects(intersectObjects, true) || []; const intersect = intersects[0];
if (intersects && intersects.length > 0) {
const intersect = intersects[0]; // 锁定鼠标指针
window.viewer.modules.controls.lockPointer();
// 锁定鼠标指针
window.viewer.modules.controls.lockPointer(); canvas.removeEventListener("pointerdown", roamPdFn);
roamPdFn = undefined;
canvas.removeEventListener("pointerdown", roamPdFn);
roamPdFn = undefined; window.viewer.modules.controls.getTarget(MenuOperation.lastRoadCameraTarget);
window.viewer.modules.controls.getPosition(MenuOperation.lastRoadCameraPos);
window.viewer.modules.controls.getTarget(MenuOperation.lastRoadCameraTarget);
window.viewer.modules.controls.getPosition(MenuOperation.lastRoadCameraPos); const point = new THREE.Vector3(intersect.point.x, intersect.point.y + 2, intersect.point.z);
MenuOperation.Roaming.playerInitPos.copy(point);
const point = new THREE.Vector3(intersect.point.x, intersect.point.y + 2, intersect.point.z);
MenuOperation.Roaming.playerInitPos.copy(point); MenuOperation.Roaming.startRoaming();
MenuOperation.Roaming.startRoaming(); // 第三人称
window.viewer.modules.controls.maxPolarAngle = Math.PI / 2;
// 第三人称 window.viewer.modules.controls.minDistance = 0.8;
window.viewer.modules.controls.maxPolarAngle = Math.PI / 2; window.viewer.modules.controls.maxDistance = 0.8;
window.viewer.modules.controls.minDistance = 0.8; window.viewer.modules.controls.distance = 0.8;
window.viewer.modules.controls.maxDistance = 0.8;
window.viewer.modules.controls.distance = 0.8; Hooks.useDispatchSignal("sceneGraphChanged");
}
Hooks.useDispatchSignal("sceneGraphChanged"); return null;
} }
return null;
} pointerlockFn = () => {
if (document.pointerLockElement) {
pointerlockFn = () => { // console.log("指针被锁定到:", document.pointerLockElement);
if (document.pointerLockElement) { } else {
// console.log("指针锁定到:", document.pointerLockElement); // console.log("指针锁定状态现已解锁");
} else { MenuOperation.roaming();
// console.log("指针锁定状态现已解锁"); }
MenuOperation.roaming(); }
} // 监听鼠标锁定取消
} document.addEventListener("pointerlockchange", pointerlockFn);
// 监听鼠标锁定取消
document.addEventListener("pointerlockchange", pointerlockFn); // 监听选取初始位置
roamPdFn = handlePointerDown.bind(this);
// 监听选取初始位置 canvas.addEventListener("pointerdown", roamPdFn);
roamPdFn = handlePointerDown.bind(this); }
canvas.addEventListener("pointerdown", roamPdFn);
} // 退出漫游
static leaveRoaming() {
// 退出漫游 MenuOperation.Roaming.exitRoaming(MenuOperation.lastRoadCameraPos,MenuOperation.lastRoadCameraTarget);
static leaveRoaming() {
MenuOperation.Roaming.exitRoaming(MenuOperation.lastRoadCameraPos,MenuOperation.lastRoadCameraTarget); if (roamPdFn) {
window.viewer.renderer.domElement.removeEventListener("pointerdown", roamPdFn);
if (roamPdFn) { roamPdFn = null;
window.viewer.renderer.domElement.removeEventListener("pointerdown", roamPdFn); }
roamPdFn = null;
} if (pointerlockFn) {
document.removeEventListener("pointerlockchange", pointerlockFn);
if (pointerlockFn) { pointerlockFn = null;
document.removeEventListener("pointerlockchange", pointerlockFn); }
pointerlockFn = null; }
}
} // 小地图
static miniMap() {
// 小地图 if (MenuOperation.MiniMap.isShow) {
static miniMap() { operationStore.menuList.miniMap.active = false;
if (MenuOperation.MiniMap.isShow) { MenuOperation.MiniMap.close();
operationStore.menuList.miniMap.active = false; } else {
MenuOperation.MiniMap.close(); operationStore.menuList.miniMap.active = true;
} else { MenuOperation.MiniMap.open();
operationStore.menuList.miniMap.active = true; }
MenuOperation.MiniMap.open(); }
}
} // 设置
static settings(){
// 设置 operationStore.menuList.settings.active = !operationStore.menuList.settings.active;
static settings(){ }
operationStore.menuList.settings.active = !operationStore.menuList.settings.active;
} static fullscreen() {
operationStore.menuList.fullscreen.show = false;
static fullscreen() { operationStore.menuList.exitFullscreen.show = true;
operationStore.menuList.fullscreen.show = false;
operationStore.menuList.exitFullscreen.show = true; enterFullscreen();
}
enterFullscreen();
} static exitFullscreen() {
operationStore.menuList.fullscreen.show = true;
static exitFullscreen() { operationStore.menuList.exitFullscreen.show = false;
operationStore.menuList.fullscreen.show = true;
operationStore.menuList.exitFullscreen.show = false; exitFullscreen();
}
exitFullscreen();
}
} }

View File

@@ -343,7 +343,7 @@ const allList = {
image: "/static/resource/billboard/icon/no-entry.svg", image: "/static/resource/billboard/icon/no-entry.svg",
name: cpt("extra.resource.billboard.No entry") name: cpt("extra.resource.billboard.No entry")
}, },
{key: "clown", image: "/static/resource/billboard/icon/clown.svg", name: cpt("ErSan")}, {key: "clown", image: "/static/resource/billboard/icon/clown.svg", name: cpt("")},
], ],
"text": [ "text": [
{ {

View File

@@ -2,4 +2,4 @@
VITE_PORT=8091 VITE_PORT=8091
# 作者 # 作者
VITE_GLOB_AUTHOR='ErSan' VITE_GLOB_AUTHOR=''

View File

@@ -16,4 +16,4 @@ VITE_ENABLE_ANALYZE=false
VITE_ENABLE_CONFIG_GENERATE=false VITE_ENABLE_CONFIG_GENERATE=false
# 当前站点URL # 当前站点URL
VITE_GLOB_ORIGIN=https://editor.astraljs.com/ VITE_GLOB_ORIGIN=

View File

@@ -1,7 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/4/22 10:16
* @description 静态类,全局场景管理 * @description 静态类,全局场景管理
*/ */
import * as THREE from 'three'; import * as THREE from 'three';

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/4/26 13:59 * @date 2025/4/26 13:59
* @description 应用的全局配置,会存储在本地缓存 * @description 应用的全局配置,会存储在本地缓存
*/ */

View File

@@ -1,7 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/4/26 13:59
* @description 当前项目相关信息 * @description 当前项目相关信息
*/ */
import * as THREE from 'three'; import * as THREE from 'three';

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/01/08 * @date 2025/01/08
* @description 从原型链扩充原threejs对象方法 * @description 从原型链扩充原threejs对象方法
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/3/11 10:18 * @date 2024/3/11 10:18
* @description 茶壶几何,修改于 three/examples/jsm/geometries/TeapotGeometry.js * @description 茶壶几何,修改于 three/examples/jsm/geometries/TeapotGeometry.js
*/ */

View File

@@ -1,6 +1,6 @@
/** /**
* THREE JSON 解析器 * THREE JSON 解析器
* @author ErSan * @date 2026/3/13
* @version 2.0.0 * @version 2.0.0
* @description 来自于THREE.ObjectLoader修改了部分代码添加了部分功能 * @description 来自于THREE.ObjectLoader修改了部分代码添加了部分功能
*/ */

View File

@@ -1,7 +1,5 @@
/** /**
* @author MaHaiBing * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/8/3 22:49
* @description 包 骨骼处理 * @description 包 骨骼处理
*/ */
import {Skeleton, Bone, Object3D } from "three"; import {Skeleton, Bone, Object3D } from "three";

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/4/6 12:02 * @date 2025/4/6 12:02
* @description 广告牌对象 * @description 广告牌对象
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/4/10 0:29 * @date 2025/4/10 0:29
* @description html面板 * @description html面板
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/5/21 22:08 * @date 2024/5/21 22:08
* @description 带线框子模型的Mesh对象 * @description 带线框子模型的Mesh对象
*/ */

View File

@@ -1,7 +1,6 @@
/** /**
* 用于代理粒子发射器的空对象,以便于进行场景树显示及控制操作 * 用于代理粒子发射器的空对象,以便于进行场景树显示及控制操作
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025-02-14 16:00:00 * @date 2025-02-14 16:00:00
*/ */
import * as THREE from 'three'; import * as THREE from 'three';

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/4/6 13:07 * @date 2025/4/6 13:07
* @description 广告牌map * @description 广告牌map
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/01/07 * @date 2025/01/07
* @description 贴相机的下雨效果 * @description 贴相机的下雨效果
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/01/08 * @date 2025/01/08
* @description 贴相机的下雪效果 * @description 贴相机的下雪效果
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/9/16 23:18 * @date 2024/9/16 23:18
* @description 在此处注册内置插件 * @description 在此处注册内置插件
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/9/16 2:57 * @date 2024/9/16 2:57
* @description 插件加载 * @description 插件加载
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/07/03 19:38 * @date 2025/07/03 19:38
* @description 点云语义重建 * @description 点云语义重建
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/7/30 18:49 * @date 2025/7/30 18:49
* @description 资源预览类 * @description 资源预览类
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/4/24 10:39 * @date 2024/4/24 10:39
* @description 迁移App.ts中的一些方法也有新增提供脚本使用因为脚本在运行态时执行应该与原环境隔离 * @description 迁移App.ts中的一些方法也有新增提供脚本使用因为脚本在运行态时执行应该与原环境隔离
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/5/18 13:49 * @date 2024/5/18 13:49
* @description 圆网格着色器材质 * @description 圆网格着色器材质
* @from https://www.shadertoy.com/view/7dG3zy * @from https://www.shadertoy.com/view/7dG3zy

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/5/18 13:49 * @date 2024/5/18 13:49
* @description 动态棋盘着色器材质 * @description 动态棋盘着色器材质
* @from https://glslsandbox.com/e#109552.0 * @from https://glslsandbox.com/e#109552.0

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/5/18 13:49 * @date 2024/5/18 13:49
* @description 闪烁着色器材质 * @description 闪烁着色器材质
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/5/18 13:49 * @date 2024/5/18 13:49
* @description 动态烟着色器材质 * @description 动态烟着色器材质
* @from https://glslsandbox.com/e#109550.0 * @from https://glslsandbox.com/e#109550.0

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @description 下雪特效着色器 * @description 下雪特效着色器
*/ */
import { ShaderChunk, ShaderMaterial, Vector2, Vector4, TextureLoader, RepeatWrapping, DoubleSide } from "three"; import { ShaderChunk, ShaderMaterial, Vector2, Vector4, TextureLoader, RepeatWrapping, DoubleSide } from "three";

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/5/18 13:49 * @date 2024/5/18 13:49
* @description 流光墙着色器材质 * @description 流光墙着色器材质
* @from http://t.csdnimg.cn/zSxq0 * @from http://t.csdnimg.cn/zSxq0

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/08/28 * @date 2024/08/28
* @description 漫游类使用BVH检测碰撞,人物模型必须包含动画Enter,Idle, Walking, WalkingBackward,Jumping * @description 漫游类使用BVH检测碰撞,人物模型必须包含动画Enter,Idle, Walking, WalkingBackward,Jumping
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/8/29 22:10 * @date 2024/8/29 22:10
* @description 漫游的人物动画状态机 * @description 漫游的人物动画状态机
*/ */

View File

@@ -3,8 +3,7 @@ import CameraControls from "camera-controls";
import Viewer from "../Viewer" import Viewer from "../Viewer"
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/10/21 21:16 * @date 2024/10/21 21:16
* @description 场景相机管理器。 TODO: 后续应把viewport.camera也管理进来 * @description 场景相机管理器。 TODO: 后续应把viewport.camera也管理进来
*/ */

View File

@@ -1,6 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/10/26 18:27 * @date 2024/10/26 18:27
* @description 后处理 * @description 后处理
*/ */

View File

@@ -1,7 +1,5 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2025/9/23 21:31
* @description 3d tiles加载管理 * @description 3d tiles加载管理
*/ */
import {TilesRenderer} from "3d-tiles-renderer"; import {TilesRenderer} from "3d-tiles-renderer";

View File

@@ -1,7 +1,5 @@
/* /*
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com
* @date 2024/12/23 19:27
* @description 天气系统 * @description 天气系统
*/ */
import * as THREE from "three"; import * as THREE from "three";

View File

@@ -1,120 +1,118 @@
/** /**
* @author ErSan * @date 2026/3/13
* @email mlt131220@163.com * @description 日志记录器
* @date 2025/2/24 下午2:21 */
* @description 日志记录器 import {useAddSignal, useDispatchSignal,useRemoveSignal} from '@/hooks';
*/
import {useAddSignal, useDispatchSignal,useRemoveSignal} from '@/hooks'; export interface ILog {
id: number;
export interface ILog { message: string;
id: number; time: string;
message: string; level: string;
time: string; }
level: string;
} let _delLogFn,_clearLogFn,_historyChangedFn;
class Logger {
let _delLogFn,_clearLogFn,_historyChangedFn; static Enum = Object.freeze({
class Logger { TRACE: "trace",
static Enum = Object.freeze({ DEBUG: "debug",
TRACE: "trace", INFO: "info",
DEBUG: "debug", WARN: "warn",
INFO: "info", ERROR: "error"
WARN: "warn", });
ERROR: "error"
}); // 是否启用日志
enabled: boolean = true;
// 是否启用日志
enabled: boolean = true; // 日志信息
logs: ILog[] = [];
// 日志信息
logs: ILog[] = []; constructor() {
_delLogFn = this.delLog.bind(this);
constructor() { useAddSignal("deleteLog",_delLogFn);
_delLogFn = this.delLog.bind(this); _clearLogFn = this.clearLogs.bind(this);
useAddSignal("deleteLog",_delLogFn); useAddSignal("clearLogs",_clearLogFn);
_clearLogFn = this.clearLogs.bind(this); _historyChangedFn = this.historyChanged.bind(this);
useAddSignal("clearLogs",_clearLogFn); useAddSignal("historyChanged",_historyChangedFn);
_historyChangedFn = this.historyChanged.bind(this); }
useAddSignal("historyChanged",_historyChangedFn);
} log(methodName:string,message:string){
if(!this.enabled) return;
log(methodName:string,message:string){
if(!this.enabled) return; const _log = {
id: this.logs.length,
const _log = { message,
id: this.logs.length, level: methodName,
message, time: new Date().toLocaleString()
level: methodName, }
time: new Date().toLocaleString()
} this.logs.unshift(_log);
this.logs.unshift(_log); useDispatchSignal("addLog", _log, this.logs);
}
useDispatchSignal("addLog", _log, this.logs);
} trace(message:string) { this.log(Logger.Enum.TRACE, message); }
debug(message:string) { this.log(Logger.Enum.DEBUG, message); }
trace(message:string) { this.log(Logger.Enum.TRACE, message); } info(message:string) { this.log(Logger.Enum.INFO, message); }
debug(message:string) { this.log(Logger.Enum.DEBUG, message); } warn(message:string) { this.log(Logger.Enum.WARN, message); }
info(message:string) { this.log(Logger.Enum.INFO, message); } error(message:string) { this.log(Logger.Enum.ERROR, message); }
warn(message:string) { this.log(Logger.Enum.WARN, message); }
error(message:string) { this.log(Logger.Enum.ERROR, message); } /**
* 删除日志
/** * @param _log
* 删除日志 */
* @param _log delLog(_log: ILog) {
*/ this.logs = this.logs.filter(log => log.id!== _log.id);
delLog(_log: ILog) { }
this.logs = this.logs.filter(log => log.id!== _log.id);
} /**
* 清空日志
/** */
* 清空日志 clearLogs() {
*/ this.logs = [];
clearLogs() { }
this.logs = [];
} /**
* 历史记录变化回调
/** * @param cmd
* 历史记录变化回调 */
* @param cmd historyChanged(cmd){
*/ if(!cmd?.name) return;
historyChanged(cmd){
if(!cmd?.name) return; let msg = cmd.name;
const postposition = ['AddObjectCommand','RemoveObjectCommand','MoveObjectCommand'];
let msg = cmd.name; if(postposition.includes(cmd.type)){
const postposition = ['AddObjectCommand','RemoveObjectCommand','MoveObjectCommand']; msg = `${msg}: ${cmd.object.name} `;
if(postposition.includes(cmd.type)){ }else if(cmd.object){
msg = `${msg}: ${cmd.object.name} `; msg = `${cmd.object.name} ${msg.toLowerCase()}`;
}else if(cmd.object){ }
msg = `${cmd.object.name} ${msg.toLowerCase()}`;
} if(cmd.newValue !== undefined && cmd.oldValue !== undefined){
let newValue = cmd.newValue;
if(cmd.newValue !== undefined && cmd.oldValue !== undefined){ let oldValue = cmd.oldValue;
let newValue = cmd.newValue; if(typeof newValue === 'object'){
let oldValue = cmd.oldValue; newValue = JSON.stringify(newValue);
if(typeof newValue === 'object'){ }
newValue = JSON.stringify(newValue); if(typeof oldValue === 'object'){
} oldValue = JSON.stringify(oldValue);
if(typeof oldValue === 'object'){ }
oldValue = JSON.stringify(oldValue);
} msg = `${msg}: ${oldValue}${newValue}`;
}
msg = `${msg}: ${oldValue}${newValue}`;
} this.info(msg);
}
this.info(msg);
} dispose() {
useRemoveSignal("deleteLog",_delLogFn)
dispose() { _delLogFn = null;
useRemoveSignal("deleteLog",_delLogFn) useRemoveSignal("clearLogs",_clearLogFn)
_delLogFn = null; _clearLogFn = null;
useRemoveSignal("clearLogs",_clearLogFn) useRemoveSignal("historyChanged",_historyChangedFn)
_clearLogFn = null; _historyChangedFn = null;
useRemoveSignal("historyChanged",_historyChangedFn) }
_historyChangedFn = null; }
}
} export const logger = new Logger();
export const logger = new Logger();
export default logger; export default logger;

339
README.md
View File

@@ -1,170 +1,169 @@
# Astral3D 项目说明 # Astral3D 项目说明
## 项目简介 ## 项目简介
Astral3D 是一个基于 Vue3 + Three.js 的现代 Web 3D 编辑器,同时包含配套的 Java 后端服务。项目旨在提供一站式 3D 场景创建、管理和展示解决方案。 Astral3D 是一个基于 Vue3 + Three.js 的现代 Web 3D 编辑器,同时包含配套的 Java 后端服务。项目旨在提供一站式 3D 场景创建、管理和展示解决方案。
**在线演示**: [Astral 3D Editor](https://editor.astraljs.com) **在线演示**: [Astral 3D Editor](https://editor.astraljs.com)
--- ---
## 项目结构 ## 项目结构
``` ```
Astral3D-Projects/ Astral3D-Projects/
├── Astral3D/ # 前端编辑器项目 (Vue3 + Three.js) ├── Astral3D/ # 前端编辑器项目 (Vue3 + Three.js)
│ ├── packages/ # 子包 │ ├── packages/ # 子包
│ ├── common/ # 公共模块 │ ├── common/ # 公共模块
│ └── node_modules/ # 依赖 │ └── node_modules/ # 依赖
├── astral-service/ # 后端服务项目 (Java + SpringBoot) ├── astral-service/ # 后端服务项目 (Java + SpringBoot)
│ ├── astral-common/ # 公共模块 │ ├── astral-common/ # 公共模块
│ ├── astral-core/ # 核心配置 │ ├── astral-core/ # 核心配置
│ ├── astral-business/ # 业务模块 │ ├── astral-business/ # 业务模块
│ ├── astral-system/ # 系统模块 │ ├── astral-system/ # 系统模块
│ ├── astral-web/ # Web 层 │ ├── astral-web/ # Web 层
│ └── static/ # 静态资源 │ └── static/ # 静态资源
└── docs/ # 文档目录 └── docs/ # 文档目录
``` ```
--- ---
## 技术栈 ## 技术栈
### 前端 (Astral3D) ### 前端 (Astral3D)
| 技术 | 版本 | 说明 | | 技术 | 版本 | 说明 |
|------|------|------| |------|------|------|
| Vue | 3.5.22 | 渐进式前端框架 | | Vue | 3.5.22 | 渐进式前端框架 |
| Three.js | r176 | 3D 渲染引擎 | | Three.js | r176 | 3D 渲染引擎 |
| Cesium | 1.107.0 | 地理信息系统 | | Cesium | 1.107.0 | 地理信息系统 |
| Naive UI | 2.43.1 | UI 组件库 | | Naive UI | 2.43.1 | UI 组件库 |
| UnoCSS | 0.46.5 | 原子化 CSS | | UnoCSS | 0.46.5 | 原子化 CSS |
### 后端 (AstralService) ### 后端 (AstralService)
| 技术 | 版本 | 说明 | | 技术 | 版本 | 说明 |
|------|------|------| |------|------|------|
| Java | 8 | 编程语言 | | Java | 8 | 编程语言 |
| Spring Boot | 2.7.18 | Web 框架 | | Spring Boot | 2.7.18 | Web 框架 |
| MyBatis Plus | - | ORM 框架 | | MyBatis Plus | - | ORM 框架 |
| MySQL | - | 数据库 | | MySQL | - | 数据库 |
| WebSocket | - | 实时通信 | | WebSocket | - | 实时通信 |
--- ---
## 核心功能 ## 核心功能
### 前端功能 ### 前端功能
- **多格式模型支持**: 30+ 模型格式GLTF/OBJ/FBX/GLB/RVT/IFC 等) - **多格式模型支持**: 30+ 模型格式GLTF/OBJ/FBX/GLB/RVT/IFC 等)
- **BIM 模型轻量化**: RVT/IFC 文件浏览器端展示 - **BIM 模型轻量化**: RVT/IFC 文件浏览器端展示
- **CAD 图纸解析**: DWG/DXF 文件解析和展示 - **CAD 图纸解析**: DWG/DXF 文件解析和展示
- **场景管理**: 分包存储与动态加载 - **场景管理**: 分包存储与动态加载
- **动画编辑器**: 内置动画制作工具 - **动画编辑器**: 内置动画制作工具
- **插件系统**: 扩展能力支持 - **插件系统**: 扩展能力支持
- **脚本运行时**: 脚本化业务逻辑 - **脚本运行时**: 脚本化业务逻辑
- **粒子系统**: 视觉效果增强 - **粒子系统**: 视觉效果增强
- **天气系统**: 场景环境模拟 - **天气系统**: 场景环境模拟
- **云存储集成**: 又拍云 USS 支持 - **云存储集成**: 又拍云 USS 支持
- **资源中心**: 统一资源管理 - **资源中心**: 统一资源管理
### 后端功能 ### 后端功能
- **用户认证**: 用户注册登录 - **用户认证**: 用户注册登录
- **场景管理**: 场景 CRUD 操作 - **场景管理**: 场景 CRUD 操作
- **资产管理**: 资源上传、存储、加载 - **资产管理**: 资源上传、存储、加载
- **文件转换**: CAD DWG 转 DXF - **文件转换**: CAD DWG 转 DXF
- **WebSocket**: 实时通信支持 - **WebSocket**: 实时通信支持
--- ---
## 快速开始 ## 快速开始
### 前端开发 ### 前端开发
```bash ```bash
# 进入前端项目 # 进入前端项目
cd Astral3D cd Astral3D
# 使用 nvm 切换 Node 版本 # 使用 nvm 切换 Node 版本
nvm use 23.11.0 nvm use 23.11.0
# 安装依赖 # 安装依赖
pnpm install pnpm install
# 构建 SDK # 构建 SDK
pnpm run sdk:build pnpm run sdk:build
# 启动开发服务器 # 启动开发服务器
pnpm run editor:dev pnpm run editor:dev
``` ```
### 后端服务 ### 后端服务
```bash ```bash
# 进入后端项目 # 进入后端项目
cd astral-service cd astral-service
# 配置数据库 # 配置数据库
# 修改 astral-web/src/main/resources/application-dev.yml # 修改 astral-web/src/main/resources/application-dev.yml
# 方式一IDEA 直接运行 # 方式一IDEA 直接运行
# 运行 AstralWebApplication 类 # 运行 AstralWebApplication 类
# 方式二Maven 运行 # 方式二Maven 运行
mvn spring-boot:run mvn spring-boot:run
# 方式三:打包运行 # 方式三:打包运行
mvn clean package mvn clean package
cd astral-web/target cd astral-web/target
java -jar astral-web-exec.jar java -jar astral-web-exec.jar
``` ```
--- ---
## 数据库配置 ## 数据库配置
1. 创建 MySQL 数据库 1. 创建 MySQL 数据库
2. 导入表结构:`static/sql/astral_3d.sql` 2. 导入表结构:`static/sql/astral_3d.sql`
3. 修改配置文件中的数据库连接信息 3. 修改配置文件中的数据库连接信息
--- ---
## 文件存储配置 ## 文件存储配置
支持两种存储方式: 支持两种存储方式:
- **本地存储 (local)**: 保存在项目 static 文件夹 - **本地存储 (local)**: 保存在项目 static 文件夹
- **又拍云存储 (upyun)**: 云端存储 - **又拍云存储 (upyun)**: 云端存储
--- ---
## 文档目录 ## 文档目录
| 文档 | 说明 | | 文档 | 说明 |
|------|------| |------|------|
| [GIT_TUTORIAL.md](../GIT_TUTORIAL.md) | Git 操作教程 | | [二开改造方案.md](docs/二开改造方案.md) | 二次开发视觉改造方案(新品牌 Logo、配色、UI 风格) |
| [digital-atelier-philosophy.md](docs/digital-atelier-philosophy.md) | 设计哲学Digital Atelier 理念 |
--- | [ui-migration-todo.md](docs/ui-migration-todo.md) | UI 迁移待办NaiveUI → Shadcn UI |
| [css-migration-spec.md](docs/css-migration-spec.md) | CSS 样式迁移规范 |
## 许可证 | [GIT_TUTORIAL.md](docs/GIT_TUTORIAL.md) | Git 操作教程 |
本项目基于 Apache 2.0 许可证开源,详细内容请参阅 [LICENSE](../Astral3D/LICENSE) 文件。 ---
--- ## 许可证
## 相关链接 本项目基于 Apache 2.0 许可证开源,详细内容请参阅 [LICENSE](../Astral3D/LICENSE) 文件。
- [在线演示](https://editor.astraljs.com) ---
- [文档中心](http://editor-doc.astraljs.com/)
- [后端仓库](https://github.com/yx8663/astral-service) ## 相关链接
- [前端仓库](https://github.com/mlt131220/Astral3D)
- [在线演示](https://editor.astraljs.com)
--- - [文档中心](http://editor-doc.astraljs.com/)
- [代码仓库](http://code-server:3000/lyf/Astral3D.git)
## 交流社区
---
- QQ 交流群1040320579
- 微信:联系开发者(仅限商务)

View File

@@ -1,50 +1,77 @@
# AstralService # AstralService
🌍 🌍 *[简体中文](README.md)* | English
*[简体中文](README.md)*
Backend service for NeonVision 3D Editor.
基于`Java:8``SpringBoot:2.7.18`的 [Astral3D](https://github.com/mlt131220/Astral3D) 项目后端代码.
Based on `Java 8` and `SpringBoot 2.7.18`.
![Static Badge](https://img.shields.io/badge/Java-8-green)
![Static Badge](https://img.shields.io/badge/SpringBoot-2.7.18-8732D7) ![Static Badge](https://img.shields.io/badge/Java-8-green)
![Static Badge](https://img.shields.io/badge/license-MIT-blue) ![Static Badge](https://img.shields.io/badge/SpringBoot-2.7.18-8732D7)
![Static Badge](https://img.shields.io/badge/license-MIT-blue)
## 快速开始
```shell ## Prerequisites
git clone https://github.com/yx8663/astral-service.git
``` - Java 8+
### 配置 - Maven 3.6+
* 数据库使用 MySQL表结构数据文件位于 `static/sql/astral_3d.sql` - MySQL 5.7+
* 配置文件路径: `astral-web/src/main/resources/application-dev.yml`;
* 修改配置文件下 `spring.datasource` 项为自己的数据库连接; ## Quick Start
* 修改配置文件下 `astral.uploadType` 项为自己使用的文件存储方式:`本地-local 又拍云-upyun`
* 修改配置文件下 `astral.uploadDir` 项为自己使用的文件存储方式对应的存储目录: ```shell
* `本地-local`存储推荐修改为此项目下的`static`文件夹; git clone http://code-server:3000/lyf/Astral3D.git
* `又拍云-upyun`存储按需配置存储路径; cd astral-service
* 如果使用`又拍云-upyun`存储,还需修改配置文件下 `upyun`项配置: ```
* `bucket` 为对应存储桶名;
* `operator` 为在又拍云为该桶配置的操作员账号; ### Database Setup
* `password` 为对应操作员密码;
* `domain` 为该存储使用的加速域名(为保证又拍云信息不在前端泄露,故不使用在`HTTP Header`/`HTTP Body`签名认证的方式鉴权,而是配置对应存储的加速域名,将前端请求重定向来通过该加速域名获取资源); 1. Create database and import schema:
* 修改配置文件下 `dev` 项配置: ```sql
* `currentAbPath` 为本项目绝对路径地址; mysql -u root -p < static/sql/astral_3d.sql
* `cadDwgConverterAbPath` 为本地CAD Dwg转换器执行程序文件夹绝对路径转换程序使用 libreDWG(已包含在项目static/lib/libredwg文件夹) ```
* `temporaryFolder` 为临时文件夹地址,推荐配置为本项目地址下的`static/tmp`文件夹;
2. Configure database connection in:
## 运行 ```
astral-web/src/main/resources/application-dev.yml
### 方式一 ```
> IDEA中直接运行@SpringBootApplication注解的类的main方法 <br />
> `astral-web/src/main/java/com.astral.web/AstralWebApplication/AstralWebApplication` ### Configuration
### 方式二 Edit `astral-web/src/main/resources/application-dev.yml`:
> cmd中执行命令 `mvn spring-boot:run`
| Property | Description |
### 方式三(打包) |----------|-------------|
> cmd中执行命令 | `spring.datasource` | Your MySQL connection settings |
> * 生成jar包: mvn clean package | `astral.uploadType` | Storage type: `local` or `upyun` |
> * 进入生成的jar包目录cd astral-web/target | `astral.uploadDir` | Storage directory path |
> * 运行项目java -jar astral-web-exec.jar | `upyun.bucket` | UpYun bucket name (if using UpYun) |
| `upyun.operator` | UpYun operator account |
## Star History | `upyun.password` | UpYun operator password |
[![Star History Chart](https://api.star-history.com/svg?repos=yx8663/astral-service&type=Date)](https://star-history.com/#yx8663/astral-service&Date) | `upyun.domain` | UpYun CDN domain |
| `dev.currentAbPath` | Project absolute path |
| `dev.cadDwgConverterAbPath` | CAD DWG converter path (libreDWG) |
| `dev.temporaryFolder` | Temporary folder path |
### Running
**Option 1: IDEA**
Run the main class:
```
astral-web/src/main/java/com.astral.web/AstralWebApplication/AstralWebApplication
```
**Option 2: Maven**
```bash
mvn spring-boot:run
```
**Option 3: JAR Package**
```bash
mvn clean package
cd astral-web/target
java -jar astral-web-exec.jar
```
## License
Open source under MIT License.