第04章-场景编辑与模型管理

第四章:场景编辑与模型管理

4.1 场景编辑器界面

Astral3D编辑器提供了专业级的场景编辑界面,包含工具栏、侧边栏、视口、属性面板等核心区域。

4.1.1 界面布局

┌─────────────────────────────────────────────────────────────────────┐
│                            工具栏 (Toolbar)                          │
│  [文件] [编辑] [视图] [对象] [工具] [窗口] [帮助]                      │
├──────────────┬─────────────────────────────────┬────────────────────┤
│              │                                 │                    │
│    场景树    │           3D视口                │    属性面板        │
│   (Scene    │         (Viewport)              │   (Properties)     │
│    Tree)    │                                 │                    │
│              │                                 │   ┌──────────────┐ │
│  ├─ 场景    │                                 │   │ 变换属性     │ │
│  │  ├─ 灯光 │        [视图控制工具]            │   ├──────────────┤ │
│  │  ├─ 相机 │                                 │   │ 材质属性     │ │
│  │  └─ 模型 │                                 │   ├──────────────┤ │
│  │     ├─ 建筑│                               │   │ 几何属性     │ │
│  │     └─ 车辆│                               │   └──────────────┘ │
│              │                                 │                    │
├──────────────┴─────────────────────────────────┴────────────────────┤
│                            资源面板 (Assets)                         │
│  [模型库] [材质库] [贴图库] [预制件] [动画]                           │
├─────────────────────────────────────────────────────────────────────┤
│                            状态栏 (Status Bar)                       │
│  FPS: 60 | 对象: 128 | 顶点: 1.2M | 选中: 3个对象                    │
└─────────────────────────────────────────────────────────────────────┘

4.1.2 工具栏功能

功能分组 工具 快捷键 说明
文件 新建场景 Ctrl+N 创建空白场景
文件 打开场景 Ctrl+O 打开已有场景文件
文件 保存场景 Ctrl+S 保存当前场景
文件 导出 Ctrl+E 导出为各种格式
编辑 撤销 Ctrl+Z 撤销上一步操作
编辑 重做 Ctrl+Y 重做撤销的操作
编辑 复制 Ctrl+C 复制选中对象
编辑 粘贴 Ctrl+V 粘贴对象
编辑 删除 Delete 删除选中对象
视图 顶视图 Numpad 7 切换到顶视图
视图 前视图 Numpad 1 切换到前视图
视图 右视图 Numpad 3 切换到右视图
对象 移动 W 激活移动工具
对象 旋转 E 激活旋转工具
对象 缩放 R 激活缩放工具

4.1.3 快捷键大全

通用操作:
  Ctrl+N          新建场景
  Ctrl+O          打开场景
  Ctrl+S          保存场景
  Ctrl+Shift+S    另存为
  Ctrl+Z          撤销
  Ctrl+Y          重做
  Delete          删除选中
  Ctrl+A          全选
  Ctrl+D          取消选择
  Ctrl+C          复制
  Ctrl+V          粘贴
  Ctrl+Shift+D    复制并粘贴

变换工具:
  W               移动工具
  E               旋转工具
  R               缩放工具
  Q               选择工具
  G               抓取移动(实时移动)
  X/Y/Z           限制到对应轴

视图控制:
  鼠标中键拖动    旋转视图
  Shift+中键拖动  平移视图
  滚轮            缩放视图
  F               聚焦到选中对象
  Home            重置视图
  Numpad 0        切换透视/正交
  Numpad 1        前视图
  Numpad 3        右视图
  Numpad 7        顶视图

对象操作:
  H               隐藏选中对象
  Alt+H           显示所有隐藏对象
  L               锁定选中对象
  Alt+L           解锁所有对象
  M               移动到图层
  P               设置父级
  Alt+P           清除父级

4.2 模型导入

4.2.1 支持的导入格式

Astral3D支持30+种3D模型格式的导入:

格式类别 支持格式 推荐程度
通用格式 GLTF/GLB ⭐⭐⭐⭐⭐ 最推荐
通用格式 FBX ⭐⭐⭐⭐
通用格式 OBJ ⭐⭐⭐
通用格式 STL ⭐⭐⭐
通用格式 DAE (Collada) ⭐⭐⭐
通用格式 3DS ⭐⭐
BIM格式 IFC ⭐⭐⭐⭐
BIM格式 RVT (Revit) ⭐⭐⭐⭐
CAD格式 DWG ⭐⭐⭐
CAD格式 DXF ⭐⭐⭐
CAD格式 STEP/STP ⭐⭐⭐⭐
点云格式 PCD ⭐⭐⭐
点云格式 XYZ ⭐⭐⭐

