threejs(6)-操控物体实现家居编辑器

js 复制代码
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 导入gltf加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
// 导入draco解码器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
// 导入变换控制器
import { TransformControls } from "three/addons/controls/TransformControls.js";
// 创建场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(
  45, // 视角
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近平面
  1000 // 远平面
);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true, // 开启抗锯齿
});
renderer.shadowMap.enabled = true;
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.toneMappingExposure = 1;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 设置相机位置
camera.position.z = 8;
camera.position.y = 2.5;
camera.position.x = 3;
camera.lookAt(0, 1.2, 0);

// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

// 添加网格辅助器
const gridHelper = new THREE.GridHelper(50, 50);
gridHelper.material.opacity = 0.3;
gridHelper.material.transparent = true;
scene.add(gridHelper);

// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;

// 渲染函数
function animate() {
  controls.update();
  requestAnimationFrame(animate);
  // 渲染
  renderer.render(scene, camera);
}
animate();

// 监听窗口变化
window.addEventListener("resize", () => {
  // 重置渲染器宽高比
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 重置相机宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新相机投影矩阵
  camera.updateProjectionMatrix();
});

// rgbeLoader 加载hdr贴图
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
  // 设置球形贴图
  // envMap.mapping = THREE.EquirectangularReflectionMapping;
  envMap.mapping = THREE.EquirectangularRefractionMapping;
  // 设置环境贴图
  // scene.background = envMap;
  scene.background = new THREE.Color(0xcccccc);
  // 设置环境贴图
  scene.environment = envMap;
});
// rgbeLoader 加载hdr贴图
// 实例化加载器gltf
const gltfLoader = new GLTFLoader();
// 实例化加载器draco
const dracoLoader = new DRACOLoader();
// 设置draco路径
dracoLoader.setDecoderPath("./draco/");
// 设置gltf加载器draco解码器
gltfLoader.setDRACOLoader(dracoLoader);
// 加载模型
gltfLoader.load(
  // 模型路径
  "./model/house/house-scene-min.glb",
  // 加载完成回调
  (gltf) => {
    basicScene = gltf.scene;
  }
);

// 创建变换控制器
let tControls = new TransformControls(camera, renderer.domElement);
tControls.addEventListener("change", animate);
// 监听拖动事件,当拖动物体时候,禁用轨道控制器
tControls.addEventListener("dragging-changed", function (event) {
  controls.enabled = !event.value;
});
tControls.addEventListener("change", () => {
  if (eventObj.isClampGroup) {
    tControls.object.position.y = 0;
  }
});
scene.add(tControls);

let basicScene;
let eventObj = {
  Fullscreen: function () {
    // 全屏
    document.body.requestFullscreen();
    console.log("全屏");
  },
  ExitFullscreen: function () {
    document.exitFullscreen();
    console.log("退出全屏");
  },
  addScene: function () {
    scene.add(basicScene);
  },
  setTranslate: function () {
    tControls.setMode("translate");
  },
  setRotate: function () {
    tControls.setMode("rotate");
  },
  setScale: function () {
    tControls.setMode("scale");
  },
  toggleSpace: function () {
    tControls.setSpace(tControls.space === "local" ? "world" : "local");
  },
  cancelMesh: function () {
    tControls.detach();
  },
  translateSnapNum: null,
  rotateSnapNum: 0,
  scaleSnapNum: 0,
  isClampGroup: false,
  isLight: true,
};
// 创建GUI
const gui = new GUI();
// 添加按钮
// gui.add(eventObj, "Fullscreen").name("全屏");
// gui.add(eventObj, "ExitFullscreen").name("退出全屏");
// 控制立方体的位置
// gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");

gui.add(eventObj, "addScene").name("添加户型基础模型");
gui.add(eventObj, "setTranslate").name("位移模式");
gui.add(eventObj, "setRotate").name("旋转模式");
gui.add(eventObj, "setScale").name("缩放模式");
gui.add(eventObj, "toggleSpace").name("切换空间模式");
gui.add(eventObj, "cancelMesh").name("取消选择");
gui
  .add(eventObj, "isLight")
  .name("是否开启灯光")
  .onChange((value) => {
    if (value) {
      renderer.toneMappingExposure = 1;
    } else {
      renderer.toneMappingExposure = 0.1;
    }
  });
// 监听鼠标按键事件
window.addEventListener("keydown", (event) => {
  // 判断是否按的是t键
  if (event.key === "t") {
    eventObj.setTranslate();
  }
  if (event.key === "r") {
    eventObj.setRotate();
  }
  if (event.key === "s") {
    eventObj.setScale();
  }
});

// 添加物体目录
let meshList = [
  {
    name: "盆栽",
    path: "./model/house/plants-min.glb",
  },
  {
    name: "单人沙发",
    path: "./model/house/sofa_chair_min.glb",
  },
];
let folderAddMehs = gui.addFolder("添加物体");
let sceneMeshes = [];
let meshesNum = {};
meshList.forEach((item) => {
  item.addMesh = function () {
    gltfLoader.load(item.path, (gltf) => {
      sceneMeshes.push({
        ...item,
        object3d: gltf.scene,
      });
      let object3d = gltf.scene;

      scene.add(object3d);
      tControlSelect(object3d);
      let meshOpt = {
        toggleMesh: function () {
          tControlSelect(object3d);
        },
      };
      meshesNum[item.name] = meshesNum[item.name]
        ? meshesNum[item.name] + 1
        : 1;
      meshesFolder
        .add(meshOpt, "toggleMesh")
        .name(item.name + meshesNum[item.name]);
    });
  };
  folderAddMehs.add(item, "addMesh").name(item.name);
});

function tControlSelect(mesh) {
  tControls.attach(mesh);
}

let meshesFolder = gui.addFolder("家居列表");

let snapFolder = gui.addFolder("固定设置");
snapFolder
  .add(eventObj, "translateSnapNum", {
    不固定: null,
    1: 1,
    0.1: 0.1,
    10: 10,
  })
  .name("固定位移设置")
  .onChange(() => {
    tControls.setTranslationSnap(eventObj.translateSnapNum);
  });
snapFolder
  .add(eventObj, "rotateSnapNum", 0, 1)
  .step(0.01)
  .name("旋转")
  .onChange(() => {
    tControls.setRotationSnap(eventObj.rotateSnapNum * Math.PI * 2);
  });
snapFolder
  .add(eventObj, "scaleSnapNum", 0, 2)
  .step(0.1)
  .name("缩放")
  .onChange(() => {
    tControls.setScaleSnap(eventObj.scaleSnapNum);
  });
snapFolder.add(eventObj, "isClampGroup").name("是否吸附到地面");
相关推荐
越甲八千4 小时前
相机的曝光和增益
数码相机
越甲八千4 小时前
黑白彩色相机成像原理
数码相机
幻想趾于现实4 小时前
机器视觉调试——现场链接相机(解决各种相机链接问题)
数码相机·工业相机
1alisa13 小时前
Sublime Text for Mac v4【注册汉化版】代码编辑器
macos·编辑器·sublime text
神码编程13 小时前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎
mzak16 小时前
vscode集成deepseek实现辅助编程(银河麒麟系统)【详细自用版】
linux·vscode·编辑器·银河麒麟·deepseek
一个程序员(●—●)16 小时前
编辑器检视器面板深度扩展2+编辑器菜单栏扩展
编辑器
进阶的小木桩18 小时前
Visual Studio Code配置自动规范代码格式
ide·vscode·编辑器
a31582380620 小时前
SnapdragonCamera骁龙相机源码解析
android·数码相机·framework·高通
归叶再无青21 小时前
Mark down编辑器
编辑器