完善项目文档和SDK功能
- 更新 README 文档和许可证文件 - 完善编辑器使用手册,包括BIM浏览、CAD查看等功能说明 - 更新文档配置和首页内容 - 优化编辑器环境配置 - 完善 WebSocket 通信和 API 接口 - 增强 glTF 处理器和点云重建功能 - 优化 SDK 核心模块: - 更新 App 和 Config 配置 - 添加 Billboard、HtmlPanel 等对象支持 - 完善粒子系统和天气效果 - 优化相机管理和漫游工具 - 改进材质着色器效果 - 更新服务层 README 文档 Made-with: Cursor
This commit is contained in:
@@ -36,7 +36,7 @@
|
|||||||
- 暗示与官方项目的关联性
|
- 暗示与官方项目的关联性
|
||||||
|
|
||||||
## ©️ 版权声明
|
## ©️ 版权声明
|
||||||
2019-2025 © ErSan<mlt131220@163.com>. All rights reserved.
|
Copyright © 2026 . All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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。
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ permalink: /astral/ib77r5fd/
|
|||||||
|
|
||||||
{width="618" height="616" style="width: 200px"}
|
{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
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -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=''
|
||||||
@@ -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=
|
||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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; // 如果任务失败,停止执行后续任务
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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 { }
|
|
||||||
}
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
@@ -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";
|
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
VITE_PORT=8091
|
VITE_PORT=8091
|
||||||
|
|
||||||
# 作者
|
# 作者
|
||||||
VITE_GLOB_AUTHOR='ErSan'
|
VITE_GLOB_AUTHOR=''
|
||||||
@@ -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=
|
||||||
@@ -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';
|
||||||
|
|||||||
@@ -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 应用的全局配置,会存储在本地缓存
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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对象方法
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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,修改了部分代码,添加了部分功能
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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 广告牌对象
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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面板
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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对象
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @author ErSan
|
* @date 2026/3/13
|
||||||
* @email mlt131220@163.com
|
|
||||||
* @date 2025/01/07
|
* @date 2025/01/07
|
||||||
* @description 贴相机的下雨效果
|
* @description 贴相机的下雨效果
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @author ErSan
|
* @date 2026/3/13
|
||||||
* @email mlt131220@163.com
|
|
||||||
* @date 2025/01/08
|
* @date 2025/01/08
|
||||||
* @description 贴相机的下雪效果
|
* @description 贴相机的下雪效果
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 在此处注册内置插件
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 插件加载
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 点云语义重建
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 资源预览类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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中的一些方法(也有新增)提供脚本使用,因为脚本在运行态时执行,应该与原环境隔离
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 闪烁着色器材质
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 漫游的人物动画状态机
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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也管理进来
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 后处理
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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
339
README.md
@@ -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
|
|
||||||
- 微信:联系开发者(仅限商务)
|
|
||||||
|
|||||||
@@ -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`.
|
||||||

|
|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
## 快速开始
|
|
||||||
```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 |
|
||||||
[](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.
|
||||||
|
|||||||
Reference in New Issue
Block a user