4.2.2 导入操作

方法一:拖拽导入

  1. 打开文件管理器找到模型文件
  2. 直接将文件拖拽到3D视口区域
  3. 等待加载完成

方法二:菜单导入

  1. 点击菜单栏 文件 > 导入
  2. 选择文件格式
  3. 在文件对话框中选择文件
  4. 配置导入选项
  5. 点击确认导入

方法三:资源面板导入

  1. 打开底部资源面板
  2. 点击导入按钮
  3. 选择文件
  4. 拖拽到场景中

4.2.3 导入选项配置

// 通用导入选项
interface ImportOptions {
  // 变换设置
  position: { x: number, y: number, z: number };  // 初始位置
  rotation: { x: number, y: number, z: number };  // 初始旋转(度)
  scale: { x: number, y: number, z: number };     // 初始缩放
  
  // 单位转换
  convertUnits: boolean;        // 是否转换单位
  sourceUnit: 'mm' | 'cm' | 'm' | 'inch' | 'feet';
  targetUnit: 'm';
  
  // 材质设置
  importMaterials: boolean;     // 导入材质
  embedTextures: boolean;       // 嵌入贴图
  
  // 优化设置
  mergeGeometries: boolean;     // 合并几何体
  removeHidden: boolean;        // 移除隐藏对象
  simplify: boolean;            // 简化网格
  simplifyRatio: number;        // 简化比例 (0-1)
  
  // 阴影设置
  castShadow: boolean;          // 投射阴影
  receiveShadow: boolean;       // 接收阴影
}

// BIM导入选项
interface BIMImportOptions extends ImportOptions {
  lightweight: boolean;         // 轻量化处理
  extractProperties: boolean;   // 提取属性信息
  preserveHierarchy: boolean;   // 保留层级结构
  mergeByCategory: boolean;     // 按类别合并
  lodLevels: number[];          // LOD级别
}

// CAD导入选项
interface CADImportOptions extends ImportOptions {
  importLayers: boolean;        // 导入图层
  layerToGroup: boolean;        // 图层转换为组
  importDimensions: boolean;    // 导入尺寸标注
  convertTo3D: boolean;         // 转换为3D
  extrudeHeight: number;        // 拉伸高度
}

4.2.4 大模型导入优化

对于大型模型,建议进行以下优化:

// 大型模型导入配置示例
const largeModelOptions = {
  // 启用轻量化
  lightweight: true,
  
  // 合并相同材质的几何体
  mergeGeometries: true,
  
  // 简化网格
  simplify: true,
  simplifyRatio: 0.5,  // 保留50%的面数
  
  // 生成LOD
  generateLOD: true,
  lodLevels: [
    { distance: 50, ratio: 1.0 },    // 50米内显示原始
    { distance: 100, ratio: 0.5 },   // 50-100米显示50%
    { distance: 200, ratio: 0.2 },   // 100-200米显示20%
    { distance: 500, ratio: 0.05 }   // 200米外显示5%
  ],
  
  // 禁用不必要的功能
  importMaterials: false,  // 稍后单独设置材质
  castShadow: false,       // 稍后启用阴影
};

4.3 对象变换

4.3.1 变换工具

移动工具 (W)

  • 功能:在3D空间中移动对象
  • 操作:拖动箭头沿单轴移动,拖动平面沿双轴移动
  • 约束:按住Shift键进行精确移动
      Y
      ↑
      │    移动Gizmo
      │   ╱
      │  ╱
      │ ╱
      │╱─────────→ X
     ╱
    ╱
   ↓
   Z

旋转工具 (E)

  • 功能:绕轴旋转对象
  • 操作:拖动圆环绕对应轴旋转
  • 约束:按住Ctrl键以15度为单位旋转

缩放工具 (R)

  • 功能:调整对象大小
  • 操作:拖动方块沿单轴缩放,拖动中心均匀缩放
  • 约束:按住Shift保持比例缩放

4.3.2 变换坐标系

坐标系 说明 适用场景
世界坐标 (World) 使用场景的全局坐标轴 对齐到场景方向
本地坐标 (Local) 使用对象自身的坐标轴 沿对象方向移动
视图坐标 (View) 使用当前视图的坐标轴 屏幕空间操作
父级坐标 (Parent) 使用父对象的坐标轴 相对父级操作

4.3.3 精确变换

通过属性面板可以进行精确的数值变换:

