Editor.js 9.6 KB
Newer Older
M
Mr.doob 已提交
1 2 3 4
/**
 * @author mrdoob / http://mrdoob.com/
 */

5 6
var Editor = function () {

M
Mr.doob 已提交
7
	this.DEFAULT_CAMERA = new THREE.PerspectiveCamera( 50, 1, 0.01, 1000 );
M
Mr.doob 已提交
8
	this.DEFAULT_CAMERA.name = 'Camera';
M
Mr.doob 已提交
9
	this.DEFAULT_CAMERA.position.set( 0, 5, 10 );
M
Mr.doob 已提交
10 11
	this.DEFAULT_CAMERA.lookAt( new THREE.Vector3() );

M
Mr.doob 已提交
12 13
	var Signal = signals.Signal;

14 15
	this.signals = {

16 17
		// script

M
Mr.doob 已提交
18
		editScript: new Signal(),
19

M
Mr.doob 已提交
20 21
		// player

M
Mr.doob 已提交
22 23
		startPlayer: new Signal(),
		stopPlayer: new Signal(),
M
Mr.doob 已提交
24

25 26
		// actions

M
Mr.doob 已提交
27
		showModal: new Signal(),
28

29 30
		// notifications

M
Mr.doob 已提交
31
		editorCleared: new Signal(),
32

M
Mr.doob 已提交
33 34
		savingStarted: new Signal(),
		savingFinished: new Signal(),
M
Mr.doob 已提交
35

M
Mr.doob 已提交
36
		themeChanged: new Signal(),
37

M
Mr.doob 已提交
38 39 40 41
		transformModeChanged: new Signal(),
		snapChanged: new Signal(),
		spaceChanged: new Signal(),
		rendererChanged: new Signal(),
M
Mr.doob 已提交
42

43
		sceneBackgroundChanged: new Signal(),
M
Mr.doob 已提交
44
		sceneFogChanged: new Signal(),
M
Mr.doob 已提交
45
		sceneGraphChanged: new Signal(),
46

M
Mr.doob 已提交
47
		cameraChanged: new Signal(),
48

M
Mr.doob 已提交
49
		geometryChanged: new Signal(),
M
Mr.doob 已提交
50

M
Mr.doob 已提交
51 52
		objectSelected: new Signal(),
		objectFocused: new Signal(),
53

M
Mr.doob 已提交
54 55 56
		objectAdded: new Signal(),
		objectChanged: new Signal(),
		objectRemoved: new Signal(),
M
Mr.doob 已提交
57

M
Mr.doob 已提交
58 59
		helperAdded: new Signal(),
		helperRemoved: new Signal(),
M
Mr.doob 已提交
60

M
Mr.doob 已提交
61
		materialChanged: new Signal(),
M
Mr.doob 已提交
62

M
Mr.doob 已提交
63 64 65
		scriptAdded: new Signal(),
		scriptChanged: new Signal(),
		scriptRemoved: new Signal(),
M
Mr.doob 已提交
66

M
Mr.doob 已提交
67
		windowResize: new Signal(),
C
Chris Jubb 已提交
68

M
Mr.doob 已提交
69 70
		showGridChanged: new Signal(),
		refreshSidebarObject3D: new Signal(),
M
Mugen87 已提交
71
		historyChanged: new Signal()
72 73

	};
M
Mr.doob 已提交
74

75
	this.config = new Config();
76
	this.history = new History( this );
77
	this.storage = new Storage();
M
Mr.doob 已提交
78 79
	this.strings = new Strings( this.config );

80 81
	this.loader = new Loader( this );

M
Mr.doob 已提交
82
	this.camera = this.DEFAULT_CAMERA.clone();
83

84
	this.scene = new THREE.Scene();
85
	this.scene.name = 'Scene';
86
	this.scene.background = new THREE.Color( 0xaaaaaa );
M
Mr.doob 已提交
87

88
	this.sceneHelpers = new THREE.Scene();
89 90 91 92 93

	this.object = {};
	this.geometries = {};
	this.materials = {};
	this.textures = {};
M
Mr.doob 已提交
94
	this.scripts = {};
95

96 97 98
	this.selected = null;
	this.helpers = {};

99 100 101 102
};

