You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
952 lines
17 KiB
952 lines
17 KiB
2 years ago
|
import * as THREE from 'three';
|
||
|
|
||
|
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
|
||
|
import { TGALoader } from 'three/addons/loaders/TGALoader.js';
|
||
|
|
||
|
import { UIElement, UISpan, UIDiv, UIRow, UIButton, UICheckbox, UIText, UINumber } from './ui.js';
|
||
|
import { MoveObjectCommand } from '../commands/MoveObjectCommand.js';
|
||
|
|
||
|
class UITexture extends UISpan {
|
||
|
|
||
|
constructor( mapping ) {
|
||
|
|
||
|
super();
|
||
|
|
||
|
const scope = this;
|
||
|
|
||
|
const form = document.createElement( 'form' );
|
||
|
|
||
|
const input = document.createElement( 'input' );
|
||
|
input.type = 'file';
|
||
|
input.addEventListener( 'change', function ( event ) {
|
||
|
|
||
|
loadFile( event.target.files[ 0 ] );
|
||
|
|
||
|
} );
|
||
|
form.appendChild( input );
|
||
|
|
||
|
const canvas = document.createElement( 'canvas' );
|
||
|
canvas.width = 32;
|
||
|
canvas.height = 16;
|
||
|
canvas.style.cursor = 'pointer';
|
||
|
canvas.style.marginRight = '5px';
|
||
|
canvas.style.border = '1px solid #888';
|
||
|
canvas.addEventListener( 'click', function () {
|
||
|
|
||
|
input.click();
|
||
|
|
||
|
} );
|
||
|
canvas.addEventListener( 'drop', function ( event ) {
|
||
|
|
||
|
event.preventDefault();
|
||
|
event.stopPropagation();
|
||
|
loadFile( event.dataTransfer.files[ 0 ] );
|
||
|
|
||
|
} );
|
||
|
this.dom.appendChild( canvas );
|
||
|
|
||
|
function loadFile( file ) {
|
||
|
|
||
|
const extension = file.name.split( '.' ).pop().toLowerCase();
|
||
|
const reader = new FileReader();
|
||
|
|
||
|
if ( extension === 'hdr' || extension === 'pic' ) {
|
||
|
|
||
|
reader.addEventListener( 'load', function ( event ) {
|
||
|
|
||
|
// assuming RGBE/Radiance HDR iamge format
|
||
|
|
||
|
const loader = new RGBELoader();
|
||
|
loader.load( event.target.result, function ( hdrTexture ) {
|
||
|
|
||
|
hdrTexture.sourceFile = file.name;
|
||
|
hdrTexture.isHDRTexture = true;
|
||
|
|
||
|
scope.setValue( hdrTexture );
|
||
|
|
||
|
if ( scope.onChangeCallback ) scope.onChangeCallback( hdrTexture );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
reader.readAsDataURL( file );
|
||
|
|
||
|
} else if ( extension === 'tga' ) {
|
||
|
|
||
|
reader.addEventListener( 'load', function ( event ) {
|
||
|
|
||
|
const canvas = new TGALoader().parse( event.target.result );
|
||
|
|
||
|
const texture = new THREE.CanvasTexture( canvas, mapping );
|
||
|
texture.sourceFile = file.name;
|
||
|
|
||
|
scope.setValue( texture );
|
||
|
|
||
|
if ( scope.onChangeCallback ) scope.onChangeCallback( texture );
|
||
|
|
||
|
}, false );
|
||
|
|
||
|
reader.readAsArrayBuffer( file );
|
||
|
|
||
|
} else if ( file.type.match( 'image.*' ) ) {
|
||
|
|
||
|
reader.addEventListener( 'load', function ( event ) {
|
||
|
|
||
|
const image = document.createElement( 'img' );
|
||
|
image.addEventListener( 'load', function () {
|
||
|
|
||
|
const texture = new THREE.Texture( this, mapping );
|
||
|
texture.sourceFile = file.name;
|
||
|
texture.needsUpdate = true;
|
||
|
|
||
|
scope.setValue( texture );
|
||
|
|
||
|
if ( scope.onChangeCallback ) scope.onChangeCallback( texture );
|
||
|
|
||
|
}, false );
|
||
|
|
||
|
image.src = event.target.result;
|
||
|
|
||
|
}, false );
|
||
|
|
||
|
reader.readAsDataURL( file );
|
||
|
|
||
|
}
|
||
|
|
||
|
form.reset();
|
||
|
|
||
|
}
|
||
|
|
||
|
this.texture = null;
|
||
|
this.onChangeCallback = null;
|
||
|
|
||
|
}
|
||
|
|
||
|
getValue() {
|
||
|
|
||
|
return this.texture;
|
||
|
|
||
|
}
|
||
|
|
||
|
setValue( texture ) {
|
||
|
|
||
|
const canvas = this.dom.children[ 0 ];
|
||
|
const context = canvas.getContext( '2d' );
|
||
|
|
||
|
// Seems like context can be null if the canvas is not visible
|
||
|
if ( context ) {
|
||
|
|
||
|
// Always clear the context before set new texture, because new texture may has transparency
|
||
|
context.clearRect( 0, 0, canvas.width, canvas.height );
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( texture !== null ) {
|
||
|
|
||
|
const image = texture.image;
|
||
|
|
||
|
if ( image !== undefined && image.width > 0 ) {
|
||
|
|
||
|
canvas.title = texture.sourceFile;
|
||
|
const scale = canvas.width / image.width;
|
||
|
|
||
|
if ( image.data === undefined ) {
|
||
|
|
||
|
context.drawImage( image, 0, 0, image.width * scale, image.height * scale );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const canvas2 = renderToCanvas( texture );
|
||
|
context.drawImage( canvas2, 0, 0, image.width * scale, image.height * scale );
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
canvas.title = texture.sourceFile + ' (error)';
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
canvas.title = 'empty';
|
||
|
|
||
|
}
|
||
|
|
||
|
this.texture = texture;
|
||
|
|
||
|
}
|
||
|
|
||
|
setEncoding( encoding ) {
|
||
|
|
||
|
const texture = this.getValue();
|
||
|
|
||
|
if ( texture !== null ) {
|
||
|
|
||
|
texture.encoding = encoding;
|
||
|
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
onChange( callback ) {
|
||
|
|
||
|
this.onChangeCallback = callback;
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class UICubeTexture extends UIElement {
|
||
|
|
||
|
constructor() {
|
||
|
|
||
|
const container = new UIDiv();
|
||
|
|
||
|
super( container.dom );
|
||
|
|
||
|
this.cubeTexture = null;
|
||
|
this.onChangeCallback = null;
|
||
|
|
||
|
this.textures = [];
|
||
|
|
||
|
const scope = this;
|
||
|
|
||
|
const pRow = new UIRow();
|
||
|
const nRow = new UIRow();
|
||
|
|
||
|
pRow.add( new UIText( 'P:' ).setWidth( '35px' ) );
|
||
|
nRow.add( new UIText( 'N:' ).setWidth( '35px' ) );
|
||
|
|
||
|
const posXTexture = new UITexture().onChange( onTextureChanged );
|
||
|
const negXTexture = new UITexture().onChange( onTextureChanged );
|
||
|
const posYTexture = new UITexture().onChange( onTextureChanged );
|
||
|
const negYTexture = new UITexture().onChange( onTextureChanged );
|
||
|
const posZTexture = new UITexture().onChange( onTextureChanged );
|
||
|
const negZTexture = new UITexture().onChange( onTextureChanged );
|
||
|
|
||
|
this.textures.push( posXTexture, negXTexture, posYTexture, negYTexture, posZTexture, negZTexture );
|
||
|
|
||
|
pRow.add( posXTexture );
|
||
|
pRow.add( posYTexture );
|
||
|
pRow.add( posZTexture );
|
||
|
|
||
|
nRow.add( negXTexture );
|
||
|
nRow.add( negYTexture );
|
||
|
nRow.add( negZTexture );
|
||
|
|
||
|
container.add( pRow, nRow );
|
||
|
|
||
|
function onTextureChanged() {
|
||
|
|
||
|
const images = [];
|
||
|
|
||
|
for ( let i = 0; i < scope.textures.length; i ++ ) {
|
||
|
|
||
|
const texture = scope.textures[ i ].getValue();
|
||
|
|
||
|
if ( texture !== null ) {
|
||
|
|
||
|
images.push( texture.isHDRTexture ? texture : texture.image );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( images.length === 6 ) {
|
||
|
|
||
|
const cubeTexture = new THREE.CubeTexture( images );
|
||
|
cubeTexture.needsUpdate = true;
|
||
|
|
||
|
if ( images[ 0 ].isHDRTexture ) cubeTexture.isHDRTexture = true;
|
||
|
|
||
|
scope.cubeTexture = cubeTexture;
|
||
|
|
||
|
if ( scope.onChangeCallback ) scope.onChangeCallback( cubeTexture );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
setEncoding( encoding ) {
|
||
|
|
||
|
const cubeTexture = this.getValue();
|
||
|
if ( cubeTexture !== null ) {
|
||
|
|
||
|
cubeTexture.encoding = encoding;
|
||
|
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
getValue() {
|
||
|
|
||
|
return this.cubeTexture;
|
||
|
|
||
|
}
|
||
|
|
||
|
setValue( cubeTexture ) {
|
||
|
|
||
|
this.cubeTexture = cubeTexture;
|
||
|
|
||
|
if ( cubeTexture !== null ) {
|
||
|
|
||
|
const images = cubeTexture.image;
|
||
|
|
||
|
if ( Array.isArray( images ) === true && images.length === 6 ) {
|
||
|
|
||
|
for ( let i = 0; i < images.length; i ++ ) {
|
||
|
|
||
|
const image = images[ i ];
|
||
|
|
||
|
const texture = new THREE.Texture( image );
|
||
|
this.textures[ i ].setValue( texture );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const textures = this.textures;
|
||
|
|
||
|
for ( let i = 0; i < textures.length; i ++ ) {
|
||
|
|
||
|
textures[ i ].setValue( null );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
onChange( callback ) {
|
||
|
|
||
|
this.onChangeCallback = callback;
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class UIOutliner extends UIDiv {
|
||
|
|
||
|
constructor( editor ) {
|
||
|
|
||
|
super();
|
||
|
|
||
|
this.dom.className = 'Outliner';
|
||
|
this.dom.tabIndex = 0; // keyup event is ignored without setting tabIndex
|
||
|
|
||
|
const scope = this;
|
||
|
|
||
|
// hack
|
||
|
this.scene = editor.scene;
|
||
|
|
||
|
// Prevent native scroll behavior
|
||
|
this.dom.addEventListener( 'keydown', function ( event ) {
|
||
|
|
||
|
switch ( event.keyCode ) {
|
||
|
|
||
|
case 38: // up
|
||
|
case 40: // down
|
||
|
event.preventDefault();
|
||
|
event.stopPropagation();
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
} );
|
||
|
|
||
|
// Keybindings to support arrow navigation
|
||
|
this.dom.addEventListener( 'keyup', function ( event ) {
|
||
|
|
||
|
switch ( event.keyCode ) {
|
||
|
|
||
|
case 38: // up
|
||
|
scope.selectIndex( scope.selectedIndex - 1 );
|
||
|
break;
|
||
|
case 40: // down
|
||
|
scope.selectIndex( scope.selectedIndex + 1 );
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
} );
|
||
|
|
||
|
this.editor = editor;
|
||
|
|
||
|
this.options = [];
|
||
|
this.selectedIndex = - 1;
|
||
|
this.selectedValue = null;
|
||
|
|
||
|
}
|
||
|
|
||
|
selectIndex( index ) {
|
||
|
|
||
|
if ( index >= 0 && index < this.options.length ) {
|
||
|
|
||
|
this.setValue( this.options[ index ].value );
|
||
|
|
||
|
const changeEvent = document.createEvent( 'HTMLEvents' );
|
||
|
changeEvent.initEvent( 'change', true, true );
|
||
|
this.dom.dispatchEvent( changeEvent );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
setOptions( options ) {
|
||
|
|
||
|
const scope = this;
|
||
|
|
||
|
while ( scope.dom.children.length > 0 ) {
|
||
|
|
||
|
scope.dom.removeChild( scope.dom.firstChild );
|
||
|
|
||
|
}
|
||
|
|
||
|
function onClick() {
|
||
|
|
||
|
scope.setValue( this.value );
|
||
|
|
||
|
const changeEvent = document.createEvent( 'HTMLEvents' );
|
||
|
changeEvent.initEvent( 'change', true, true );
|
||
|
scope.dom.dispatchEvent( changeEvent );
|
||
|
|
||
|
}
|
||
|
|
||
|
// Drag
|
||
|
|
||
|
let currentDrag;
|
||
|
|
||
|
function onDrag() {
|
||
|
|
||
|
currentDrag = this;
|
||
|
|
||
|
}
|
||
|
|
||
|
function onDragStart( event ) {
|
||
|
|
||
|
event.dataTransfer.setData( 'text', 'foo' );
|
||
|
|
||
|
}
|
||
|
|
||
|
function onDragOver( event ) {
|
||
|
|
||
|
if ( this === currentDrag ) return;
|
||
|
|
||
|
const area = event.offsetY / this.clientHeight;
|
||
|
|
||
|
if ( area < 0.25 ) {
|
||
|
|
||
|
this.className = 'option dragTop';
|
||
|
|
||
|
} else if ( area > 0.75 ) {
|
||
|
|
||
|
this.className = 'option dragBottom';
|
||
|
|
||
|
} else {
|
||
|
|
||
|
this.className = 'option drag';
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
function onDragLeave() {
|
||
|
|
||
|
if ( this === currentDrag ) return;
|
||
|
|
||
|
this.className = 'option';
|
||
|
|
||
|
}
|
||
|
|
||
|
function onDrop( event ) {
|
||
|
|
||
|
if ( this === currentDrag || currentDrag === undefined ) return;
|
||
|
|
||
|
this.className = 'option';
|
||
|
|
||
|
const scene = scope.scene;
|
||
|
const object = scene.getObjectById( currentDrag.value );
|
||
|
|
||
|
const area = event.offsetY / this.clientHeight;
|
||
|
|
||
|
if ( area < 0.25 ) {
|
||
|
|
||
|
const nextObject = scene.getObjectById( this.value );
|
||
|
moveObject( object, nextObject.parent, nextObject );
|
||
|
|
||
|
} else if ( area > 0.75 ) {
|
||
|
|
||
|
let nextObject, parent;
|
||
|
|
||
|
if ( this.nextSibling !== null ) {
|
||
|
|
||
|
nextObject = scene.getObjectById( this.nextSibling.value );
|
||
|
parent = nextObject.parent;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// end of list (no next object)
|
||
|
|
||
|
nextObject = null;
|
||
|
parent = scene.getObjectById( this.value ).parent;
|
||
|
|
||
|
}
|
||
|
|
||
|
moveObject( object, parent, nextObject );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const parentObject = scene.getObjectById( this.value );
|
||
|
moveObject( object, parentObject );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
function moveObject( object, newParent, nextObject ) {
|
||
|
|
||
|
if ( nextObject === null ) nextObject = undefined;
|
||
|
|
||
|
let newParentIsChild = false;
|
||
|
|
||
|
object.traverse( function ( child ) {
|
||
|
|
||
|
if ( child === newParent ) newParentIsChild = true;
|
||
|
|
||
|
} );
|
||
|
|
||
|
if ( newParentIsChild ) return;
|
||
|
|
||
|
const editor = scope.editor;
|
||
|
editor.execute( new MoveObjectCommand( editor, object, newParent, nextObject ) );
|
||
|
|
||
|
const changeEvent = document.createEvent( 'HTMLEvents' );
|
||
|
changeEvent.initEvent( 'change', true, true );
|
||
|
scope.dom.dispatchEvent( changeEvent );
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
scope.options = [];
|
||
|
|
||
|
for ( let i = 0; i < options.length; i ++ ) {
|
||
|
|
||
|
const div = options[ i ];
|
||
|
div.className = 'option';
|
||
|
scope.dom.appendChild( div );
|
||
|
|
||
|
scope.options.push( div );
|
||
|
|
||
|
div.addEventListener( 'click', onClick );
|
||
|
|
||
|
if ( div.draggable === true ) {
|
||
|
|
||
|
div.addEventListener( 'drag', onDrag );
|
||
|
div.addEventListener( 'dragstart', onDragStart ); // Firefox needs this
|
||
|
|
||
|
div.addEventListener( 'dragover', onDragOver );
|
||
|
div.addEventListener( 'dragleave', onDragLeave );
|
||
|
div.addEventListener( 'drop', onDrop );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
return scope;
|
||
|
|
||
|
}
|
||
|
|
||
|
getValue() {
|
||
|
|
||
|
return this.selectedValue;
|
||
|
|
||
|
}
|
||
|
|
||
|
setValue( value ) {
|
||
|
|
||
|
for ( let i = 0; i < this.options.length; i ++ ) {
|
||
|
|
||
|
const element = this.options[ i ];
|
||
|
|
||
|
if ( element.value === value ) {
|
||
|
|
||
|
element.classList.add( 'active' );
|
||
|
|
||
|
// scroll into view
|
||
|
|
||
|
const y = element.offsetTop - this.dom.offsetTop;
|
||
|
const bottomY = y + element.offsetHeight;
|
||
|
const minScroll = bottomY - this.dom.offsetHeight;
|
||
|
|
||
|
if ( this.dom.scrollTop > y ) {
|
||
|
|
||
|
this.dom.scrollTop = y;
|
||
|
|
||
|
} else if ( this.dom.scrollTop < minScroll ) {
|
||
|
|
||
|
this.dom.scrollTop = minScroll;
|
||
|
|
||
|
}
|
||
|
|
||
|
this.selectedIndex = i;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
element.classList.remove( 'active' );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
this.selectedValue = value;
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class UIPoints extends UISpan {
|
||
|
|
||
|
constructor() {
|
||
|
|
||
|
super();
|
||
|
|
||
|
this.dom.style.display = 'inline-block';
|
||
|
|
||
|
this.pointsList = new UIDiv();
|
||
|
this.add( this.pointsList );
|
||
|
|
||
|
this.pointsUI = [];
|
||
|
this.lastPointIdx = 0;
|
||
|
this.onChangeCallback = null;
|
||
|
|
||
|
// TODO Remove this bind() stuff
|
||
|
|
||
|
this.update = function () {
|
||
|
|
||
|
if ( this.onChangeCallback !== null ) {
|
||
|
|
||
|
this.onChangeCallback();
|
||
|
|
||
|
}
|
||
|
|
||
|
}.bind( this );
|
||
|
|
||
|
}
|
||
|
|
||
|
onChange( callback ) {
|
||
|
|
||
|
this.onChangeCallback = callback;
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
clear() {
|
||
|
|
||
|
for ( let i = 0; i < this.pointsUI.length; ++ i ) {
|
||
|
|
||
|
if ( this.pointsUI[ i ] ) {
|
||
|
|
||
|
this.deletePointRow( i, true );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
this.lastPointIdx = 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
deletePointRow( idx, dontUpdate ) {
|
||
|
|
||
|
if ( ! this.pointsUI[ idx ] ) return;
|
||
|
|
||
|
this.pointsList.remove( this.pointsUI[ idx ].row );
|
||
|
|
||
|
this.pointsUI.splice( idx, 1 );
|
||
|
|
||
|
if ( dontUpdate !== true ) {
|
||
|
|
||
|
this.update();
|
||
|
|
||
|
}
|
||
|
|
||
|
this.lastPointIdx --;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class UIPoints2 extends UIPoints {
|
||
|
|
||
|
constructor() {
|
||
|
|
||
|
super();
|
||
|
|
||
|
const row = new UIRow();
|
||
|
this.add( row );
|
||
|
|
||
|
const addPointButton = new UIButton( '+' );
|
||
|
addPointButton.onClick( () => {
|
||
|
|
||
|
if ( this.pointsUI.length === 0 ) {
|
||
|
|
||
|
this.pointsList.add( this.createPointRow( 0, 0 ) );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const point = this.pointsUI[ this.pointsUI.length - 1 ];
|
||
|
|
||
|
this.pointsList.add( this.createPointRow( point.x.getValue(), point.y.getValue() ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
this.update();
|
||
|
|
||
|
} );
|
||
|
row.add( addPointButton );
|
||
|
|
||
|
}
|
||
|
|
||
|
getValue() {
|
||
|
|
||
|
const points = [];
|
||
|
|
||
|
let count = 0;
|
||
|
|
||
|
for ( let i = 0; i < this.pointsUI.length; i ++ ) {
|
||
|
|
||
|
const pointUI = this.pointsUI[ i ];
|
||
|
|
||
|
if ( ! pointUI ) continue;
|
||
|
|
||
|
points.push( new THREE.Vector2( pointUI.x.getValue(), pointUI.y.getValue() ) );
|
||
|
++ count;
|
||
|
pointUI.lbl.setValue( count );
|
||
|
|
||
|
}
|
||
|
|
||
|
return points;
|
||
|
|
||
|
}
|
||
|
|
||
|
setValue( points ) {
|
||
|
|
||
|
this.clear();
|
||
|
|
||
|
for ( let i = 0; i < points.length; i ++ ) {
|
||
|
|
||
|
const point = points[ i ];
|
||
|
this.pointsList.add( this.createPointRow( point.x, point.y ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
this.update();
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
createPointRow( x, y ) {
|
||
|
|
||
|
const pointRow = new UIDiv();
|
||
|
const lbl = new UIText( this.lastPointIdx + 1 ).setWidth( '20px' );
|
||
|
const txtX = new UINumber( x ).setWidth( '30px' ).onChange( this.update );
|
||
|
const txtY = new UINumber( y ).setWidth( '30px' ).onChange( this.update );
|
||
|
|
||
|
const scope = this;
|
||
|
const btn = new UIButton( '-' ).onClick( function () {
|
||
|
|
||
|
if ( scope.isEditing ) return;
|
||
|
|
||
|
const idx = scope.pointsList.getIndexOfChild( pointRow );
|
||
|
scope.deletePointRow( idx );
|
||
|
|
||
|
} );
|
||
|
|
||
|
this.pointsUI.push( { row: pointRow, lbl: lbl, x: txtX, y: txtY } );
|
||
|
++ this.lastPointIdx;
|
||
|
pointRow.add( lbl, txtX, txtY, btn );
|
||
|
|
||
|
return pointRow;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class UIPoints3 extends UIPoints {
|
||
|
|
||
|
constructor() {
|
||
|
|
||
|
super();
|
||
|
|
||
|
const row = new UIRow();
|
||
|
this.add( row );
|
||
|
|
||
|
const addPointButton = new UIButton( '+' );
|
||
|
addPointButton.onClick( () => {
|
||
|
|
||
|
if ( this.pointsUI.length === 0 ) {
|
||
|
|
||
|
this.pointsList.add( this.createPointRow( 0, 0, 0 ) );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const point = this.pointsUI[ this.pointsUI.length - 1 ];
|
||
|
|
||
|
this.pointsList.add( this.createPointRow( point.x.getValue(), point.y.getValue(), point.z.getValue() ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
this.update();
|
||
|
|
||
|
} );
|
||
|
row.add( addPointButton );
|
||
|
|
||
|
}
|
||
|
|
||
|
getValue() {
|
||
|
|
||
|
const points = [];
|
||
|
let count = 0;
|
||
|
|
||
|
for ( let i = 0; i < this.pointsUI.length; i ++ ) {
|
||
|
|
||
|
const pointUI = this.pointsUI[ i ];
|
||
|
|
||
|
if ( ! pointUI ) continue;
|
||
|
|
||
|
points.push( new THREE.Vector3( pointUI.x.getValue(), pointUI.y.getValue(), pointUI.z.getValue() ) );
|
||
|
++ count;
|
||
|
pointUI.lbl.setValue( count );
|
||
|
|
||
|
}
|
||
|
|
||
|
return points;
|
||
|
|
||
|
}
|
||
|
|
||
|
setValue( points ) {
|
||
|
|
||
|
this.clear();
|
||
|
|
||
|
for ( let i = 0; i < points.length; i ++ ) {
|
||
|
|
||
|
const point = points[ i ];
|
||
|
this.pointsList.add( this.createPointRow( point.x, point.y, point.z ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
this.update();
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
createPointRow( x, y, z ) {
|
||
|
|
||
|
const pointRow = new UIDiv();
|
||
|
const lbl = new UIText( this.lastPointIdx + 1 ).setWidth( '20px' );
|
||
|
const txtX = new UINumber( x ).setWidth( '30px' ).onChange( this.update );
|
||
|
const txtY = new UINumber( y ).setWidth( '30px' ).onChange( this.update );
|
||
|
const txtZ = new UINumber( z ).setWidth( '30px' ).onChange( this.update );
|
||
|
|
||
|
const scope = this;
|
||
|
const btn = new UIButton( '-' ).onClick( function () {
|
||
|
|
||
|
if ( scope.isEditing ) return;
|
||
|
|
||
|
const idx = scope.pointsList.getIndexOfChild( pointRow );
|
||
|
scope.deletePointRow( idx );
|
||
|
|
||
|
} );
|
||
|
|
||
|
this.pointsUI.push( { row: pointRow, lbl: lbl, x: txtX, y: txtY, z: txtZ } );
|
||
|
++ this.lastPointIdx;
|
||
|
pointRow.add( lbl, txtX, txtY, txtZ, btn );
|
||
|
|
||
|
return pointRow;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class UIBoolean extends UISpan {
|
||
|
|
||
|
constructor( boolean, text ) {
|
||
|
|
||
|
super();
|
||
|
|
||
|
this.setMarginRight( '4px' );
|
||
|
|
||
|
this.checkbox = new UICheckbox( boolean );
|
||
|
this.text = new UIText( text ).setMarginLeft( '3px' );
|
||
|
|
||
|
this.add( this.checkbox );
|
||
|
this.add( this.text );
|
||
|
|
||
|
}
|
||
|
|
||
|
getValue() {
|
||
|
|
||
|
return this.checkbox.getValue();
|
||
|
|
||
|
}
|
||
|
|
||
|
setValue( value ) {
|
||
|
|
||
|
return this.checkbox.setValue( value );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
let renderer;
|
||
|
|
||
|
function renderToCanvas( texture ) {
|
||
|
|
||
|
if ( renderer === undefined ) {
|
||
|
|
||
|
renderer = new THREE.WebGLRenderer();
|
||
|
renderer.outputEncoding = THREE.sRGBEncoding;
|
||
|
|
||
|
}
|
||
|
|
||
|
const image = texture.image;
|
||
|
|
||
|
renderer.setSize( image.width, image.height, false );
|
||
|
|
||
|
const scene = new THREE.Scene();
|
||
|
const camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||
|
|
||
|
const material = new THREE.MeshBasicMaterial( { map: texture } );
|
||
|
const quad = new THREE.PlaneGeometry( 2, 2 );
|
||
|
const mesh = new THREE.Mesh( quad, material );
|
||
|
scene.add( mesh );
|
||
|
|
||
|
renderer.render( scene, camera );
|
||
|
|
||
|
return renderer.domElement;
|
||
|
|
||
|
}
|
||
|
|
||
|
export { UITexture, UICubeTexture, UIOutliner, UIPoints, UIPoints2, UIPoints3, UIBoolean };
|