// 位置设置
object.position.set(10, 0, 5);  // X=10, Y=0, Z=5

// 旋转设置(弧度)
object.rotation.set(0, Math.PI / 2, 0);  // Y轴旋转90度

// 旋转设置(角度)
object.rotation.y = THREE.MathUtils.degToRad(45);  // Y轴旋转45度

// 缩放设置
object.scale.set(2, 2, 2);  // 均匀放大2倍
object.scale.set(1, 2, 1);  // 只在Y轴方向放大2倍

4.3.4 变换枢轴点

枢轴点是变换操作的中心点:

枢轴模式 说明
边界框中心 对象边界框的几何中心
原点 对象的本地原点
底部中心 边界框底面的中心
顶点 选中的顶点位置
3D游标 场景中的3D游标位置

4.4 层级管理

4.4.1 父子关系

// 设置父级
child.parent = parent;
parent.add(child);

// 清除父级
parent.remove(child);
scene.add(child);  // 添加到场景根节点

// 获取层级信息
const parentObj = object.parent;
const children = object.children;
const ancestors = [];
let current = object.parent;
while (current) {
  ancestors.push(current);
  current = current.parent;
}

4.4.2 场景树操作

通过界面操作:

  1. 拖拽排序:在场景树中拖动节点改变顺序
  2. 拖拽到父级:将节点拖到另一个节点上建立父子关系
  3. 拖出父级:将节点拖到根节点区域清除父级关系
  4. 右键菜单
    • 重命名
    • 复制/粘贴
    • 删除
    • 显示/隐藏
    • 锁定/解锁
    • 创建分组
    • 解散分组

4.4.3 分组管理

// 创建分组
const group = new THREE.Group();
group.name = '建筑群';
scene.add(group);

// 将对象添加到分组
building1.parent?.remove(building1);
building2.parent?.remove(building2);
group.add(building1);
group.add(building2);

// 解散分组
group.children.forEach(child => {
  group.remove(child);
  group.parent.add(child);
});
group.parent.remove(group);

// 分组批量操作
group.visible = false;  // 隐藏整个分组
group.traverse(child => {
  if (child instanceof THREE.Mesh) {
    child.castShadow = true;
  }
});

4.5 对象属性

4.5.1 基础属性

属性 类型 说明
name string 对象名称
uuid string 唯一标识(只读)
type string 对象类型(只读)
visible boolean 是否可见
castShadow boolean 是否投射阴影
receiveShadow boolean 是否接收阴影
frustumCulled boolean 是否进行视锥剔除
renderOrder number 渲染顺序

4.5.2 变换属性

属性 类型 说明
position Vector3 位置 (x, y, z)
rotation Euler 旋转 (x, y, z) 弧度
scale Vector3 缩放 (x, y, z)
quaternion Quaternion 四元数旋转
matrix Matrix4 本地变换矩阵
matrixWorld Matrix4 世界变换矩阵

4.5.3 网格属性(Mesh)

属性 类型 说明
geometry BufferGeometry 几何体
material Material 材质
morphTargetInfluences number[] 形变目标权重

4.5.4 用户数据

// 存储自定义数据
object.userData = {
  // 业务数据
  buildingId: 'B001',
  floor: 3,
  area: 1500,
  
  // 状态数据
  isSelected: false,
  isHighlighted: false,
  
  // 元数据
  source: 'BIM导入',
  importTime: Date.now(),
  category: '办公楼'
};

// 读取数据
const buildingId = object.userData.buildingId;

// 遍历查找
scene.traverse(obj => {
  if (obj.userData.category === '办公楼') {
    console.log(obj.name, obj.userData.buildingId);
  }
});

4.6 场景导出

4.6.1 支持的导出格式

格式 说明 用途
GLTF glTF 2.0格式 通用3D交换格式
GLB glTF二进制格式 单文件分发
OBJ Wavefront OBJ 通用模型格式
STL STL格式 3D打印
DAE Collada格式 动画交换
FBX FBX格式 游戏引擎
JSON Three.js JSON 场景序列化

4.6.2 导出选项

interface ExportOptions {
  // 通用选项
  format: 'gltf' | 'glb' | 'obj' | 'stl' | 'fbx';
  filename: string;
  
  // 内容选项
  exportSelected: boolean;    // 仅导出选中
  exportVisible: boolean;     // 仅导出可见
  includeAnimations: boolean; // 包含动画
  includeLights: boolean;     // 包含灯光
  includeCameras: boolean;    // 包含相机
  