Editor.prototype = {

103 104 105 106 107 108 109 110
	setTheme: function ( value ) {

		document.getElementById( 'theme' ).href = value;

		this.signals.themeChanged.dispatch( value );

	},

111 112
	//

113 114
	setScene: function ( scene ) {

M
Mr.doob 已提交
115
		this.scene.uuid = scene.uuid;
116
		this.scene.name = scene.name;
M
Mr.doob 已提交
117

118
		if ( scene.background !== null ) this.scene.background = scene.background.clone();
M
Mr.doob 已提交
119 120
		if ( scene.fog !== null ) this.scene.fog = scene.fog.clone();

121 122
		this.scene.userData = JSON.parse( JSON.stringify( scene.userData ) );

123 124 125 126
		// avoid render per object

		this.signals.sceneGraphChanged.active = false;

127 128 129 130 131 132
		while ( scene.children.length > 0 ) {

			this.addObject( scene.children[ 0 ] );

		}

133 134 135
		this.signals.sceneGraphChanged.active = true;
		this.signals.sceneGraphChanged.dispatch();

136 137 138 139
	},

	//

140 141
	addObject: function ( object ) {

142 143 144 145
		var scope = this;

		object.traverse( function ( child ) {

146 147 148
			if ( child.geometry !== undefined ) scope.addGeometry( child.geometry );
			if ( child.material !== undefined ) scope.addMaterial( child.material );

149 150 151 152
			scope.addHelper( child );

		} );

153 154 155
		this.scene.add( object );

		this.signals.objectAdded.dispatch( object );
156
		this.signals.sceneGraphChanged.dispatch();
157 158 159

	},

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	moveObject: function ( object, parent, before ) {

		if ( parent === undefined ) {

			parent = this.scene;

		}

		parent.add( object );

		// sort children array

		if ( before !== undefined ) {

			var index = parent.children.indexOf( before );
			parent.children.splice( index, 0, object );
			parent.children.pop();

		}

		this.signals.sceneGraphChanged.dispatch();

	},

184
	nameObject: function ( object, name ) {
185 186 187 188 189 190

		object.name = name;
		this.signals.sceneGraphChanged.dispatch();

	},

191 192
	removeObject: function ( object ) {

193
		if ( object.parent === null ) return; // avoid deleting the camera or scene
194

195 196 197 198 199 200 201 202
		var scope = this;

		object.traverse( function ( child ) {

			scope.removeHelper( child );

		} );

203
		object.parent.remove( object );
M
Mr.doob 已提交
204 205

		this.signals.objectRemoved.dispatch( object );
206
		this.signals.sceneGraphChanged.dispatch();
207 208 209

	},

210
	addGeometry: function ( geometry ) {
211

212
		this.geometries[ geometry.uuid ] = geometry;
213 214 215

	},

216 217 218 219 220 221 222
	setGeometryName: function ( geometry, name ) {

		geometry.name = name;
		this.signals.sceneGraphChanged.dispatch();

	},

223 224
	addMaterial: function ( material ) {

225
		this.materials[ material.uuid ] = material;
226 227 228

	},

229 230 231 232 233 234 235
	setMaterialName: function ( material, name ) {

		material.name = name;
		this.signals.sceneGraphChanged.dispatch();

	},

236 237
	addTexture: function ( texture ) {

238
		this.textures[ texture.uuid ] = texture;
239 240 241

	},

242 243
	//

M
Mr.doob 已提交
244
	addHelper: function () {
245

246
		var geometry = new THREE.SphereBufferGeometry( 2, 4, 2 );
M
Mr.doob 已提交
247
		var material = new THREE.MeshBasicMaterial( { color: 0xff0000, visible: false } );
248

M
Mr.doob 已提交
249
		return function ( object ) {
M
Mr.doob 已提交
250

M
Mr.doob 已提交
251
			var helper;
252

M
Mr.doob 已提交
253
			if ( object instanceof THREE.Camera ) {
254

255
				helper = new THREE.CameraHelper( object, 1 );
256 257

			} else if ( object instanceof THREE.PointLight ) {
258

259
				helper = new THREE.PointLightHelper( object, 1 );
260

M
Mr.doob 已提交
261
			} else if ( object instanceof THREE.DirectionalLight ) {
262

263
				helper = new THREE.DirectionalLightHelper( object, 1 );
M
Mr.doob 已提交
264

M
Mr.doob 已提交
265
			} else if ( object instanceof THREE.SpotLight ) {
266

267
				helper = new THREE.SpotLightHelper( object, 1 );
M
Mr.doob 已提交
268

M
Mr.doob 已提交
269
			} else if ( object instanceof THREE.HemisphereLight ) {
M
Mr.doob 已提交
270

271
				helper = new THREE.HemisphereLightHelper( object, 1 );
M
Mr.doob 已提交
272

M
Mr.doob 已提交
273 274 275 276
			} else if ( object instanceof THREE.SkinnedMesh ) {

				helper = new THREE.SkeletonHelper( object );

M
Mr.doob 已提交
277
			} else {
M
Mr.doob 已提交
278

M
Mr.doob 已提交
279 280
				// no helper for this object type
				return;
M
Mr.doob 已提交
281

M
Mr.doob 已提交
282
			}
M
Mr.doob 已提交
283

M
Mr.doob 已提交
284 285 286 287
			var picker = new THREE.Mesh( geometry, material );
			picker.name = 'picker';
			picker.userData.object = object;
			helper.add( picker );
M
Mr.doob 已提交
288

M
Mr.doob 已提交
289 290
			this.sceneHelpers.add( helper );
			this.helpers[ object.id ] = helper;
M
Mr.doob 已提交
291

M
Mr.doob 已提交
292
			this.signals.helperAdded.dispatch( helper );
M
Mr.doob 已提交
293 294 295 296

		};

	}(),
297 298 299

	removeHelper: function ( object ) {

300 301
		if ( this.helpers[ object.id ] !== undefined ) {

302 303 304
			var helper = this.helpers[ object.id ];
			helper.parent.remove( helper );

305 306
			delete this.helpers[ object.id ];

M
Mr.doob 已提交
307 308
			this.signals.helperRemoved.dispatch( helper );

309 310
		}

311 312 313 314
	},

	//

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
	addScript: function ( object, script ) {

		if ( this.scripts[ object.uuid ] === undefined ) {

			this.scripts[ object.uuid ] = [];

		}

		this.scripts[ object.uuid ].push( script );

		this.signals.scriptAdded.dispatch( script );

	},

	removeScript: function ( object, script ) {

		if ( this.scripts[ object.uuid ] === undefined ) return;

		var index = this.scripts[ object.uuid ].indexOf( script );

		if ( index !== - 1 ) {

			this.scripts[ object.uuid ].splice( index, 1 );

		}

		this.signals.scriptRemoved.dispatch( script );

	},

345 346 347 348
	getObjectMaterial: function ( object, slot ) {

		var material = object.material;

M
Mr.doob 已提交
349 350 351 352 353 354
		if ( Array.isArray( material ) ) {

			material = material[ slot ];

		}

355 356 357 358 359 360
		return material;

	},

	setObjectMaterial: function ( object, slot, newMaterial ) {

M
Mr.doob 已提交
361 362 363 364 365 366 367 368 369
		if ( Array.isArray( object.material ) ) {

			object.material[ slot ] = newMaterial;

		} else {

			object.material = newMaterial;

		}
370 371 372

	},

373 374
	//

375 376
	select: function ( object ) {

377
		if ( this.selected === object ) return;
378

379
		var uuid = null;
380

381
		if ( object !== null ) {
382

383
			uuid = object.uuid;
384 385 386

		}

387 388 389
		this.selected = object;

		this.config.setKey( 'selected', uuid );
390 391
		this.signals.objectSelected.dispatch( object );

392 393
	},

394 395
	selectById: function ( id ) {

396 397 398 399 400 401 402
		if ( id === this.camera.id ) {

			this.select( this.camera );
			return;

		}

403
		this.select( this.scene.getObjectById( id, true ) );
404

M
Mr.doob 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
	},

	selectByUuid: function ( uuid ) {

		var scope = this;

		this.scene.traverse( function ( child ) {

			if ( child.uuid === uuid ) {

				scope.select( child );

			}

		} );
420 421 422 423 424

	},

	deselect: function () {

M
Mr.doob 已提交
425
		this.select( null );
426

427 428 429 430 431 432 433 434 435 436 437 438
	},

	focus: function ( object ) {

		this.signals.objectFocused.dispatch( object );

	},

	focusById: function ( id ) {

		this.focus( this.scene.getObjectById( id, true ) );

M
Mr.doob 已提交
439 440
	},

M
Mr.doob 已提交
441 442
	clear: function () {

443
		this.history.clear();
444
		this.storage.clear();
445

M
Mr.doob 已提交
446
		this.camera.copy( this.DEFAULT_CAMERA );
M
Mr.doob 已提交
447 448
		this.scene.background.setHex( 0xaaaaaa );
		this.scene.fog = null;
M
Mr.doob 已提交
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

		var objects = this.scene.children;

		while ( objects.length > 0 ) {

			this.removeObject( objects[ 0 ] );

		}

		this.geometries = {};
		this.materials = {};
		this.textures = {};
		this.scripts = {};

		this.deselect();

465 466
		this.signals.editorCleared.dispatch();

M
Mr.doob 已提交
467 468
	},

M
Mr.doob 已提交
469 470 471 472 473 474 475 476 477 478
	//

	fromJSON: function ( json ) {

		var loader = new THREE.ObjectLoader();

		// backwards

		if ( json.scene === undefined ) {

479
			this.setScene( loader.parse( json ) );
M
Mr.doob 已提交
480 481 482 483 484 485
			return;

		}

		var camera = loader.parse( json.camera );

M
Mr.doob 已提交
486
		this.camera.copy( camera );
M
Mr.doob 已提交
487 488 489
		this.camera.aspect = this.DEFAULT_CAMERA.aspect;
		this.camera.updateProjectionMatrix();

M
Mr.doob 已提交
490 491
		this.history.fromJSON( json.history );
		this.scripts = json.scripts;
M
Mr.doob 已提交
492

M
Mr.doob 已提交
493 494 495 496 497 498
		this.setScene( loader.parse( json.scene ) );

	},

	toJSON: function () {

M
Mr.doob 已提交
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
		// scripts clean up

		var scene = this.scene;
		var scripts = this.scripts;

		for ( var key in scripts ) {

			var script = scripts[ key ];

			if ( script.length === 0 || scene.getObjectByProperty( 'uuid', key ) === undefined ) {

				delete scripts[ key ];

			}

		}

		//

M
Mr.doob 已提交
518 519
		return {

M
Mr.doob 已提交
520
			metadata: {},
M
Mr.doob 已提交
521
			project: {
T
thmasn 已提交
522 523
				gammaInput: this.config.getKey( 'project/renderer/gammaInput' ),
				gammaOutput: this.config.getKey( 'project/renderer/gammaOutput' ),
M
Mr.doob 已提交
524
				shadows: this.config.getKey( 'project/renderer/shadows' ),
M
Mr.doob 已提交
525 526
				vr: this.config.getKey( 'project/vr' )
			},
M
Mr.doob 已提交
527 528
			camera: this.camera.toJSON(),
			scene: this.scene.toJSON(),
529 530
			scripts: this.scripts,
			history: this.history.toJSON()
M
Mr.doob 已提交
531 532 533

		};

534 535 536 537 538 539 540 541
	},

	objectByUuid: function ( uuid ) {

		return this.scene.getObjectByProperty( 'uuid', uuid, true );

	},

542
	execute: function ( cmd, optionalName ) {
543

544
		this.history.execute( cmd, optionalName );
545 546 547 548 549 550 551 552 553 554 555 556 557

	},

	undo: function () {

		this.history.undo();

	},

	redo: function () {

		this.history.redo();

558 559
	}

M
Mr.doob 已提交
560
};