Source: ui/world/terrain-editor.js

import {VRSPACEUI} from '../vrspace-ui.js';
import {World} from '../../world/world.js';
import {TextureSelector} from './texture-selector.js';

export class TerrainEditor {
  /**
   * @param {World} world 
   */
  constructor(world) {
    this.world = world;
    this.scene = world.scene;
    this.terrain = world.terrain;
    this.heightIncrement=1;
    this.editing = false;
    this.textureSelector = new TextureSelector(this.scene, (img) => this.publishTexture(img));
    // add own selection predicate to the world
    this.selectionPredicate = (mesh) => this.isSelectableMesh(mesh);
    world.addSelectionPredicate(this.selectionPredicate);
    this.observer = null;
    this.groundPickable = null;
  }
  
  createSharedTerrain() {
    if ( ! World.lastInstance.sharedTerrain ) {
      var object = {
        permanent: true,
        active:true,
        specularColor:this.terrain.terrainMaterial.specularColor,
        diffuseColor:this.terrain.terrainMaterial.diffuseColor,
        emissiveColor:this.terrain.terrainMaterial.emissiveColor
      };
      this.world.worldManager.VRSPACE.createSharedObject(object, "Terrain").then(obj=>{
        console.log("Created new Terrain", obj);
        World.lastInstance.sharedTerrain = obj;
      });
    }
  }
  
  edit() {
    this.createSharedTerrain();
    if ( ! this.observer ) {
      this.observer = this.scene.onPointerObservable.add((pointerInfo) => {
        switch (pointerInfo.type) {
          case BABYLON.PointerEventTypes.POINTERDOWN:
            if(pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh == this.terrain.mesh()) {
              this.lastIndex = this.updatePicked(pointerInfo.pickInfo);
              this.terrain.mesh().enablePointerMoveEvents = true;
            }
            break;
          case BABYLON.PointerEventTypes.POINTERUP:
            this.lastIndex = -1;
            this.terrain.mesh().enablePointerMoveEvents = false;
            break;
          case BABYLON.PointerEventTypes.POINTERMOVE:
            if ( this.lastIndex >= 0 && pointerInfo.pickInfo.pickedMesh == this.terrain.mesh() ) {
              var newIndex = this.terrain.findIndex(pointerInfo.pickInfo.pickedPoint.x,pointerInfo.pickInfo.pickedPoint.z);
              if ( newIndex != this.lastIndex ) {
                this.lastIndex = newIndex;
                this.updatePicked(pointerInfo.pickInfo);
              }
            }
            break;
          }
      });      
    }
    
    if ( World.lastInstance.ground ) {
      this.groundPickable = World.lastInstance.ground.isPickable;
      World.lastInstance.ground.isPickable = false;
    }
    
    this.raiseButton = VRSPACEUI.hud.addButton("Raise", VRSPACEUI.contentBase+"/content/icons/upload.png");
    this.digButton = VRSPACEUI.hud.addButton("Dig", VRSPACEUI.contentBase+"/content/icons/download.png");
    this.textureButton = VRSPACEUI.hud.addButton("Texture", VRSPACEUI.contentBase+"/content/icons/terrain-texture.png");    
    this.raiseSlider = VRSPACEUI.hud.addSlider("Height",0,2,this.heightIncrement)
    
    this.raiseButton.onPointerDownObservable.add( () => {
      this.direction = 1;
      VRSPACEUI.hud.showButtons(this.editing,this.raiseButton,this.raiseSlider);
      this.world.enableFloorSelection(this.editing);
      this.editing = !this.editing;
      this.terrain.terrainMaterial.wireframe = this.editing;
    });
    this.digButton.onPointerDownObservable.add( () => {
      this.direction = -1;
      VRSPACEUI.hud.showButtons(this.editing,this.digButton,this.raiseSlider);
      this.world.enableFloorSelection(this.editing);
      this.editing = !this.editing;
      this.terrain.terrainMaterial.wireframe = this.editing;
    });
    this.textureButton.onPointerDownObservable.add( () => {
      this.editing = !this.editing;
      if ( this.editing ) {
        this.textureSelector.show();
      } else {
        this.textureSelector.hide();
      }
      VRSPACEUI.hud.showButtons(!this.editing,this.textureButton);
    });
    this.raiseSlider.onValueChangedObservable.add(value=>{this.heightIncrement=value});
    
    this.diffusePicker = VRSPACEUI.hud.addColorPicker("Diffuse", this.terrain.terrainMaterial.diffuseColor);
    this.specularPicker = VRSPACEUI.hud.addColorPicker("Specular", this.terrain.terrainMaterial.specularColor);
    this.emissivePicker = VRSPACEUI.hud.addColorPicker("Emissive", this.terrain.terrainMaterial.emissiveColor);
    this.specularPicker.onValueChangedObservable.add( (val) => {
      //this.terrain.terrainMaterial.specularColor.copyFrom(val);
      this.world.worldManager.VRSPACE.sendEvent(World.lastInstance.sharedTerrain, {specularColor:val});      
    });
    this.diffusePicker.onValueChangedObservable.add( (val) => {
      //this.terrain.terrainMaterial.diffuseColor.copyFrom(val);
      this.world.worldManager.VRSPACE.sendEvent(World.lastInstance.sharedTerrain, {diffuseColor:val});      
    });
    this.emissivePicker.onValueChangedObservable.add( (val) => {
      //this.terrain.terrainMaterial.emissiveColor.copyFrom(val);
      this.world.worldManager.VRSPACE.sendEvent(World.lastInstance.sharedTerrain, {emissiveColor:val});      
    });
    
    VRSPACEUI.hud.enableSpeech(true);
  }
  
  updatePicked( pickInfo ) {
    var index = -1;
    var x = pickInfo.pickedPoint.x;
    var z = pickInfo.pickedPoint.z;
    if ( this.editing ) {
      var online = this.world.isOnline() && World.lastInstance.sharedTerrain;
      if ( online ) {
        // if online, terrain is not refreshed until the server responds with updated height
        index = this.terrain.findIndex(x,z);
        if ( index ) {
          var point = this.terrain.point(index);
          point.y += this.heightIncrement*this.direction;
          // publish updates
          var change = { change: {index: index, point: point} };
          this.world.worldManager.VRSPACE.sendEvent(World.lastInstance.sharedTerrain, change);
        } else {
          console.log("ERROR: index "+index+" for "+x+","+z);
        }
      } else {
        index = this.terrain.raise(x,z,this.heightIncrement*this.direction, online);
      }
    }
    return index;
  }
  
  publishTexture(imgUrl) {
    console.log("Publishing texture: "+imgUrl);
    this.world.worldManager.VRSPACE.sendEvent(World.lastInstance.sharedTerrain, {diffuseTexture:imgUrl});
  }
  
  dispose() {
    this.world.removeSelectionPredicate(this.selectionPredicate);
    this.world.removeListener(this);
    this.terrain.terrainMaterial.wireframe = false;
    this.scene.onPointerObservable.remove(this.observer);
    this.raiseButton.dispose();
    this.digButton.dispose();
    this.textureButton.dispose();
    this.raiseSlider.dispose();
    if ( World.lastInstance.ground ) {
      World.lastInstance.ground.isPickable = this.groundPickable;
    }
  }
  
  isSelectableMesh(mesh) {
    // terrain is selectable only while editing
    return this.editing && this.terrain && mesh == this.terrain.mesh();
  }
}