  // 优化选项
  embedTextures: boolean;     // 嵌入贴图
  optimizeTextures: boolean;  // 优化贴图尺寸
  maxTextureSize: number;     // 最大贴图尺寸
  draco: boolean;             // Draco压缩 (仅GLTF)
  
  // 变换选项
  applyTransforms: boolean;   // 应用变换到几何体
  centerOrigin: boolean;      // 居中原点
}

4.6.3 导出示例

导出为GLB(推荐):

import { GLTFExporter } from '@astral3d/engine';

const exporter = new GLTFExporter();

// 导出整个场景
exporter.export(viewer.scene, {
  format: 'glb',
  filename: 'scene.glb',
  embedTextures: true,
  draco: true
}).then(blob => {
  // 下载文件
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = 'scene.glb';
  link.click();
});

// 导出选中对象
const selected = viewer.selection.getSelected();
exporter.export(selected, {
  format: 'glb',
  filename: 'selected.glb'
});

4.6.4 场景序列化

// 序列化场景
const sceneData = viewer.serialize();

// 保存到本地存储
localStorage.setItem('scene', JSON.stringify(sceneData));

// 保存到服务器
fetch('/api/scenes', {
  method: 'POST',
  body: JSON.stringify(sceneData),
  headers: { 'Content-Type': 'application/json' }
});

// 反序列化加载
const savedData = JSON.parse(localStorage.getItem('scene'));
viewer.deserialize(savedData);

4.7 对象复制与实例化

4.7.1 普通复制

// 深度复制(复制几何体和材质)
const clone = object.clone();
clone.position.x += 10;
scene.add(clone);

// 浅复制(共享几何体和材质)
const shallowClone = object.clone(false);

4.7.2 实例化渲染

对于大量相同对象,使用实例化渲染可以显著提高性能:

import * as THREE from 'three';

// 创建实例化网格
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const count = 1000;  // 实例数量

const instancedMesh = new THREE.InstancedMesh(geometry, material, count);

// 设置每个实例的变换
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
  dummy.position.set(
    Math.random() * 100 - 50,
    Math.random() * 100 - 50,
    Math.random() * 100 - 50
  );
  dummy.rotation.set(
    Math.random() * Math.PI,
    Math.random() * Math.PI,
    Math.random() * Math.PI
  );
  dummy.scale.setScalar(Math.random() * 0.5 + 0.5);
  dummy.updateMatrix();
  instancedMesh.setMatrixAt(i, dummy.matrix);
}

instancedMesh.instanceMatrix.needsUpdate = true;
scene.add(instancedMesh);

4.7.3 阵列复制

// 线性阵列
function linearArray(object, count, spacing) {
  const group = new THREE.Group();
  group.name = `${object.name}_Array`;
  
  for (let i = 0; i < count; i++) {
    const clone = object.clone();
    clone.position.x += i * spacing.x;
    clone.position.y += i * spacing.y;
    clone.position.z += i * spacing.z;
    group.add(clone);
  }
  
  return group;
}

// 环形阵列
function circularArray(object, count, radius, axis = 'Y') {
  const group = new THREE.Group();
  group.name = `${object.name}_CircularArray`;
  
  for (let i = 0; i < count; i++) {
    const angle = (i / count) * Math.PI * 2;
    const clone = object.clone();
    
    if (axis === 'Y') {
      clone.position.x = Math.cos(angle) * radius;
      clone.position.z = Math.sin(angle) * radius;
      clone.rotation.y = angle;
    }
    // 处理其他轴...
    
    group.add(clone);
  }
  
  return group;
}

// 使用示例
const tree = scene.getObjectByName('树');
const treeRow = linearArray(tree, 10, { x: 5, y: 0, z: 0 });
scene.add(treeRow);

const pillar = scene.getObjectByName('柱子');
const pillars = circularArray(pillar, 8, 10);
scene.add(pillars);

4.8 对象对齐与分布

4.8.1 对齐工具

// 对齐函数
function alignObjects(objects, axis, alignTo) {
  if (objects.length < 2) return;
  
  // 计算对齐参考值
  let reference;
  switch (alignTo) {
    case 'min':
      reference = Math.min(...objects.map(o => getBounds(o).min[axis]));
      break;
    case 'max':
      reference = Math.max(...objects.map(o => getBounds(o).max[axis]));
      break;
    case 'center':
      const bounds = objects.map(o => getBounds(o));
      const centers = bounds.map(b => (b.min[axis] + b.max[axis]) / 2);
      reference = centers.reduce((a, b) => a + b) / centers.length;
      break;
  }
  
  // 应用对齐
  objects.forEach(obj => {
    const bounds = getBounds(obj);
    const currentCenter = (bounds.min[axis] + bounds.max[axis]) / 2;
    obj.position[axis] += reference - currentCenter;
  });
}

