threejs panorama and anchor editing

Panorama and Anchor Editing

Get into the habit of writing together! This is the sixth day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

Today, let's briefly talk about the threejs panorama and anchor editing solutions. The panorama is the so-called sky box, which is applied to scenes such as: the sky background of the scene model, the starry sky background at night, VR viewing, etc.~

This article focuses on anchor editing, which is the so-called scene editing scheme . The idea is infinitely close to Low Code , speaking of Low Code! I have delayed four articles, because I was so busy during the New Year period, I really didn't have time to update it! I'm so sorry to see many people waiting for me to finish, I'll be sure to tidy up and update later!

Panorama

In fact, the panorama has nothing to do with it. It can be imagined as a very large cube box, which is connected by the pictures of six sides. And our camera exists inside the cube, which creates a visual error that we are in the scene.

Panorama dismantling

The following is the disassembly diagram of the panorama cube. The six sides are connected to each other. You can make up for it. When the cube is assembled, what we see is a scene that is seamlessly connected. Of course, if you look carefully, you can still see the boundary of the cube. .

The dice brain can be added to the position of the camera, so it is easy to understand

Since there are sky boxes, there must be differences in the sky boxes of multiple scenes. How do we switch the corresponding sky box when we switch the scene? Very simple, we just need to encapsulate a switching function as follows

// 添加地面和天空盒
Viewer.prototype.changeSkyBox = function (skydir) {
  const that = this
  // 创建几何模型 BoxGeometry('x轴', '轴', 'z轴')
  const geometry = new THREE.BoxGeometry(999, 999, 999)
  // 创建纹理贴图  前后  上下  左右
  const texture0 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/px.jpg`))
  const texture1 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/nx.jpg`))
  const texture2 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/py.jpg`))
  const texture3 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/ny.jpg`))
  const texture4 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/pz.jpg`))
  const texture5 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/nz.jpg`))
  // 添加材质
  const material = [
    new THREE.MeshBasicMaterial({color: 0xffddff, map: texture0, side: THREE.DoubleSide}),
    new THREE.MeshBasicMaterial({color: 0xffddff, map: texture1, side: THREE.DoubleSide}),
    new THREE.MeshBasicMaterial({color: 0xffddff, map: texture2, side: THREE.DoubleSide}),
    new THREE.MeshBasicMaterial({color: 0xffddff, map: texture3, side: THREE.DoubleSide}),
    new THREE.MeshBasicMaterial({color: 0xffddff, map: texture4, side: THREE.DoubleSide}),
    new THREE.MeshBasicMaterial({color: 0xffddff, map: texture5, side: THREE.DoubleSide})
  ]
  // 创建网格对象
  const cube = new _3d.Mesh(geometry, material)
  cube.layers.enableAll()
  cube.name = 'skybox'
  that.skybox && this.scene.remove(that.skybox)
  that.skybox = cube
  that.AmbientGroup.add(cube)
}
复制代码

Scene editing scheme

The lighting positions and icon positions of different scenes may be different. As a basic editing platform, we must consider how to make our scenes configurable and make different changes according to different needs. The previous article introduced that we configure the model in the form of JSON, so how do we configure the points and light positions in the scene?

transformControls

Transform controller, here its main function is to translate, scale, and rotate the anchor point

Initialize the controller

//移动控制器
this.transformControls = new TransformControls(this.camera, this.renderer.domElement)
this.transformControls.setSize(0.5)
this.scene.add(this.transformControls) //添加入场景
复制代码

Add movable objects

We can add a transform_attach method to, say, a light class, which is called after editing is enabled. Only after attach can it be picked up for translation, rotation, and scaling operations

transformControls.attach(...)
//对象类中
transform_attach(value){
  if (value) {
    this.transformControls.attach(this.dlight)
  }
  else {
    this.transformControls.detach(this.dlight)
  }
}
复制代码

Pan, zoom, rotate

gapp.history.execute is mainly business logic here, save the edited object, mainly used for echo

The main function of SetPositionCommand here is to record the old position information and the new position information

Aside from the above two methods, in fact, as long as you add the transformation controller, you can operate the object. Now I will introduce why you need to save the information.

Viewer.prototype.bindTransformEvent = function () {

  this.transformControls.addEventListener('mouseDown', () => { //鼠标拾取到
    var object = gapp.transformControls.object //获取拾取对象
    this.objectPositionOnDown = object.position.clone()//保存位置
    this.objectRotationOnDown = object.rotation.clone()//保存角度
    this.objectScaleOnDown = object.scale.clone()//保存缩放大小
    gapp.controls.enabled = false
  })
  this.transformControls.addEventListener('mouseUp', () => {//鼠标提起
    var object = gapp.transformControls.object//获取拾取对象
    if (object !== undefined) {
      switch (gapp.transformControls.getMode()) { //这里判断是要进行平移、缩放、旋转操作
        case 'translate':
          if (!this.objectPositionOnDown.equals(object.position)) {
            gapp.history.execute(new SetPositionCommand(this, object, object.position, this.objectPositionOnDown))
          }
          break
        case 'rotate':
          if (!this.objectRotationOnDown.equals(object.rotation)) {
            gapp.history.execute(new SetRotationCommand(this, object, object.rotation, this.objectRotationOnDown))
          }
          break
        case 'scale':
          if (!this.objectScaleOnDown.equals(object.scale)) {
            gapp.history.execute(new SetScaleCommand(this, object, object.scale, this.objectScaleOnDown))
          }
          break
      }
    }
    gapp.controls.enabled = true
  })
}
复制代码

save object

In fact, this is very similar to my Low Code idea, that is, we will eventually save all the configured object information in the form of JSON. For easy echoing in any other project. How to echo it? If we now edit the position information of a light and adjust the color, brightness, etc. through the gui , we can convert the light object object into JSON through toJSON and store it in our final object, and then retrieve the JSON through the interface. Just add it to the scene again

//转化为threejs特有的json格式
 scene.toJSON() 
 (了解过json的同学可以发现,threejs为了缩小大小,将瓦片对象最大限度的拆分材质等,通过id关联并保存为json)
//解析json转为对象
Viewer.prototype.fromJSON = function (json, layeridx, isRayobj) {
  return new Promise((resolve, reject) => {
    // 解析 json 对象
    let loader = new THREE.ObjectLoader();
    let loadedMesh = loader.parse(json);
    let scene = this.mergeToMaterialsMap(loadedMesh, true)
    resolve(scene)
  })
}
复制代码

Epilogue

This article mainly introduces ideas. Relatively speaking, this article is relatively basic. Of course, it can be expanded according to your own requirements.It's not easy to create a likeStudents who are interested in threejs can follow my column and update threejs related cases and solutions from time to time~ threejs column

Guess you like

Origin juejin.im/post/7085930124725452830