Editor.js 9.0 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.1, 10000 );
M
Mr.doob 已提交
8 9 10 11
	this.DEFAULT_CAMERA.name = 'Camera';
	this.DEFAULT_CAMERA.position.set( 20, 10, 20 );
	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

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

M
Mr.doob 已提交
45
		cameraChanged: new Signal(),
46

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

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

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

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

M
Mr.doob 已提交
59
		materialChanged: new Signal(),
M
Mr.doob 已提交
60

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

M
Mr.doob 已提交
65 66 67 68
		fogTypeChanged: new Signal(),
		fogColorChanged: new Signal(),
		fogParametersChanged: new Signal(),
		windowResize: new Signal(),
C
Chris Jubb 已提交
69

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

	};
M
Mr.doob 已提交
76

M
Mr.doob 已提交
77
	this.config = new Config( 'threejs-editor' );
78
	this.history = new History( this );
79
	this.storage = new Storage();
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';
M
Mr.doob 已提交
86

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

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

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

98 99 100 101
};

Editor.prototype = {

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

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

		this.signals.themeChanged.dispatch( value );

	},

110 111
	//

112 113
	setScene: function ( scene ) {

M
Mr.doob 已提交
114
		this.scene.uuid = scene.uuid;
115 116 117
		this.scene.name = scene.name;
		this.scene.userData = JSON.parse( JSON.stringify( scene.userData ) );

118 119 120 121
		// avoid render per object

		this.signals.sceneGraphChanged.active = false;

122 123 124 125 126 127
		while ( scene.children.length > 0 ) {

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

		}

128 129 130
		this.signals.sceneGraphChanged.active = true;
		this.signals.sceneGraphChanged.dispatch();

131 132 133 134
	},

	//

135 136
	addObject: function ( object ) {

137 138 139 140
		var scope = this;

		object.traverse( function ( child ) {

141 142 143
			if ( child.geometry !== undefined ) scope.addGeometry( child.geometry );
			if ( child.material !== undefined ) scope.addMaterial( child.material );

144 145 146 147
			scope.addHelper( child );

		} );

148 149 150
		this.scene.add( object );

		this.signals.objectAdded.dispatch( object );
151
		this.signals.sceneGraphChanged.dispatch();
152 153 154

	},

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	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();

	},

179
	nameObject: function ( object, name ) {
180 181 182 183 184 185

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

	},

186 187
	removeObject: function ( object ) {

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

190 191 192 193 194 195 196 197
		var scope = this;

		object.traverse( function ( child ) {

			scope.removeHelper( child );

		} );

198
		object.parent.remove( object );
M
Mr.doob 已提交
199 200

		this.signals.objectRemoved.dispatch( object );
201
		this.signals.sceneGraphChanged.dispatch();
202 203 204

	},

205
	addGeometry: function ( geometry ) {
206

207
		this.geometries[ geometry.uuid ] = geometry;
208 209 210

	},

211 212 213 214 215 216 217
	setGeometryName: function ( geometry, name ) {

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

	},

218 219
	addMaterial: function ( material ) {

220
		this.materials[ material.uuid ] = material;
221 222 223

	},

224 225 226 227 228 229 230
	setMaterialName: function ( material, name ) {

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

	},

231 232
	addTexture: function ( texture ) {

233
		this.textures[ texture.uuid ] = texture;
234 235 236

	},

237 238
	//

M
Mr.doob 已提交
239
	addHelper: function () {
240

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

M
Mr.doob 已提交
244
		return function ( object ) {
M
Mr.doob 已提交
245

M
Mr.doob 已提交
246
			var helper;
247

M
Mr.doob 已提交
248
			if ( object instanceof THREE.Camera ) {
249

250
				helper = new THREE.CameraHelper( object, 1 );
251 252

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

254
				helper = new THREE.PointLightHelper( object, 1 );
255

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

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

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

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

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

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

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

				helper = new THREE.SkeletonHelper( object );

M
Mr.doob 已提交
272
			} else {
M
Mr.doob 已提交
273

M
Mr.doob 已提交
274 275
				// no helper for this object type
				return;
M
Mr.doob 已提交
276

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

M
Mr.doob 已提交
279 280 281 282
			var picker = new THREE.Mesh( geometry, material );
			picker.name = 'picker';
			picker.userData.object = object;
			helper.add( picker );
M
Mr.doob 已提交
283

M
Mr.doob 已提交
284 285
			this.sceneHelpers.add( helper );
			this.helpers[ object.id ] = helper;
M
Mr.doob 已提交
286

M
Mr.doob 已提交
287
			this.signals.helperAdded.dispatch( helper );
M
Mr.doob 已提交
288 289 290 291

		};

	}(),
292 293 294

	removeHelper: function ( object ) {

295 296
		if ( this.helpers[ object.id ] !== undefined ) {

297 298 299
			var helper = this.helpers[ object.id ];
			helper.parent.remove( helper );

300 301
			delete this.helpers[ object.id ];

M
Mr.doob 已提交
302 303
			this.signals.helperRemoved.dispatch( helper );

304 305
		}

306 307 308 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
	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 );

	},

	//