// 使用示例
const selected = viewer.selection.getSelected();
alignObjects(selected, 'x', 'min');   // X轴最小值对齐
alignObjects(selected, 'y', 'center'); // Y轴居中对齐
alignObjects(selected, 'z', 'max');   // Z轴最大值对齐

4.8.2 分布工具

// 均匀分布函数
function distributeObjects(objects, axis) {
  if (objects.length < 3) return;
  
  // 按轴排序
  objects.sort((a, b) => a.position[axis] - b.position[axis]);
  
  // 计算分布间距
  const first = objects[0].position[axis];
  const last = objects[objects.length - 1].position[axis];
  const spacing = (last - first) / (objects.length - 1);
  
  // 应用分布
  objects.forEach((obj, i) => {
    if (i !== 0 && i !== objects.length - 1) {
      obj.position[axis] = first + i * spacing;
    }
  });
}

// 使用示例
distributeObjects(selected, 'x');  // X轴均匀分布

4.9 场景优化

4.9.1 性能分析

// 获取场景统计信息
function getSceneStats(scene) {
  let meshCount = 0;
  let triangleCount = 0;
  let vertexCount = 0;
  let materialCount = new Set();
  let textureCount = new Set();
  
  scene.traverse(obj => {
    if (obj instanceof THREE.Mesh) {
      meshCount++;
      const geometry = obj.geometry;
      if (geometry.index) {
        triangleCount += geometry.index.count / 3;
      } else {
        triangleCount += geometry.attributes.position.count / 3;
      }
      vertexCount += geometry.attributes.position.count;
      
      const materials = Array.isArray(obj.material) 
        ? obj.material 
        : [obj.material];
      materials.forEach(mat => {
        materialCount.add(mat.uuid);
        Object.keys(mat).forEach(key => {
          if (mat[key] instanceof THREE.Texture) {
            textureCount.add(mat[key].uuid);
          }
        });
      });
    }
  });
  
  return {
    meshCount,
    triangleCount,
    vertexCount,
    materialCount: materialCount.size,
    textureCount: textureCount.size
  };
}

4.9.2 优化建议

问题 优化方案
三角面数过多 使用简化工具减少面数
材质数量过多 合并相似材质,使用材质图集
贴图尺寸过大 压缩贴图,使用合适的分辨率
网格数量过多 合并静态网格,使用实例化
层级过深 扁平化场景结构
Draw Call过多 使用批处理、实例化渲染

4.9.3 自动优化

// 场景优化函数
async function optimizeScene(scene, options) {
  // 合并几何体
  if (options.mergeGeometries) {
    mergeStaticGeometries(scene);
  }
  
  // 简化网格
  if (options.simplify) {
    await simplifyMeshes(scene, options.simplifyRatio);
  }
  
  // 压缩贴图
  if (options.compressTextures) {
    await compressTextures(scene, options.maxTextureSize);
  }
  
  // 生成LOD
  if (options.generateLOD) {
    generateLODs(scene, options.lodLevels);
  }
  
  // 优化材质
  if (options.optimizeMaterials) {
    mergeMaterials(scene);
  }
}

4.10 本章小结

本章详细介绍了Astral3D的场景编辑与模型管理功能,主要内容包括:

  1. 界面布局:工具栏、场景树、属性面板、资源面板的使用方法
  2. 模型导入:30+种格式的导入支持和优化配置
  3. 对象变换:移动、旋转、缩放工具的使用和精确变换
  4. 层级管理:父子关系、分组管理、场景树操作
  5. 对象属性:基础属性、变换属性、用户数据的管理
  6. 场景导出:多种格式导出和场景序列化
  7. 复制与实例化:普通复制、实例化渲染、阵列复制
  8. 对齐与分布:对象对齐和均匀分布工具
  9. 场景优化:性能分析和自动优化方案

通过本章的学习,读者应该能够熟练使用Astral3D进行场景编辑和模型管理,为构建复杂的3D场景打下基础。


下一章预告:第五章将介绍Astral3D的BIM轻量化和CAD解析功能,包括Revit模型导入、IFC解析、DWG/DXF预览等专业功能的使用方法。


posted @ 2026-01-10 13:17  我才是银古  阅读(8)  评论(0)    收藏  举报