123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /*
- * PaintTool.js
- *
- * @author realor
- */
- import { Tool } from './Tool.js'
- import { I18N } from '../i18n/I18N.js'
- import { Controls } from '../ui/Controls.js'
- import { Solid } from '../core/Solid.js'
- import { InputDialog } from '../ui/InputDialog.js'
- import { ObjectUtils } from '../utils/ObjectUtils.js'
- import * as THREE from '../lib/three.module.js'
- class PaintTool extends Tool {
- constructor(application, options) {
- super(application)
- this.name = 'paint'
- this.label = 'tool.paint.label'
- this.className = 'paint'
- this.setOptions(options)
- this.createPanel()
- this.materials = new Map()
- this.sceneUuid = null
- }
- createPanel() {
- const application = this.application
- this.panel = this.application.createPanel(this.label, 'left')
- this.panel.minimumHeight = 160
- this.materialListElem = Controls.addSelectField(this.panel.bodyElem, 'materialList', 'label.material_list')
- this.materialListElem.style.display = 'block'
- this.materialListElem.style.width = '90%'
- this.materialListElem.style.marginLeft = 'auto'
- this.materialListElem.style.marginRight = 'auto'
- this.materialListElem.addEventListener('change', event => {
- this.loadMaterial()
- })
- this.newMaterialButton = Controls.addButton(this.panel.bodyElem, 'new_material', 'button.new', () => this.newMaterial())
- this.renameMaterialButton = Controls.addButton(this.panel.bodyElem, 'rename_material', 'button.rename', () => this.renameMaterial())
- this.materialUsageButton = Controls.addButton(this.panel.bodyElem, 'material_usage', 'button.material_usage', () => this.materialUsage())
- this.colorElem = Controls.addColorField(this.panel.bodyElem, 'material_color', 'label.color', null, 'option_block inline')
- this.colorElem.addEventListener(
- 'input',
- event => {
- let color = this.colorElem.value
- let materialId = this.materialListElem.value
- let material = this.materials.get(materialId)
- material.color.set(color)
- application.repaint()
- },
- false
- )
- this.specularElem = Controls.addColorField(this.panel.bodyElem, 'material_specular', 'label.specular', null, 'option_block inline')
- this.specularElem.addEventListener(
- 'input',
- event => {
- let color = this.specularElem.value
- let material = this.getSelectedMaterial()
- material.specular.set(color)
- application.repaint()
- },
- false
- )
- this.emissiveElem = Controls.addColorField(this.panel.bodyElem, 'material_specular', 'label.emissive', null, 'option_block inline')
- this.emissiveElem.addEventListener(
- 'input',
- event => {
- let color = this.emissiveElem.value
- let material = this.getSelectedMaterial()
- material.emissive.set(color)
- application.repaint()
- },
- false
- )
- this.opacityElem = document.createElement('div')
- this.opacityElem.className = 'option_block'
- this.panel.bodyElem.appendChild(this.opacityElem)
- this.opacityLabel = document.createElement('label')
- this.opacityLabel.htmlFor = 'material_opacity'
- I18N.set(this.opacityLabel, 'innerHTML', 'label.opacity', 50)
- this.application.i18n.update(this.opacityLabel)
- this.opacityElem.appendChild(this.opacityLabel)
- this.opacityRange = document.createElement('input')
- this.opacityRange.id = 'material_opacity'
- this.opacityRange.type = 'range'
- this.opacityRange.min = 0
- this.opacityRange.max = 100
- this.opacityRange.step = 1
- this.opacityRange.style.display = 'block'
- this.opacityRange.style.width = '80%'
- this.opacityRange.style.marginLeft = 'auto'
- this.opacityRange.style.marginRight = 'auto'
- this.opacityElem.appendChild(this.opacityRange)
- this.opacityRange.addEventListener(
- 'input',
- () => {
- let opacity = this.opacityRange.value
- I18N.set(this.opacityLabel, 'innerHTML', 'label.opacity', opacity)
- application.i18n.update(this.opacityLabel)
- let material = this.getSelectedMaterial()
- material.opacity = opacity / 100
- material.transparent = material.opacity < 1
- application.repaint()
- },
- false
- )
- this.sideSelect = Controls.addSelectField(
- this.panel.bodyElem,
- 'material_side',
- 'label.material_side',
- [
- [String(THREE.FrontSide), 'label.front_side'],
- [String(THREE.BackSide), 'label.back_side'],
- [String(THREE.DoubleSide), 'label.double_side']
- ],
- null,
- 'option_block inline'
- )
- this.sideSelect.addEventListener('change', event => {
- let material = this.getSelectedMaterial()
- material.side = parseInt(this.sideSelect.value)
- application.repaint()
- })
- this.depthTestCheckBox = Controls.addCheckBoxField(this.panel.bodyElem, 'depth_test', 'label.depth_test', false, 'option_block')
- this.depthTestCheckBox.addEventListener('change', event => {
- let material = this.getSelectedMaterial()
- material.depthTest = this.depthTestCheckBox.checked
- application.repaint()
- })
- this.depthWriteCheckBox = Controls.addCheckBoxField(this.panel.bodyElem, 'depth_write', 'label.depth_write', false, 'option_block')
- this.depthWriteCheckBox.addEventListener('change', event => {
- let material = this.getSelectedMaterial()
- material.depthWrite = this.depthWriteCheckBox.checked
- application.repaint()
- })
- this.applyElem = document.createElement('div')
- this.applyElem.className = 'option_block'
- this.panel.bodyElem.appendChild(this.applyElem)
- this.applyMessageElem = document.createElement('div')
- I18N.set(this.applyMessageElem, 'innerHTML', 'label.material_on_selection')
- this.applyElem.appendChild(this.applyMessageElem)
- this.previewMaterialButton = Controls.addButton(this.applyElem, 'preview_material', 'button.preview_material', () => this.applyMaterial({ meshMaterial: this.getSelectedMaterial() }))
- this.applyMaterialButton = Controls.addButton(this.applyElem, 'apply_material', 'button.apply_material', () =>
- this.applyMaterial({
- meshMaterial: this.getSelectedMaterial(),
- original: true
- })
- )
- this.restoreMaterialsButton = Controls.addButton(this.applyElem, 'restore_material', 'button.restore_materials', () => this.applyMaterial({ meshMaterial: null }))
- }
- activate() {
- this.panel.visible = true
- this.findMaterials()
- }
- deactivate() {
- this.panel.visible = false
- }
- loadMaterial() {
- let materialId = this.materialListElem.value
- let material = this.materials.get(materialId)
- let haveMaterial = material !== undefined
- this.colorElem.disabled = !haveMaterial
- this.specularElem.disabled = !haveMaterial
- this.emissiveElem.disabled = !haveMaterial
- this.sideSelect.disabled = !haveMaterial
- this.depthTestCheckBox.disabled = !haveMaterial
- this.depthWriteCheckBox.disabled = !haveMaterial
- this.opacityRange.disabled = !haveMaterial
- this.renameMaterialButton.disabled = !haveMaterial
- this.materialUsageButton.disabled = !haveMaterial
- this.previewMaterialButton.disabled = !haveMaterial
- this.applyMaterialButton.disabled = !haveMaterial
- if (haveMaterial) {
- this.colorElem.value = '#' + material.color.getHexString()
- this.specularElem.value = '#' + material.specular.getHexString()
- this.emissiveElem.value = '#' + material.emissive.getHexString()
- this.sideSelect.value = String(material.side)
- this.depthTestCheckBox.checked = material.depthTest
- this.depthWriteCheckBox.checked = material.depthWrite
- let opacity = Math.round(material.opacity * 100)
- I18N.set(this.opacityLabel, 'innerHTML', 'label.opacity', opacity)
- this.application.i18n.update(this.opacityLabel)
- this.opacityRange.value = opacity
- }
- }
- findMaterials() {
- const application = this.application
- if (this.sceneUuid !== application.scene.uuid) {
- // clear materials if scene is new
- for (let material of this.materials.values()) {
- material.dispose()
- }
- this.materials.clear()
- this.sceneUuid = application.scene.uuid
- }
- const materials = this.materials
- application.baseObject.traverse(object => {
- for (let name of ['material', ObjectUtils.ORIGINAL_MATERIAL]) {
- let material = object[name]
- if (material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial) {
- let materialId = String(material.id)
- materials.set(materialId, material)
- }
- }
- })
- let materialOptions = []
- for (let material of materials.values()) {
- let materialName = this.getMaterialLabel(material)
- materialOptions.push([String(material.id), materialName])
- }
- materialOptions.sort((a, b) => {
- if (a[1] === b[1]) return 0
- return a[1] < b[1] ? -1 : 1
- })
- Controls.setSelectOptions(this.materialListElem, materialOptions)
- this.loadMaterial()
- }
- newMaterial() {
- const dialog = new InputDialog(this.application, 'title.new_material', 'label.material_name', '')
- dialog.onAccept = name => {
- let material = this.createMaterial(name)
- let optionElem = document.createElement('option')
- let materialId = String(material.id)
- optionElem.value = materialId
- optionElem.innerHTML = this.getMaterialLabel(material)
- this.materialListElem.appendChild(optionElem)
- this.materialListElem.value = materialId
- this.materials.set(materialId, material)
- this.loadMaterial()
- dialog.hide()
- }
- dialog.show()
- }
- renameMaterial() {
- let material = this.getSelectedMaterial()
- if (material) {
- const dialog = new InputDialog(this.application, 'title.rename_material', 'label.material_name', material.name)
- dialog.onAccept = name => {
- material.name = name
- let index = this.materialListElem.selectedIndex
- this.materialListElem.options[index].innerHTML = this.getMaterialLabel(material)
- dialog.hide()
- }
- dialog.show()
- }
- }
- materialUsage() {
- let usages = []
- let material = this.getSelectedMaterial()
- if (material) {
- const baseObject = this.application.baseObject
- baseObject.traverse(object => {
- if (object.visible) {
- if (object.material === material) {
- usages.push(object)
- }
- }
- })
- }
- this.application.selection.set(...usages)
- }
- applyMaterial(appearance) {
- const application = this.application
- const roots = application.selection.roots
- ObjectUtils.updateAppearance(roots, appearance)
- application.repaint()
- }
- createMaterial(name) {
- let material = new THREE.MeshPhongMaterial()
- material.color.set('#C0C0C0')
- material.name = name
- material.needsUpdate = true
- material.side = THREE.DoubleSide
- return material
- }
- getSelectedMaterial() {
- let materialId = this.materialListElem.value
- return this.materials.get(materialId)
- }
- getMaterialLabel(material) {
- let name = material.name || 'Unnamed'
- return name + ' (m' + material.id + ')'
- }
- }
- export { PaintTool }
|