342 343
	select: function ( object ) {

344
		if ( this.selected === object ) return;
345

346
		var uuid = null;
347

348
		if ( object !== null ) {
349

350
			uuid = object.uuid;
351 352 353

		}

354 355 356
		this.selected = object;

		this.config.setKey( 'selected', uuid );
357 358
		this.signals.objectSelected.dispatch( object );

359 360
	},

361 362
	selectById: function ( id ) {

363 364 365 366 367 368 369
		if ( id === this.camera.id ) {

			this.select( this.camera );
			return;

		}

370
		this.select( this.scene.getObjectById( id, true ) );
371

M
Mr.doob 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
	},

	selectByUuid: function ( uuid ) {

		var scope = this;

		this.scene.traverse( function ( child ) {

			if ( child.uuid === uuid ) {

				scope.select( child );

			}

		} );
387 388 389 390 391

	},

	deselect: function () {

M
Mr.doob 已提交
392
		this.select( null );
393

394 395 396 397 398 399 400 401 402 403 404 405
	},

	focus: function ( object ) {

		this.signals.objectFocused.dispatch( object );

	},

	focusById: function ( id ) {

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

M
Mr.doob 已提交
406 407
	},

M
Mr.doob 已提交
408 409
	clear: function () {

410
		this.history.clear();
411
		this.storage.clear();
412

M
Mr.doob 已提交
413
		this.camera.copy( this.DEFAULT_CAMERA );
M
Mr.doob 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

		var objects = this.scene.children;

		while ( objects.length > 0 ) {

			this.removeObject( objects[ 0 ] );

		}

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

		this.deselect();

430 431
		this.signals.editorCleared.dispatch();

M
Mr.doob 已提交
432 433
	},

M
Mr.doob 已提交
434 435 436 437 438 439 440 441 442 443
	//

	fromJSON: function ( json ) {

		var loader = new THREE.ObjectLoader();

		// backwards

		if ( json.scene === undefined ) {

444
			this.setScene( loader.parse( json ) );
M
Mr.doob 已提交
445 446 447 448 449 450
			return;

		}

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

M
Mr.doob 已提交
451
		this.camera.copy( camera );
M
Mr.doob 已提交
452 453 454
		this.camera.aspect = this.DEFAULT_CAMERA.aspect;
		this.camera.updateProjectionMatrix();

M
Mr.doob 已提交
455 456
		this.history.fromJSON( json.history );
		this.scripts = json.scripts;
M
Mr.doob 已提交
457

M
Mr.doob 已提交
458 459 460 461 462 463
		this.setScene( loader.parse( json.scene ) );

	},

	toJSON: function () {

M
Mr.doob 已提交
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
		// 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 已提交
483 484
		return {

M
Mr.doob 已提交
485
			metadata: {},
M
Mr.doob 已提交
486
			project: {
M
Mr.doob 已提交
487
				shadows: this.config.getKey( 'project/renderer/shadows' ),
M
Mr.doob 已提交
488
				editable: this.config.getKey( 'project/editable' ),
T
thmasn 已提交
489 490
				gammaInput: this.config.getKey( 'project/renderer/gammaInput' ),
				gammaOutput: this.config.getKey( 'project/renderer/gammaOutput' ),
M
Mr.doob 已提交
491 492
				vr: this.config.getKey( 'project/vr' )
			},
M
Mr.doob 已提交
493 494
			camera: this.camera.toJSON(),
			scene: this.scene.toJSON(),
495 496
			scripts: this.scripts,
			history: this.history.toJSON()
M
Mr.doob 已提交
497 498 499

		};

500 501 502 503 504 505 506 507
	},

	objectByUuid: function ( uuid ) {

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

	},

508
	execute: function ( cmd, optionalName ) {
509

510
		this.history.execute( cmd, optionalName );
511 512 513 514 515 516 517 518 519 520 521 522 523

	},

	undo: function () {

		this.history.undo();

	},

	redo: function () {

		this.history.redo();

524 525
	}

M
Mr.doob 已提交
526
};