зеркало из https://github.com/mozilla/Spoke.git
HierarchyPanel MVP
This commit is contained in:
Родитель
531c09e0a2
Коммит
29aa883e24
|
@ -26,6 +26,7 @@
|
|||
"react-router-dom": "^4.2.2",
|
||||
"react-select": "^1.2.1",
|
||||
"react-ui-tree": "^3.1.0",
|
||||
"signals": "^1.0.0",
|
||||
"source-map-support": "^0.5.6",
|
||||
"three": "^0.92.0"
|
||||
},
|
||||
|
|
|
@ -4,10 +4,10 @@ import styles from "./Editor.scss";
|
|||
import Modal from "react-modal";
|
||||
import { Mosaic } from "react-mosaic-component";
|
||||
|
||||
export default function Editor({ initialPanels, renderPanel, openModal, onCloseModal }) {
|
||||
export default function Editor({ initialPanels, renderPanel, openModal, onCloseModal, onPanelChange }) {
|
||||
return (
|
||||
<div className={styles.editor}>
|
||||
<Mosaic className="mosaic-theme" renderTile={renderPanel} initialValue={initialPanels} />
|
||||
<Mosaic className="mosaic-theme" renderTile={renderPanel} initialValue={initialPanels} onChange={onPanelChange} />
|
||||
<Modal
|
||||
isOpen={!!openModal}
|
||||
onRequestClose={onCloseModal}
|
||||
|
@ -25,5 +25,6 @@ Editor.propTypes = {
|
|||
initialPanels: PropTypes.object.isRequired,
|
||||
renderPanel: PropTypes.func.isRequired,
|
||||
openModal: PropTypes.object,
|
||||
onCloseModal: PropTypes.func
|
||||
onCloseModal: PropTypes.func,
|
||||
onPanelChange: PropTypes.func
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import PanelToolbar from "./PanelToolbar";
|
|||
|
||||
export default function Panel({ title, path, toolbarControls, children }) {
|
||||
return (
|
||||
<MosaicWindow title={title} path={path} toolbarControls={toolbarControls}>
|
||||
<MosaicWindow title={title} path={path} toolbarControls={toolbarControls} draggable>
|
||||
{children}
|
||||
</MosaicWindow>
|
||||
);
|
||||
|
|
|
@ -109,6 +109,10 @@ export default class EditorContainer extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
onPanelChange = e => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
renderPanel = (panelId, path) => {
|
||||
const PanelComponent = this.state.registeredPanels[panelId];
|
||||
return <PanelComponent path={path} />;
|
||||
|
@ -121,6 +125,7 @@ export default class EditorContainer extends Component {
|
|||
renderPanel={this.renderPanel}
|
||||
openModal={this.state.openModal}
|
||||
onCloseModal={this.onCloseModal}
|
||||
onPanelChange={this.onPanelChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import React from "react";
|
||||
import Editor from "../editor/Editor";
|
||||
|
||||
const editor = new Editor();
|
||||
const EditorContext = React.createContext(editor);
|
||||
|
||||
export function withEditor(Component) {
|
||||
return function EditorContextComponent(props) {
|
||||
return <EditorContext.Consumer>{editor => <Component {...props} editor={editor} />}</EditorContext.Consumer>;
|
||||
};
|
||||
}
|
|
@ -1,13 +1,87 @@
|
|||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Panel from "../components/Panel";
|
||||
import { withEditor } from "./EditorContext";
|
||||
import Tree from "react-ui-tree";
|
||||
import "../vendor/react-ui-tree/index.scss";
|
||||
import classNames from "classnames";
|
||||
|
||||
export default class HierarchyPanelContainer extends Component {
|
||||
function createNodeHierarchy(object) {
|
||||
return {
|
||||
object,
|
||||
collapsed: false,
|
||||
children: object.children.map(createNodeHierarchy)
|
||||
};
|
||||
}
|
||||
|
||||
class HierarchyPanelContainer extends Component {
|
||||
static propTypes = {
|
||||
path: PropTypes.array
|
||||
path: PropTypes.array,
|
||||
editor: PropTypes.object
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
tree: createNodeHierarchy(props.editor.scene)
|
||||
};
|
||||
|
||||
this.clicked = null;
|
||||
this.doubleClickTimeout = null;
|
||||
|
||||
const editor = this.props.editor;
|
||||
editor.signals.editorCleared.add(this.rebuildNodeHierarchy);
|
||||
editor.signals.sceneGraphChanged.add(this.rebuildNodeHierarchy);
|
||||
editor.signals.objectChanged.add(this.rebuildNodeHierarchy);
|
||||
editor.signals.objectSelected.add(this.rebuildNodeHierarchy);
|
||||
}
|
||||
|
||||
onChange = node => {
|
||||
this.ignoreObjectSelectedSignal = true;
|
||||
this.props.editor.selectById(node.object.id);
|
||||
this.ignoreObjectSelectedSignal = false;
|
||||
};
|
||||
|
||||
onClickNode = node => {
|
||||
if (this.clicked === node.object) {
|
||||
this.props.editor.focusById(node.object.id);
|
||||
}
|
||||
|
||||
this.clicked = node.object;
|
||||
|
||||
clearTimeout(this.doubleClickTimeout);
|
||||
this.doubleClickTimeout = setTimeout(() => {
|
||||
this.clicked = null;
|
||||
}, 500);
|
||||
};
|
||||
|
||||
rebuildNodeHierarchy = () => {
|
||||
this.setState({
|
||||
tree: createNodeHierarchy(this.props.editor.scene)
|
||||
});
|
||||
};
|
||||
|
||||
renderNode = node => {
|
||||
return (
|
||||
<span
|
||||
className={classNames("node", {
|
||||
"is-active": this.props.editor.selected && node.object.id === this.props.editor.selected.id
|
||||
})}
|
||||
onClick={() => this.onClickNode(node)}
|
||||
>
|
||||
{node.object.name}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Panel title="Hierarchy" path={this.props.path} />;
|
||||
return (
|
||||
<Panel title="Hierarchy" path={this.props.path}>
|
||||
<Tree paddingLeft={8} tree={this.state.tree} renderNode={this.renderNode} onChange={this.onChange} />
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withEditor(HierarchyPanelContainer);
|
||||
|
|
|
@ -1,88 +1,32 @@
|
|||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
window.THREE = require("three");
|
||||
require("three/examples/js/loaders/GLTFLoader");
|
||||
import Viewport from "../components/Viewport";
|
||||
import Panel from "../components/Panel";
|
||||
import { withEditor } from "./EditorContext";
|
||||
|
||||
export default class ViewportPanelContainer extends Component {
|
||||
class ViewportPanelContainer extends Component {
|
||||
static propTypes = {
|
||||
path: PropTypes.array,
|
||||
gltfURI: PropTypes.string
|
||||
editor: PropTypes.object
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.canvasRef = React.createRef();
|
||||
|
||||
this.renderer = null;
|
||||
this.editorScene = null;
|
||||
this.editorCamera = null;
|
||||
this.scene = null;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.gltfURI !== this.props.gltfURI) {
|
||||
this.gltfLoader.load(this.props.gltfURI, this.onGLTFLoad, undefined, this.onGLTFLoadError);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.initializeThreeScene();
|
||||
this.renderThreeScene();
|
||||
|
||||
window.addEventListener("resize", this.onResize);
|
||||
}
|
||||
|
||||
onResize = () => {
|
||||
const canvas = this.canvasRef.current;
|
||||
|
||||
this.editorCamera.aspect = canvas.offsetWidth / canvas.offsetHeight;
|
||||
this.editorCamera.updateProjectionMatrix();
|
||||
|
||||
this.renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
|
||||
|
||||
this.renderThreeScene();
|
||||
};
|
||||
|
||||
initializeThreeScene() {
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
canvas: this.canvasRef.current
|
||||
});
|
||||
|
||||
this.editorScene = new THREE.Scene();
|
||||
this.editorScene.name = "Editor Scene";
|
||||
this.editorScene = this.editorScene;
|
||||
window.editorScene = this.editorScene;
|
||||
|
||||
this.editorCamera = new THREE.PerspectiveCamera();
|
||||
this.editorCamera.name = "Editor Camera";
|
||||
this.editorScene.add(this.editorCamera);
|
||||
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
this.gltfLoader = new THREE.GLTFLoader();
|
||||
}
|
||||
|
||||
onGLTFLoad = ({ scene }) => {
|
||||
this.editorScene.add(scene);
|
||||
this.scene = scene;
|
||||
};
|
||||
|
||||
onGLTFLoadError = error => {
|
||||
console.log(error);
|
||||
};
|
||||
|
||||
renderThreeScene() {
|
||||
this.renderer.render(this.editorScene, this.editorCamera);
|
||||
this.props.editor.createRenderer(this.canvasRef.current);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Panel title="Viewport" path={this.props.path}>
|
||||
<Panel title="Viewport" path={this.props.path} toolbarControls={[]}>
|
||||
<Viewport ref={this.canvasRef} />
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withEditor(ViewportPanelContainer);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
export default class Command {
|
||||
constructor() {
|
||||
this.id = -1;
|
||||
this.inMemory = false;
|
||||
this.updatable = false;
|
||||
this.type = "";
|
||||
this.name = "";
|
||||
|
||||
this.editor = Command.editor;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const output = {};
|
||||
output.type = this.type;
|
||||
output.id = this.id;
|
||||
output.name = this.name;
|
||||
return output;
|
||||
}
|
||||
|
||||
fromJSON(json) {
|
||||
this.inMemory = true;
|
||||
this.type = json.type;
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,465 @@
|
|||
import signals from "signals";
|
||||
import THREE from "../vendor/three";
|
||||
import History from "./History";
|
||||
import Storage from "./Storage";
|
||||
import Viewport from "./Viewport";
|
||||
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
export default class Editor {
|
||||
constructor() {
|
||||
this.DEFAULT_CAMERA = new THREE.PerspectiveCamera(50, 1, 0.01, 1000);
|
||||
this.DEFAULT_CAMERA.name = "Camera";
|
||||
this.DEFAULT_CAMERA.position.set(0, 5, 10);
|
||||
this.DEFAULT_CAMERA.lookAt(new THREE.Vector3());
|
||||
|
||||
const Signal = signals.Signal;
|
||||
|
||||
this.signals = {
|
||||
// script
|
||||
|
||||
editScript: new Signal(),
|
||||
|
||||
// player
|
||||
|
||||
startPlayer: new Signal(),
|
||||
stopPlayer: new Signal(),
|
||||
|
||||
// actions
|
||||
|
||||
showModal: new Signal(),
|
||||
|
||||
// notifications
|
||||
|
||||
editorCleared: new Signal(),
|
||||
|
||||
savingStarted: new Signal(),
|
||||
savingFinished: new Signal(),
|
||||
|
||||
themeChanged: new Signal(),
|
||||
|
||||
transformModeChanged: new Signal(),
|
||||
snapChanged: new Signal(),
|
||||
spaceChanged: new Signal(),
|
||||
rendererChanged: new Signal(),
|
||||
|
||||
sceneBackgroundChanged: new Signal(),
|
||||
sceneFogChanged: new Signal(),
|
||||
sceneGraphChanged: new Signal(),
|
||||
|
||||
cameraChanged: new Signal(),
|
||||
|
||||
geometryChanged: new Signal(),
|
||||
|
||||
objectSelected: new Signal(),
|
||||
objectFocused: new Signal(),
|
||||
|
||||
objectAdded: new Signal(),
|
||||
objectChanged: new Signal(),
|
||||
objectRemoved: new Signal(),
|
||||
|
||||
helperAdded: new Signal(),
|
||||
helperRemoved: new Signal(),
|
||||
|
||||
materialChanged: new Signal(),
|
||||
|
||||
scriptAdded: new Signal(),
|
||||
scriptChanged: new Signal(),
|
||||
scriptRemoved: new Signal(),
|
||||
|
||||
windowResize: new Signal(),
|
||||
|
||||
showGridChanged: new Signal(),
|
||||
refreshSidebarObject3D: new Signal(),
|
||||
historyChanged: new Signal()
|
||||
};
|
||||
|
||||
this.history = new History(this);
|
||||
this.storage = new Storage();
|
||||
|
||||
this.camera = this.DEFAULT_CAMERA.clone();
|
||||
|
||||
this.scene = new THREE.Scene();
|
||||
this.scene.name = "Scene";
|
||||
this.scene.background = new THREE.Color(0xaaaaaa);
|
||||
|
||||
this.sceneHelpers = new THREE.Scene();
|
||||
|
||||
this.object = {};
|
||||
this.geometries = {};
|
||||
this.materials = {};
|
||||
this.textures = {};
|
||||
this.scripts = {};
|
||||
|
||||
this.selected = null;
|
||||
this.helpers = {};
|
||||
|
||||
this.viewport = null;
|
||||
|
||||
window.addEventListener("resize", this.onWindowResize, false);
|
||||
this.onWindowResize();
|
||||
}
|
||||
|
||||
onWindowResize = () => {
|
||||
this.signals.windowResize.dispatch();
|
||||
};
|
||||
|
||||
setTheme(value) {
|
||||
document.getElementById("theme").href = value;
|
||||
|
||||
this.signals.themeChanged.dispatch(value);
|
||||
}
|
||||
|
||||
createRenderer(canvas) {
|
||||
this.canvas = canvas;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas
|
||||
});
|
||||
|
||||
this.viewport = new Viewport(this);
|
||||
|
||||
this.signals.rendererChanged.dispatch(renderer);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
setScene(scene) {
|
||||
this.scene.uuid = scene.uuid;
|
||||
this.scene.name = scene.name;
|
||||
|
||||
if (scene.background !== null) this.scene.background = scene.background.clone();
|
||||
if (scene.fog !== null) this.scene.fog = scene.fog.clone();
|
||||
|
||||
this.scene.userData = JSON.parse(JSON.stringify(scene.userData));
|
||||
|
||||
// avoid render per object
|
||||
|
||||
this.signals.sceneGraphChanged.active = false;
|
||||
|
||||
while (scene.children.length > 0) {
|
||||
this.addObject(scene.children[0]);
|
||||
}
|
||||
|
||||
this.signals.sceneGraphChanged.active = true;
|
||||
this.signals.sceneGraphChanged.dispatch();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
addObject(object) {
|
||||
const scope = this;
|
||||
|
||||
object.traverse(function(child) {
|
||||
if (child.geometry !== undefined) scope.addGeometry(child.geometry);
|
||||
if (child.material !== undefined) scope.addMaterial(child.material);
|
||||
|
||||
scope.addHelper(child);
|
||||
});
|
||||
|
||||
this.scene.add(object);
|
||||
|
||||
this.signals.objectAdded.dispatch(object);
|
||||
this.signals.sceneGraphChanged.dispatch();
|
||||
}
|
||||
|
||||
moveObject(object, parent, before) {
|
||||
if (parent === undefined) {
|
||||
parent = this.scene;
|
||||
}
|
||||
|
||||
parent.add(object);
|
||||
|
||||
// sort children array
|
||||
|
||||
if (before !== undefined) {
|
||||
const index = parent.children.indexOf(before);
|
||||
parent.children.splice(index, 0, object);
|
||||
parent.children.pop();
|
||||
}
|
||||
|
||||
this.signals.sceneGraphChanged.dispatch();
|
||||
}
|
||||
|
||||
nameObject(object, name) {
|
||||
object.name = name;
|
||||
this.signals.sceneGraphChanged.dispatch();
|
||||
}
|
||||
|
||||
removeObject(object) {
|
||||
if (object.parent === null) return; // avoid deleting the camera or scene
|
||||
|
||||
const scope = this;
|
||||
|
||||
object.traverse(function(child) {
|
||||
scope.removeHelper(child);
|
||||
});
|
||||
|
||||
object.parent.remove(object);
|
||||
|
||||
this.signals.objectRemoved.dispatch(object);
|
||||
this.signals.sceneGraphChanged.dispatch();
|
||||
}
|
||||
|
||||
addGeometry(geometry) {
|
||||
this.geometries[geometry.uuid] = geometry;
|
||||
}
|
||||
|
||||
setGeometryName(geometry, name) {
|
||||
geometry.name = name;
|
||||
this.signals.sceneGraphChanged.dispatch();
|
||||
}
|
||||
|
||||
addMaterial(material) {
|
||||
this.materials[material.uuid] = material;
|
||||
}
|
||||
|
||||
setMaterialName(material, name) {
|
||||
material.name = name;
|
||||
this.signals.sceneGraphChanged.dispatch();
|
||||
}
|
||||
|
||||
addTexture(texture) {
|
||||
this.textures[texture.uuid] = texture;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
addHelper = (function() {
|
||||
const geometry = new THREE.SphereBufferGeometry(2, 4, 2);
|
||||
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, visible: false });
|
||||
|
||||
return function(object) {
|
||||
let helper;
|
||||
|
||||
if (object instanceof THREE.Camera) {
|
||||
helper = new THREE.CameraHelper(object, 1);
|
||||
} else if (object instanceof THREE.PointLight) {
|
||||
helper = new THREE.PointLightHelper(object, 1);
|
||||
} else if (object instanceof THREE.DirectionalLight) {
|
||||
helper = new THREE.DirectionalLightHelper(object, 1);
|
||||
} else if (object instanceof THREE.SpotLight) {
|
||||
helper = new THREE.SpotLightHelper(object, 1);
|
||||
} else if (object instanceof THREE.HemisphereLight) {
|
||||
helper = new THREE.HemisphereLightHelper(object, 1);
|
||||
} else if (object instanceof THREE.SkinnedMesh) {
|
||||
helper = new THREE.SkeletonHelper(object);
|
||||
} else {
|
||||
// no helper for this object type
|
||||
return;
|
||||
}
|
||||
|
||||
const picker = new THREE.Mesh(geometry, material);
|
||||
picker.name = "picker";
|
||||
picker.userData.object = object;
|
||||
helper.add(picker);
|
||||
|
||||
this.sceneHelpers.add(helper);
|
||||
this.helpers[object.id] = helper;
|
||||
|
||||
this.signals.helperAdded.dispatch(helper);
|
||||
};
|
||||
})();
|
||||
|
||||
removeHelper(object) {
|
||||
if (this.helpers[object.id] !== undefined) {
|
||||
const helper = this.helpers[object.id];
|
||||
helper.parent.remove(helper);
|
||||
|
||||
delete this.helpers[object.id];
|
||||
|
||||
this.signals.helperRemoved.dispatch(helper);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
addScript(object, script) {
|
||||
if (this.scripts[object.uuid] === undefined) {
|
||||
this.scripts[object.uuid] = [];
|
||||
}
|
||||
|
||||
this.scripts[object.uuid].push(script);
|
||||
|
||||
this.signals.scriptAdded.dispatch(script);
|
||||
}
|
||||
|
||||
removeScript(object, script) {
|
||||
if (this.scripts[object.uuid] === undefined) return;
|
||||
|
||||
const index = this.scripts[object.uuid].indexOf(script);
|
||||
|
||||
if (index !== -1) {
|
||||
this.scripts[object.uuid].splice(index, 1);
|
||||
}
|
||||
|
||||
this.signals.scriptRemoved.dispatch(script);
|
||||
}
|
||||
|
||||
getObjectMaterial(object, slot) {
|
||||
let material = object.material;
|
||||
|
||||
if (Array.isArray(material)) {
|
||||
material = material[slot];
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
setObjectMaterial(object, slot, newMaterial) {
|
||||
if (Array.isArray(object.material)) {
|
||||
object.material[slot] = newMaterial;
|
||||
} else {
|
||||
object.material = newMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
select(object) {
|
||||
if (this.selected === object) return;
|
||||
|
||||
let uuid = null;
|
||||
|
||||
if (object !== null) {
|
||||
uuid = object.uuid;
|
||||
}
|
||||
|
||||
this.selected = object;
|
||||
|
||||
this.signals.objectSelected.dispatch(object);
|
||||
}
|
||||
|
||||
selectById(id) {
|
||||
if (id === this.scene.id) {
|
||||
this.select(this.scene);
|
||||
return;
|
||||
}
|
||||
|
||||
if (id === this.camera.id) {
|
||||
this.select(this.camera);
|
||||
return;
|
||||
}
|
||||
|
||||
this.select(this.scene.getObjectById(id, true));
|
||||
}
|
||||
|
||||
selectByUuid(uuid) {
|
||||
const scope = this;
|
||||
|
||||
this.scene.traverse(function(child) {
|
||||
if (child.uuid === uuid) {
|
||||
scope.select(child);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deselect() {
|
||||
this.select(null);
|
||||
}
|
||||
|
||||
focus(object) {
|
||||
this.signals.objectFocused.dispatch(object);
|
||||
}
|
||||
|
||||
focusById(id) {
|
||||
this.focus(this.scene.getObjectById(id, true));
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.history.clear();
|
||||
this.storage.clear();
|
||||
|
||||
this.camera.copy(this.DEFAULT_CAMERA);
|
||||
this.scene.background.setHex(0xaaaaaa);
|
||||
this.scene.fog = null;
|
||||
|
||||
const objects = this.scene.children;
|
||||
|
||||
while (objects.length > 0) {
|
||||
this.removeObject(objects[0]);
|
||||
}
|
||||
|
||||
this.geometries = {};
|
||||
this.materials = {};
|
||||
this.textures = {};
|
||||
this.scripts = {};
|
||||
|
||||
this.deselect();
|
||||
|
||||
this.signals.editorCleared.dispatch();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fromJSON(json) {
|
||||
const loader = new THREE.ObjectLoader();
|
||||
|
||||
// backwards
|
||||
|
||||
if (json.scene === undefined) {
|
||||
this.setScene(loader.parse(json));
|
||||
return;
|
||||
}
|
||||
|
||||
const camera = loader.parse(json.camera);
|
||||
|
||||
this.camera.copy(camera);
|
||||
this.camera.aspect = this.DEFAULT_CAMERA.aspect;
|
||||
this.camera.updateProjectionMatrix();
|
||||
|
||||
this.history.fromJSON(json.history);
|
||||
this.scripts = json.scripts;
|
||||
|
||||
this.setScene(loader.parse(json.scene));
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
// scripts clean up
|
||||
|
||||
const scene = this.scene;
|
||||
const scripts = this.scripts;
|
||||
|
||||
for (const key in scripts) {
|
||||
const script = scripts[key];
|
||||
|
||||
if (script.length === 0 || scene.getObjectByProperty("uuid", key) === undefined) {
|
||||
delete scripts[key];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
return {
|
||||
metadata: {},
|
||||
project: {
|
||||
gammaInput: this.config.getKey("project/renderer/gammaInput"),
|
||||
gammaOutput: this.config.getKey("project/renderer/gammaOutput"),
|
||||
shadows: this.config.getKey("project/renderer/shadows"),
|
||||
vr: this.config.getKey("project/vr")
|
||||
},
|
||||
camera: this.camera.toJSON(),
|
||||
scene: this.scene.toJSON(),
|
||||
scripts: this.scripts,
|
||||
history: this.history.toJSON()
|
||||
};
|
||||
}
|
||||
|
||||
objectByUuid(uuid) {
|
||||
return this.scene.getObjectByProperty("uuid", uuid, true);
|
||||
}
|
||||
|
||||
execute(cmd, optionalName) {
|
||||
this.history.execute(cmd, optionalName);
|
||||
}
|
||||
|
||||
undo() {
|
||||
this.history.undo();
|
||||
}
|
||||
|
||||
redo() {
|
||||
this.history.redo();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
import Command from "./Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
export default class History {
|
||||
constructor(editor) {
|
||||
this.editor = editor;
|
||||
this.undos = [];
|
||||
this.redos = [];
|
||||
this.lastCmdTime = new Date();
|
||||
this.idCounter = 0;
|
||||
|
||||
this.historyDisabled = false;
|
||||
this.config = editor.config;
|
||||
|
||||
//Set editor-reference in Command
|
||||
|
||||
Command.editor = editor;
|
||||
|
||||
// signals
|
||||
|
||||
const scope = this;
|
||||
|
||||
this.editor.signals.startPlayer.add(function() {
|
||||
scope.historyDisabled = true;
|
||||
});
|
||||
|
||||
this.editor.signals.stopPlayer.add(function() {
|
||||
scope.historyDisabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
execute(cmd, optionalName) {
|
||||
const lastCmd = this.undos[this.undos.length - 1];
|
||||
const timeDifference = new Date().getTime() - this.lastCmdTime.getTime();
|
||||
|
||||
const isUpdatableCmd =
|
||||
lastCmd &&
|
||||
lastCmd.updatable &&
|
||||
cmd.updatable &&
|
||||
lastCmd.object === cmd.object &&
|
||||
lastCmd.type === cmd.type &&
|
||||
lastCmd.script === cmd.script &&
|
||||
lastCmd.attributeName === cmd.attributeName;
|
||||
|
||||
if (isUpdatableCmd && cmd.type === "SetScriptValueCommand") {
|
||||
// When the cmd.type is "SetScriptValueCommand" the timeDifference is ignored
|
||||
|
||||
lastCmd.update(cmd);
|
||||
cmd = lastCmd;
|
||||
} else if (isUpdatableCmd && timeDifference < 500) {
|
||||
lastCmd.update(cmd);
|
||||
cmd = lastCmd;
|
||||
} else {
|
||||
// the command is not updatable and is added as a new part of the history
|
||||
|
||||
this.undos.push(cmd);
|
||||
cmd.id = ++this.idCounter;
|
||||
}
|
||||
cmd.name = optionalName !== undefined ? optionalName : cmd.name;
|
||||
cmd.execute();
|
||||
cmd.inMemory = true;
|
||||
|
||||
if (this.config.getKey("settings/history")) {
|
||||
cmd.json = cmd.toJSON(); // serialize the cmd immediately after execution and append the json to the cmd
|
||||
}
|
||||
this.lastCmdTime = new Date();
|
||||
|
||||
// clearing all the redo-commands
|
||||
|
||||
this.redos = [];
|
||||
this.editor.signals.historyChanged.dispatch(cmd);
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.historyDisabled) {
|
||||
alert("Undo/Redo disabled while scene is playing.");
|
||||
return;
|
||||
}
|
||||
|
||||
let cmd = undefined;
|
||||
|
||||
if (this.undos.length > 0) {
|
||||
cmd = this.undos.pop();
|
||||
|
||||
if (cmd.inMemory === false) {
|
||||
cmd.fromJSON(cmd.json);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd !== undefined) {
|
||||
cmd.undo();
|
||||
this.redos.push(cmd);
|
||||
this.editor.signals.historyChanged.dispatch(cmd);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
redo() {
|
||||
if (this.historyDisabled) {
|
||||
alert("Undo/Redo disabled while scene is playing.");
|
||||
return;
|
||||
}
|
||||
|
||||
let cmd = undefined;
|
||||
|
||||
if (this.redos.length > 0) {
|
||||
cmd = this.redos.pop();
|
||||
|
||||
if (cmd.inMemory === false) {
|
||||
cmd.fromJSON(cmd.json);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd !== undefined) {
|
||||
cmd.execute();
|
||||
this.undos.push(cmd);
|
||||
this.editor.signals.historyChanged.dispatch(cmd);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const history = {};
|
||||
history.undos = [];
|
||||
history.redos = [];
|
||||
|
||||
if (!this.config.getKey("settings/history")) {
|
||||
return history;
|
||||
}
|
||||
|
||||
// Append Undos to History
|
||||
|
||||
for (let i = 0; i < this.undos.length; i++) {
|
||||
if (this.undos[i].hasOwnProperty("json")) {
|
||||
history.undos.push(this.undos[i].json);
|
||||
}
|
||||
}
|
||||
|
||||
// Append Redos to History
|
||||
|
||||
for (let i = 0; i < this.redos.length; i++) {
|
||||
if (this.redos[i].hasOwnProperty("json")) {
|
||||
history.redos.push(this.redos[i].json);
|
||||
}
|
||||
}
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
fromJSON(json) {
|
||||
if (json === undefined) return;
|
||||
|
||||
for (let i = 0; i < json.undos.length; i++) {
|
||||
const cmdJSON = json.undos[i];
|
||||
const cmd = new window[cmdJSON.type](); // creates a new object of type "json.type"
|
||||
cmd.json = cmdJSON;
|
||||
cmd.id = cmdJSON.id;
|
||||
cmd.name = cmdJSON.name;
|
||||
this.undos.push(cmd);
|
||||
this.idCounter = cmdJSON.id > this.idCounter ? cmdJSON.id : this.idCounter; // set last used idCounter
|
||||
}
|
||||
|
||||
for (let i = 0; i < json.redos.length; i++) {
|
||||
const cmdJSON = json.redos[i];
|
||||
const cmd = new window[cmdJSON.type](); // creates a new object of type "json.type"
|
||||
cmd.json = cmdJSON;
|
||||
cmd.id = cmdJSON.id;
|
||||
cmd.name = cmdJSON.name;
|
||||
this.redos.push(cmd);
|
||||
this.idCounter = cmdJSON.id > this.idCounter ? cmdJSON.id : this.idCounter; // set last used idCounter
|
||||
}
|
||||
|
||||
// Select the last executed undo-command
|
||||
this.editor.signals.historyChanged.dispatch(this.undos[this.undos.length - 1]);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.undos = [];
|
||||
this.redos = [];
|
||||
this.idCounter = 0;
|
||||
|
||||
this.editor.signals.historyChanged.dispatch();
|
||||
}
|
||||
|
||||
goToState(id) {
|
||||
if (this.historyDisabled) {
|
||||
alert("Undo/Redo disabled while scene is playing.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = false;
|
||||
this.editor.signals.historyChanged.active = false;
|
||||
|
||||
let cmd = this.undos.length > 0 ? this.undos[this.undos.length - 1] : undefined; // next cmd to pop
|
||||
|
||||
if (cmd === undefined || id > cmd.id) {
|
||||
cmd = this.redo();
|
||||
while (cmd !== undefined && id > cmd.id) {
|
||||
cmd = this.redo();
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line
|
||||
while (true) {
|
||||
cmd = this.undos[this.undos.length - 1]; // next cmd to pop
|
||||
|
||||
if (cmd === undefined || id === cmd.id) break;
|
||||
|
||||
this.undo();
|
||||
}
|
||||
}
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = true;
|
||||
this.editor.signals.historyChanged.active = true;
|
||||
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
this.editor.signals.historyChanged.dispatch(cmd);
|
||||
}
|
||||
|
||||
enableSerialization(id) {
|
||||
/**
|
||||
* because there might be commands in this.undos and this.redos
|
||||
* which have not been serialized with .toJSON() we go back
|
||||
* to the oldest command and redo one command after the other
|
||||
* while also calling .toJSON() on them.
|
||||
*/
|
||||
|
||||
this.goToState(-1);
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = false;
|
||||
this.editor.signals.historyChanged.active = false;
|
||||
|
||||
let cmd = this.redo();
|
||||
while (cmd !== undefined) {
|
||||
if (!cmd.hasOwnProperty("json")) {
|
||||
cmd.json = cmd.toJSON();
|
||||
}
|
||||
cmd = this.redo();
|
||||
}
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = true;
|
||||
this.editor.signals.historyChanged.active = true;
|
||||
|
||||
this.goToState(id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
const Storage = function() {
|
||||
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
||||
|
||||
if (indexedDB === undefined) {
|
||||
console.warn("Storage: IndexedDB not available.");
|
||||
return { init: function() {}, get: function() {}, set: function() {}, clear: function() {} };
|
||||
}
|
||||
|
||||
const name = "threejs-editor";
|
||||
const version = 1;
|
||||
|
||||
let database;
|
||||
|
||||
return {
|
||||
init: function(callback) {
|
||||
const request = indexedDB.open(name, version);
|
||||
request.onupgradeneeded = function(event) {
|
||||
const db = event.target.result;
|
||||
|
||||
if (db.objectStoreNames.contains("states") === false) {
|
||||
db.createObjectStore("states");
|
||||
}
|
||||
};
|
||||
request.onsuccess = function(event) {
|
||||
database = event.target.result;
|
||||
|
||||
callback();
|
||||
};
|
||||
request.onerror = function(event) {
|
||||
console.error("IndexedDB", event);
|
||||
};
|
||||
},
|
||||
|
||||
get: function(callback) {
|
||||
const transaction = database.transaction(["states"], "readwrite");
|
||||
const objectStore = transaction.objectStore("states");
|
||||
const request = objectStore.get(0);
|
||||
request.onsuccess = function(event) {
|
||||
callback(event.target.result);
|
||||
};
|
||||
},
|
||||
|
||||
set: function(data) {
|
||||
const start = performance.now();
|
||||
|
||||
const transaction = database.transaction(["states"], "readwrite");
|
||||
const objectStore = transaction.objectStore("states");
|
||||
const request = objectStore.put(data, 0);
|
||||
request.onsuccess = function() {
|
||||
console.log(
|
||||
"[" + /\d\d\:\d\d\:\d\d/.exec(new Date())[0] + "]",
|
||||
"Saved state to IndexedDB. " + (performance.now() - start).toFixed(2) + "ms"
|
||||
);
|
||||
};
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
if (database === undefined) return;
|
||||
|
||||
const transaction = database.transaction(["states"], "readwrite");
|
||||
const objectStore = transaction.objectStore("states");
|
||||
const request = objectStore.clear();
|
||||
request.onsuccess = function() {
|
||||
console.log("[" + /\d\d\:\d\d\:\d\d/.exec(new Date())[0] + "]", "Cleared IndexedDB.");
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default Storage;
|
|
@ -0,0 +1,398 @@
|
|||
import THREE from "../vendor/three";
|
||||
import SetPositionCommand from "./commands/SetPositionCommand";
|
||||
import SetRotationCommand from "./commands/SetRotationCommand";
|
||||
import SetScaleCommand from "./commands/SetScaleCommand";
|
||||
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
export default class Viewport {
|
||||
constructor(editor) {
|
||||
const signals = editor.signals;
|
||||
|
||||
//
|
||||
|
||||
let renderer = null;
|
||||
|
||||
const canvas = editor.canvas;
|
||||
const camera = editor.camera;
|
||||
const scene = editor.scene;
|
||||
const sceneHelpers = editor.sceneHelpers;
|
||||
|
||||
const objects = [];
|
||||
|
||||
// helpers
|
||||
|
||||
const grid = new THREE.GridHelper(30, 30, 0x444444, 0x888888);
|
||||
sceneHelpers.add(grid);
|
||||
|
||||
const array = grid.geometry.attributes.color.array;
|
||||
|
||||
for (let i = 0; i < array.length; i += 60) {
|
||||
for (let j = 0; j < 12; j++) {
|
||||
array[i + j] = 0.26;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const box = new THREE.Box3();
|
||||
|
||||
const selectionBox = new THREE.BoxHelper();
|
||||
selectionBox.material.depthTest = false;
|
||||
selectionBox.material.transparent = true;
|
||||
selectionBox.visible = false;
|
||||
sceneHelpers.add(selectionBox);
|
||||
|
||||
let objectPositionOnDown = null;
|
||||
let objectRotationOnDown = null;
|
||||
let objectScaleOnDown = null;
|
||||
|
||||
function render() {
|
||||
sceneHelpers.updateMatrixWorld();
|
||||
scene.updateMatrixWorld();
|
||||
|
||||
renderer.render(scene, camera);
|
||||
renderer.render(sceneHelpers, camera);
|
||||
}
|
||||
|
||||
const transformControls = new THREE.TransformControls(camera, canvas);
|
||||
transformControls.addEventListener("change", function() {
|
||||
const object = transformControls.object;
|
||||
|
||||
if (object !== undefined) {
|
||||
selectionBox.setFromObject(object);
|
||||
|
||||
if (editor.helpers[object.id] !== undefined) {
|
||||
editor.helpers[object.id].update();
|
||||
}
|
||||
|
||||
signals.refreshSidebarObject3D.dispatch(object);
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
sceneHelpers.add(transformControls);
|
||||
|
||||
// object picking
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const mouse = new THREE.Vector2();
|
||||
|
||||
// events
|
||||
|
||||
function getIntersects(point, objects) {
|
||||
mouse.set(point.x * 2 - 1, -(point.y * 2) + 1);
|
||||
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
return raycaster.intersectObjects(objects);
|
||||
}
|
||||
|
||||
const onDownPosition = new THREE.Vector2();
|
||||
const onUpPosition = new THREE.Vector2();
|
||||
const onDoubleClickPosition = new THREE.Vector2();
|
||||
|
||||
function getMousePosition(dom, x, y) {
|
||||
const rect = dom.getBoundingClientRect();
|
||||
return [(x - rect.left) / rect.width, (y - rect.top) / rect.height];
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
if (onDownPosition.distanceTo(onUpPosition) === 0) {
|
||||
const intersects = getIntersects(onUpPosition, objects);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const object = intersects[0].object;
|
||||
|
||||
if (object.userData.object !== undefined) {
|
||||
// helper
|
||||
|
||||
editor.select(object.userData.object);
|
||||
} else {
|
||||
editor.select(object);
|
||||
}
|
||||
} else {
|
||||
editor.select(null);
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp(event) {
|
||||
const array = getMousePosition(canvas, event.clientX, event.clientY);
|
||||
onUpPosition.fromArray(array);
|
||||
|
||||
handleClick();
|
||||
|
||||
document.removeEventListener("mouseup", onMouseUp, false);
|
||||
}
|
||||
|
||||
function onMouseDown(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const array = getMousePosition(canvas, event.clientX, event.clientY);
|
||||
onDownPosition.fromArray(array);
|
||||
|
||||
document.addEventListener("mouseup", onMouseUp, false);
|
||||
}
|
||||
|
||||
function onTouchEnd(event) {
|
||||
const touch = event.changedTouches[0];
|
||||
|
||||
const array = getMousePosition(canvas, touch.clientX, touch.clientY);
|
||||
onUpPosition.fromArray(array);
|
||||
|
||||
handleClick();
|
||||
|
||||
document.removeEventListener("touchend", onTouchEnd, false);
|
||||
}
|
||||
|
||||
function onTouchStart(event) {
|
||||
const touch = event.changedTouches[0];
|
||||
|
||||
const array = getMousePosition(canvas, touch.clientX, touch.clientY);
|
||||
onDownPosition.fromArray(array);
|
||||
|
||||
document.addEventListener("touchend", onTouchEnd, false);
|
||||
}
|
||||
|
||||
function onDoubleClick(event) {
|
||||
const array = getMousePosition(canvas, event.clientX, event.clientY);
|
||||
onDoubleClickPosition.fromArray(array);
|
||||
|
||||
const intersects = getIntersects(onDoubleClickPosition, objects);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const intersect = intersects[0];
|
||||
|
||||
signals.objectFocused.dispatch(intersect.object);
|
||||
}
|
||||
}
|
||||
|
||||
canvas.addEventListener("mousedown", onMouseDown, false);
|
||||
canvas.addEventListener("touchstart", onTouchStart, false);
|
||||
canvas.addEventListener("dblclick", onDoubleClick, false);
|
||||
|
||||
// controls need to be added *after* main logic,
|
||||
// otherwise controls.enabled doesn't work.
|
||||
|
||||
const controls = new THREE.EditorControls(camera, canvas);
|
||||
controls.addEventListener("change", function() {
|
||||
transformControls.update();
|
||||
signals.cameraChanged.dispatch(camera);
|
||||
});
|
||||
|
||||
transformControls.addEventListener("mouseDown", function() {
|
||||
const object = transformControls.object;
|
||||
|
||||
objectPositionOnDown = object.position.clone();
|
||||
objectRotationOnDown = object.rotation.clone();
|
||||
objectScaleOnDown = object.scale.clone();
|
||||
|
||||
controls.enabled = false;
|
||||
});
|
||||
transformControls.addEventListener("mouseUp", function() {
|
||||
const object = transformControls.object;
|
||||
|
||||
if (object !== undefined) {
|
||||
switch (transformControls.getMode()) {
|
||||
case "translate":
|
||||
if (!objectPositionOnDown.equals(object.position)) {
|
||||
editor.execute(new SetPositionCommand(object, object.position, objectPositionOnDown));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "rotate":
|
||||
if (!objectRotationOnDown.equals(object.rotation)) {
|
||||
editor.execute(new SetRotationCommand(object, object.rotation, objectRotationOnDown));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "scale":
|
||||
if (!objectScaleOnDown.equals(object.scale)) {
|
||||
editor.execute(new SetScaleCommand(object, object.scale, objectScaleOnDown));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
controls.enabled = true;
|
||||
});
|
||||
|
||||
// signals
|
||||
|
||||
signals.editorCleared.add(function() {
|
||||
controls.center.set(0, 0, 0);
|
||||
render();
|
||||
});
|
||||
|
||||
signals.transformModeChanged.add(function(mode) {
|
||||
transformControls.setMode(mode);
|
||||
});
|
||||
|
||||
signals.snapChanged.add(function(dist) {
|
||||
transformControls.setTranslationSnap(dist);
|
||||
});
|
||||
|
||||
signals.spaceChanged.add(function(space) {
|
||||
transformControls.setSpace(space);
|
||||
});
|
||||
|
||||
signals.rendererChanged.add(function(newRenderer) {
|
||||
renderer = newRenderer;
|
||||
|
||||
renderer.autoClear = false;
|
||||
renderer.autoUpdateScene = false;
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
signals.sceneGraphChanged.add(function() {
|
||||
render();
|
||||
});
|
||||
|
||||
signals.cameraChanged.add(function() {
|
||||
render();
|
||||
});
|
||||
|
||||
signals.objectSelected.add(function(object) {
|
||||
selectionBox.visible = false;
|
||||
transformControls.detach();
|
||||
|
||||
if (object !== null && object !== scene && object !== camera) {
|
||||
box.setFromObject(object);
|
||||
|
||||
if (box.isEmpty() === false) {
|
||||
selectionBox.setFromObject(object);
|
||||
selectionBox.visible = true;
|
||||
}
|
||||
|
||||
transformControls.attach(object);
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
signals.objectFocused.add(function(object) {
|
||||
controls.focus(object);
|
||||
});
|
||||
|
||||
signals.geometryChanged.add(function(object) {
|
||||
if (object !== undefined) {
|
||||
selectionBox.setFromObject(object);
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
signals.objectAdded.add(function(object) {
|
||||
object.traverse(function(child) {
|
||||
objects.push(child);
|
||||
});
|
||||
});
|
||||
|
||||
signals.objectChanged.add(function(object) {
|
||||
if (editor.selected === object) {
|
||||
selectionBox.setFromObject(object);
|
||||
transformControls.update();
|
||||
}
|
||||
|
||||
if (object instanceof THREE.PerspectiveCamera) {
|
||||
object.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
if (editor.helpers[object.id] !== undefined) {
|
||||
editor.helpers[object.id].update();
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
signals.objectRemoved.add(function(object) {
|
||||
object.traverse(function(child) {
|
||||
objects.splice(objects.indexOf(child), 1);
|
||||
});
|
||||
});
|
||||
|
||||
signals.helperAdded.add(function(object) {
|
||||
objects.push(object.getObjectByName("picker"));
|
||||
});
|
||||
|
||||
signals.helperRemoved.add(function(object) {
|
||||
objects.splice(objects.indexOf(object.getObjectByName("picker")), 1);
|
||||
});
|
||||
|
||||
signals.materialChanged.add(function() {
|
||||
render();
|
||||
});
|
||||
|
||||
// fog
|
||||
|
||||
signals.sceneBackgroundChanged.add(function(backgroundColor) {
|
||||
scene.background.setHex(backgroundColor);
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
let currentFogType = null;
|
||||
|
||||
signals.sceneFogChanged.add(function(fogType, fogColor, fogNear, fogFar, fogDensity) {
|
||||
if (currentFogType !== fogType) {
|
||||
switch (fogType) {
|
||||
case "None":
|
||||
scene.fog = null;
|
||||
break;
|
||||
case "Fog":
|
||||
scene.fog = new THREE.Fog();
|
||||
break;
|
||||
case "FogExp2":
|
||||
scene.fog = new THREE.FogExp2();
|
||||
break;
|
||||
}
|
||||
|
||||
currentFogType = fogType;
|
||||
}
|
||||
|
||||
if (scene.fog instanceof THREE.Fog) {
|
||||
scene.fog.color.setHex(fogColor);
|
||||
scene.fog.near = fogNear;
|
||||
scene.fog.far = fogFar;
|
||||
} else if (scene.fog instanceof THREE.FogExp2) {
|
||||
scene.fog.color.setHex(fogColor);
|
||||
scene.fog.density = fogDensity;
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
signals.windowResize.add(function() {
|
||||
// TODO: Move this out?
|
||||
|
||||
editor.DEFAULT_CAMERA.aspect = canvas.offsetWidth / canvas.offsetHeight;
|
||||
editor.DEFAULT_CAMERA.updateProjectionMatrix();
|
||||
|
||||
camera.aspect = canvas.offsetWidth / canvas.offsetHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
signals.showGridChanged.add(function(showGrid) {
|
||||
grid.visible = showGrid;
|
||||
render();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import THREE from "../../vendor/three";
|
||||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
export default class AddObjectCommand extends Command {
|
||||
constructor(object) {
|
||||
super();
|
||||
this.type = "AddObjectCommand";
|
||||
|
||||
this.object = object;
|
||||
if (object !== undefined) {
|
||||
this.name = "Add Object: " + object.name;
|
||||
}
|
||||
}
|
||||
|
||||
execute() {
|
||||
this.editor.addObject(this.object);
|
||||
this.editor.select(this.object);
|
||||
}
|
||||
|
||||
undo() {
|
||||
this.editor.removeObject(this.object);
|
||||
this.editor.deselect();
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const output = super.toJSON();
|
||||
output.object = this.object.toJSON();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
fromJSON(json) {
|
||||
super.fromJSON(json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.object.object.uuid);
|
||||
|
||||
if (this.object === undefined) {
|
||||
const loader = new THREE.ObjectLoader();
|
||||
this.object = loader.parse(json.object);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param newParent THREE.Object3D
|
||||
* @param newBefore THREE.Object3D
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const MoveObjectCommand = function(object, newParent, newBefore) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "MoveObjectCommand";
|
||||
this.name = "Move Object";
|
||||
|
||||
this.object = object;
|
||||
this.oldParent = object !== undefined ? object.parent : undefined;
|
||||
this.oldIndex = this.oldParent !== undefined ? this.oldParent.children.indexOf(this.object) : undefined;
|
||||
this.newParent = newParent;
|
||||
|
||||
if (newBefore !== undefined) {
|
||||
this.newIndex = newParent !== undefined ? newParent.children.indexOf(newBefore) : undefined;
|
||||
} else {
|
||||
this.newIndex = newParent !== undefined ? newParent.children.length : undefined;
|
||||
}
|
||||
|
||||
if (this.oldParent === this.newParent && this.newIndex > this.oldIndex) {
|
||||
this.newIndex--;
|
||||
}
|
||||
|
||||
this.newBefore = newBefore;
|
||||
};
|
||||
|
||||
MoveObjectCommand.prototype = {
|
||||
execute: function() {
|
||||
this.oldParent.remove(this.object);
|
||||
|
||||
const children = this.newParent.children;
|
||||
children.splice(this.newIndex, 0, this.object);
|
||||
this.object.parent = this.newParent;
|
||||
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.newParent.remove(this.object);
|
||||
|
||||
const children = this.oldParent.children;
|
||||
children.splice(this.oldIndex, 0, this.object);
|
||||
this.object.parent = this.oldParent;
|
||||
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.newParentUuid = this.newParent.uuid;
|
||||
output.oldParentUuid = this.oldParent.uuid;
|
||||
output.newIndex = this.newIndex;
|
||||
output.oldIndex = this.oldIndex;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.oldParent = this.editor.objectByUuid(json.oldParentUuid);
|
||||
if (this.oldParent === undefined) {
|
||||
this.oldParent = this.editor.scene;
|
||||
}
|
||||
this.newParent = this.editor.objectByUuid(json.newParentUuid);
|
||||
if (this.newParent === undefined) {
|
||||
this.newParent = this.editor.scene;
|
||||
}
|
||||
this.newIndex = json.newIndex;
|
||||
this.oldIndex = json.oldIndex;
|
||||
}
|
||||
};
|
||||
|
||||
export default MoveObjectCommand;
|
|
@ -0,0 +1,69 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param cmdArray array containing command objects
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const MultiCmdsCommand = function(cmdArray) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "MultiCmdsCommand";
|
||||
this.name = "Multiple Changes";
|
||||
|
||||
this.cmdArray = cmdArray !== undefined ? cmdArray : [];
|
||||
};
|
||||
|
||||
MultiCmdsCommand.prototype = {
|
||||
execute: function() {
|
||||
this.editor.signals.sceneGraphChanged.active = false;
|
||||
|
||||
for (let i = 0; i < this.cmdArray.length; i++) {
|
||||
this.cmdArray[i].execute();
|
||||
}
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = true;
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.editor.signals.sceneGraphChanged.active = false;
|
||||
|
||||
for (let i = this.cmdArray.length - 1; i >= 0; i--) {
|
||||
this.cmdArray[i].undo();
|
||||
}
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = true;
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
const cmds = [];
|
||||
for (let i = 0; i < this.cmdArray.length; i++) {
|
||||
cmds.push(this.cmdArray[i].toJSON());
|
||||
}
|
||||
output.cmds = cmds;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
const cmds = json.cmds;
|
||||
for (let i = 0; i < cmds.length; i++) {
|
||||
const cmd = new window[cmds[i].type](); // creates a new object of type "json.type"
|
||||
cmd.fromJSON(cmds[i]);
|
||||
this.cmdArray.push(cmd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default MultiCmdsCommand;
|
|
@ -0,0 +1,85 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const RemoveObjectCommand = function(object) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "RemoveObjectCommand";
|
||||
this.name = "Remove Object";
|
||||
|
||||
this.object = object;
|
||||
this.parent = object !== undefined ? object.parent : undefined;
|
||||
if (this.parent !== undefined) {
|
||||
this.index = this.parent.children.indexOf(this.object);
|
||||
}
|
||||
};
|
||||
|
||||
RemoveObjectCommand.prototype = {
|
||||
execute: function() {
|
||||
const scope = this.editor;
|
||||
this.object.traverse(function(child) {
|
||||
scope.removeHelper(child);
|
||||
});
|
||||
|
||||
this.parent.remove(this.object);
|
||||
this.editor.select(this.parent);
|
||||
|
||||
this.editor.signals.objectRemoved.dispatch(this.object);
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
const scope = this.editor;
|
||||
|
||||
this.object.traverse(function(child) {
|
||||
if (child.geometry !== undefined) scope.addGeometry(child.geometry);
|
||||
if (child.material !== undefined) scope.addMaterial(child.material);
|
||||
|
||||
scope.addHelper(child);
|
||||
});
|
||||
|
||||
this.parent.children.splice(this.index, 0, this.object);
|
||||
this.object.parent = this.parent;
|
||||
this.editor.select(this.object);
|
||||
|
||||
this.editor.signals.objectAdded.dispatch(this.object);
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
output.object = this.object.toJSON();
|
||||
output.index = this.index;
|
||||
output.parentUuid = this.parent.uuid;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.parent = this.editor.objectByUuid(json.parentUuid);
|
||||
if (this.parent === undefined) {
|
||||
this.parent = this.editor.scene;
|
||||
}
|
||||
|
||||
this.index = json.index;
|
||||
|
||||
this.object = this.editor.objectByUuid(json.object.object.uuid);
|
||||
if (this.object === undefined) {
|
||||
const loader = new THREE.ObjectLoader();
|
||||
this.object = loader.parse(json.object);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default RemoveObjectCommand;
|
|
@ -0,0 +1,67 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param script javascript object
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const RemoveScriptCommand = function(object, script) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "RemoveScriptCommand";
|
||||
this.name = "Remove Script";
|
||||
|
||||
this.object = object;
|
||||
this.script = script;
|
||||
if (this.object && this.script) {
|
||||
this.index = this.editor.scripts[this.object.uuid].indexOf(this.script);
|
||||
}
|
||||
};
|
||||
|
||||
RemoveScriptCommand.prototype = {
|
||||
execute: function() {
|
||||
if (this.editor.scripts[this.object.uuid] === undefined) return;
|
||||
|
||||
if (this.index !== -1) {
|
||||
this.editor.scripts[this.object.uuid].splice(this.index, 1);
|
||||
}
|
||||
|
||||
this.editor.signals.scriptRemoved.dispatch(this.script);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
if (this.editor.scripts[this.object.uuid] === undefined) {
|
||||
this.editor.scripts[this.object.uuid] = [];
|
||||
}
|
||||
|
||||
this.editor.scripts[this.object.uuid].splice(this.index, 0, this.script);
|
||||
|
||||
this.editor.signals.scriptAdded.dispatch(this.script);
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.script = this.script;
|
||||
output.index = this.index;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.script = json.script;
|
||||
this.index = json.index;
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
}
|
||||
};
|
||||
|
||||
export default RemoveScriptCommand;
|
|
@ -0,0 +1,64 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param attributeName string
|
||||
* @param newValue integer representing a hex color value
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetColorCommand = function(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetColorCommand";
|
||||
this.name = "Set " + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.attributeName = attributeName;
|
||||
this.oldValue = object !== undefined ? this.object[this.attributeName].getHex() : undefined;
|
||||
this.newValue = newValue;
|
||||
};
|
||||
|
||||
SetColorCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object[this.attributeName].setHex(this.newValue);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object[this.attributeName].setHex(this.oldValue);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
update: function(cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.attributeName = this.attributeName;
|
||||
output.oldValue = this.oldValue;
|
||||
output.newValue = this.newValue;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.attributeName = json.attributeName;
|
||||
this.oldValue = json.oldValue;
|
||||
this.newValue = json.newValue;
|
||||
}
|
||||
};
|
||||
|
||||
export default SetColorCommand;
|
|
@ -0,0 +1,75 @@
|
|||
import THREE from "../../vendor/three";
|
||||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param newGeometry THREE.Geometry
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetGeometryCommand = function(object, newGeometry) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetGeometryCommand";
|
||||
this.name = "Set Geometry";
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.oldGeometry = object !== undefined ? object.geometry : undefined;
|
||||
this.newGeometry = newGeometry;
|
||||
};
|
||||
|
||||
SetGeometryCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object.geometry.dispose();
|
||||
this.object.geometry = this.newGeometry;
|
||||
this.object.geometry.computeBoundingSphere();
|
||||
|
||||
this.editor.signals.geometryChanged.dispatch(this.object);
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object.geometry.dispose();
|
||||
this.object.geometry = this.oldGeometry;
|
||||
this.object.geometry.computeBoundingSphere();
|
||||
|
||||
this.editor.signals.geometryChanged.dispatch(this.object);
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
update: function(cmd) {
|
||||
this.newGeometry = cmd.newGeometry;
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.oldGeometry = this.object.geometry.toJSON();
|
||||
output.newGeometry = this.newGeometry.toJSON();
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
|
||||
function parseGeometry(data) {
|
||||
const loader = new THREE.ObjectLoader();
|
||||
return loader.parseGeometries([data])[data.uuid];
|
||||
}
|
||||
|
||||
this.oldGeometry = parseGeometry(json.oldGeometry);
|
||||
this.newGeometry = parseGeometry(json.newGeometry);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetGeometryCommand;
|
|
@ -0,0 +1,63 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param attributeName string
|
||||
* @param newValue number, string, boolean or object
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetGeometryValueCommand = function(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetGeometryValueCommand";
|
||||
this.name = "Set Geometry." + attributeName;
|
||||
|
||||
this.object = object;
|
||||
this.attributeName = attributeName;
|
||||
this.oldValue = object !== undefined ? object.geometry[attributeName] : undefined;
|
||||
this.newValue = newValue;
|
||||
};
|
||||
|
||||
SetGeometryValueCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object.geometry[this.attributeName] = this.newValue;
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
this.editor.signals.geometryChanged.dispatch();
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object.geometry[this.attributeName] = this.oldValue;
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
this.editor.signals.geometryChanged.dispatch();
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.attributeName = this.attributeName;
|
||||
output.oldValue = this.oldValue;
|
||||
output.newValue = this.newValue;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.attributeName = json.attributeName;
|
||||
this.oldValue = json.oldValue;
|
||||
this.newValue = json.newValue;
|
||||
}
|
||||
};
|
||||
|
||||
export default SetGeometryValueCommand;
|
|
@ -0,0 +1,69 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param attributeName string
|
||||
* @param newValue integer representing a hex color value
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetMaterialColorCommand = function(object, attributeName, newValue, materialSlot) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetMaterialColorCommand";
|
||||
this.name = "Set Material." + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.material = this.editor.getObjectMaterial(object, materialSlot);
|
||||
|
||||
this.oldValue = this.material !== undefined ? this.material[attributeName].getHex() : undefined;
|
||||
this.newValue = newValue;
|
||||
|
||||
this.attributeName = attributeName;
|
||||
};
|
||||
|
||||
SetMaterialColorCommand.prototype = {
|
||||
execute: function() {
|
||||
this.material[this.attributeName].setHex(this.newValue);
|
||||
|
||||
this.editor.signals.materialChanged.dispatch(this.material);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.material[this.attributeName].setHex(this.oldValue);
|
||||
|
||||
this.editor.signals.materialChanged.dispatch(this.material);
|
||||
},
|
||||
|
||||
update: function(cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.attributeName = this.attributeName;
|
||||
output.oldValue = this.oldValue;
|
||||
output.newValue = this.newValue;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.attributeName = json.attributeName;
|
||||
this.oldValue = json.oldValue;
|
||||
this.newValue = json.newValue;
|
||||
}
|
||||
};
|
||||
|
||||
export default SetMaterialColorCommand;
|
|
@ -0,0 +1,66 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param newMaterial THREE.Material
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetMaterialCommand = function(object, newMaterial, materialSlot) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetMaterialCommand";
|
||||
this.name = "New Material";
|
||||
|
||||
this.object = object;
|
||||
this.materialSlot = materialSlot;
|
||||
|
||||
this.oldMaterial = this.editor.getObjectMaterial(object, materialSlot);
|
||||
this.newMaterial = newMaterial;
|
||||
};
|
||||
|
||||
SetMaterialCommand.prototype = {
|
||||
execute: function() {
|
||||
this.editor.setObjectMaterial(this.object, this.materialSlot, this.newMaterial);
|
||||
this.editor.signals.materialChanged.dispatch(this.newMaterial);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.editor.setObjectMaterial(this.object, this.materialSlot, this.oldMaterial);
|
||||
this.editor.signals.materialChanged.dispatch(this.oldMaterial);
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.oldMaterial = this.oldMaterial.toJSON();
|
||||
output.newMaterial = this.newMaterial.toJSON();
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
|
||||
function parseMaterial(json) {
|
||||
const loader = new THREE.ObjectLoader();
|
||||
const images = loader.parseImages(json.images);
|
||||
const textures = loader.parseTextures(json.textures, images);
|
||||
const materials = loader.parseMaterials([json], textures);
|
||||
return materials[json.uuid];
|
||||
}
|
||||
|
||||
this.oldMaterial = parseMaterial(json.oldMaterial);
|
||||
this.newMaterial = parseMaterial(json.newMaterial);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetMaterialCommand;
|
|
@ -0,0 +1,114 @@
|
|||
import THREE from "../../vendor/three";
|
||||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param mapName string
|
||||
* @param newMap THREE.Texture
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetMaterialMapCommand = function(object, mapName, newMap, materialSlot) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetMaterialMapCommand";
|
||||
this.name = "Set Material." + mapName;
|
||||
|
||||
this.object = object;
|
||||
this.material = this.editor.getObjectMaterial(object, materialSlot);
|
||||
|
||||
this.oldMap = object !== undefined ? this.material[mapName] : undefined;
|
||||
this.newMap = newMap;
|
||||
|
||||
this.mapName = mapName;
|
||||
};
|
||||
|
||||
SetMaterialMapCommand.prototype = {
|
||||
execute: function() {
|
||||
this.material[this.mapName] = this.newMap;
|
||||
this.material.needsUpdate = true;
|
||||
|
||||
this.editor.signals.materialChanged.dispatch(this.material);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.material[this.mapName] = this.oldMap;
|
||||
this.material.needsUpdate = true;
|
||||
|
||||
this.editor.signals.materialChanged.dispatch(this.material);
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
// Note: The function 'extractFromCache' is copied from Object3D.toJSON()
|
||||
|
||||
// extract data from the cache hash
|
||||
// remove metadata on each item
|
||||
// and return as array
|
||||
function extractFromCache(cache) {
|
||||
const values = [];
|
||||
for (const key in cache) {
|
||||
const data = cache[key];
|
||||
delete data.metadata;
|
||||
values.push(data);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// serializes a map (THREE.Texture)
|
||||
|
||||
function serializeMap(map) {
|
||||
if (map === null || map === undefined) return null;
|
||||
|
||||
const meta = {
|
||||
geometries: {},
|
||||
materials: {},
|
||||
textures: {},
|
||||
images: {}
|
||||
};
|
||||
|
||||
const json = map.toJSON(meta);
|
||||
const images = extractFromCache(meta.images);
|
||||
if (images.length > 0) json.images = images;
|
||||
json.sourceFile = map.sourceFile;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.mapName = this.mapName;
|
||||
output.newMap = serializeMap(this.newMap);
|
||||
output.oldMap = serializeMap(this.oldMap);
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
function parseTexture(json) {
|
||||
let map = null;
|
||||
if (json !== null) {
|
||||
const loader = new THREE.ObjectLoader();
|
||||
const images = loader.parseImages(json.images);
|
||||
const textures = loader.parseTextures([json], images);
|
||||
map = textures[json.uuid];
|
||||
map.sourceFile = json.sourceFile;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.mapName = json.mapName;
|
||||
this.oldMap = parseTexture(json.oldMap);
|
||||
this.newMap = parseTexture(json.newMap);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetMaterialMapCommand;
|
|
@ -0,0 +1,73 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param attributeName string
|
||||
* @param newValue number, string, boolean or object
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetMaterialValueCommand = function(object, attributeName, newValue, materialSlot) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetMaterialValueCommand";
|
||||
this.name = "Set Material." + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.material = this.editor.getObjectMaterial(object, materialSlot);
|
||||
|
||||
this.oldValue = this.material !== undefined ? this.material[attributeName] : undefined;
|
||||
this.newValue = newValue;
|
||||
|
||||
this.attributeName = attributeName;
|
||||
};
|
||||
|
||||
SetMaterialValueCommand.prototype = {
|
||||
execute: function() {
|
||||
this.material[this.attributeName] = this.newValue;
|
||||
this.material.needsUpdate = true;
|
||||
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
this.editor.signals.materialChanged.dispatch(this.material);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.material[this.attributeName] = this.oldValue;
|
||||
this.material.needsUpdate = true;
|
||||
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
this.editor.signals.materialChanged.dispatch(this.material);
|
||||
},
|
||||
|
||||
update: function(cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.attributeName = this.attributeName;
|
||||
output.oldValue = this.oldValue;
|
||||
output.newValue = this.newValue;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.attributeName = json.attributeName;
|
||||
this.oldValue = json.oldValue;
|
||||
this.newValue = json.newValue;
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetMaterialValueCommand;
|
|
@ -0,0 +1,71 @@
|
|||
import THREE from "../../vendor/three";
|
||||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param newPosition THREE.Vector3
|
||||
* @param optionalOldPosition THREE.Vector3
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetPositionCommand = function(object, newPosition, optionalOldPosition) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetPositionCommand";
|
||||
this.name = "Set Position";
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
|
||||
if (object !== undefined && newPosition !== undefined) {
|
||||
this.oldPosition = object.position.clone();
|
||||
this.newPosition = newPosition.clone();
|
||||
}
|
||||
|
||||
if (optionalOldPosition !== undefined) {
|
||||
this.oldPosition = optionalOldPosition.clone();
|
||||
}
|
||||
};
|
||||
|
||||
SetPositionCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object.position.copy(this.newPosition);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object.position.copy(this.oldPosition);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
update: function(command) {
|
||||
this.newPosition.copy(command.newPosition);
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.oldPosition = this.oldPosition.toArray();
|
||||
output.newPosition = this.newPosition.toArray();
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.oldPosition = new THREE.Vector3().fromArray(json.oldPosition);
|
||||
this.newPosition = new THREE.Vector3().fromArray(json.newPosition);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetPositionCommand;
|
|
@ -0,0 +1,71 @@
|
|||
import THREE from "../../vendor/three";
|
||||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param newRotation THREE.Euler
|
||||
* @param optionalOldRotation THREE.Euler
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetRotationCommand = function(object, newRotation, optionalOldRotation) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetRotationCommand";
|
||||
this.name = "Set Rotation";
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
|
||||
if (object !== undefined && newRotation !== undefined) {
|
||||
this.oldRotation = object.rotation.clone();
|
||||
this.newRotation = newRotation.clone();
|
||||
}
|
||||
|
||||
if (optionalOldRotation !== undefined) {
|
||||
this.oldRotation = optionalOldRotation.clone();
|
||||
}
|
||||
};
|
||||
|
||||
SetRotationCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object.rotation.copy(this.newRotation);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object.rotation.copy(this.oldRotation);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
update: function(command) {
|
||||
this.newRotation.copy(command.newRotation);
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.oldRotation = this.oldRotation.toArray();
|
||||
output.newRotation = this.newRotation.toArray();
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.oldRotation = new THREE.Euler().fromArray(json.oldRotation);
|
||||
this.newRotation = new THREE.Euler().fromArray(json.newRotation);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetRotationCommand;
|
|
@ -0,0 +1,71 @@
|
|||
import THREE from "../../vendor/three";
|
||||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param newScale THREE.Vector3
|
||||
* @param optionalOldScale THREE.Vector3
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetScaleCommand = function(object, newScale, optionalOldScale) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetScaleCommand";
|
||||
this.name = "Set Scale";
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
|
||||
if (object !== undefined && newScale !== undefined) {
|
||||
this.oldScale = object.scale.clone();
|
||||
this.newScale = newScale.clone();
|
||||
}
|
||||
|
||||
if (optionalOldScale !== undefined) {
|
||||
this.oldScale = optionalOldScale.clone();
|
||||
}
|
||||
};
|
||||
|
||||
SetScaleCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object.scale.copy(this.newScale);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object.scale.copy(this.oldScale);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
},
|
||||
|
||||
update: function(command) {
|
||||
this.newScale.copy(command.newScale);
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.oldScale = this.oldScale.toArray();
|
||||
output.newScale = this.newScale.toArray();
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.oldScale = new THREE.Vector3().fromArray(json.oldScale);
|
||||
this.newScale = new THREE.Vector3().fromArray(json.newScale);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetScaleCommand;
|
|
@ -0,0 +1,83 @@
|
|||
import SetUuidCommand from "./SetUuidCommand";
|
||||
import SetValueCommand from "./SetValueCommand";
|
||||
import AddObjectCommand from "./AddObjectCommand";
|
||||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param scene containing children to import
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetSceneCommand = function(scene) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetSceneCommand";
|
||||
this.name = "Set Scene";
|
||||
|
||||
this.cmdArray = [];
|
||||
|
||||
if (scene !== undefined) {
|
||||
this.cmdArray.push(new SetUuidCommand(this.editor.scene, scene.uuid));
|
||||
this.cmdArray.push(new SetValueCommand(this.editor.scene, "name", scene.name));
|
||||
this.cmdArray.push(new SetValueCommand(this.editor.scene, "userData", JSON.parse(JSON.stringify(scene.userData))));
|
||||
|
||||
while (scene.children.length > 0) {
|
||||
const child = scene.children.pop();
|
||||
this.cmdArray.push(new AddObjectCommand(child));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SetSceneCommand.prototype = {
|
||||
execute: function() {
|
||||
this.editor.signals.sceneGraphChanged.active = false;
|
||||
|
||||
for (let i = 0; i < this.cmdArray.length; i++) {
|
||||
this.cmdArray[i].execute();
|
||||
}
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = true;
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.editor.signals.sceneGraphChanged.active = false;
|
||||
|
||||
for (let i = this.cmdArray.length - 1; i >= 0; i--) {
|
||||
this.cmdArray[i].undo();
|
||||
}
|
||||
|
||||
this.editor.signals.sceneGraphChanged.active = true;
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
const cmds = [];
|
||||
for (let i = 0; i < this.cmdArray.length; i++) {
|
||||
cmds.push(this.cmdArray[i].toJSON());
|
||||
}
|
||||
output.cmds = cmds;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
const cmds = json.cmds;
|
||||
for (let i = 0; i < cmds.length; i++) {
|
||||
const cmd = new window[cmds[i].type](); // creates a new object of type "json.type"
|
||||
cmd.fromJSON(cmds[i]);
|
||||
this.cmdArray.push(cmd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default SetSceneCommand;
|
|
@ -0,0 +1,61 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param newUuid string
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetUuidCommand = function(object, newUuid) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetUuidCommand";
|
||||
this.name = "Update UUID";
|
||||
|
||||
this.object = object;
|
||||
|
||||
this.oldUuid = object !== undefined ? object.uuid : undefined;
|
||||
this.newUuid = newUuid;
|
||||
};
|
||||
|
||||
SetUuidCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object.uuid = this.newUuid;
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object.uuid = this.oldUuid;
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.oldUuid = this.oldUuid;
|
||||
output.newUuid = this.newUuid;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.oldUuid = json.oldUuid;
|
||||
this.newUuid = json.newUuid;
|
||||
this.object = this.editor.objectByUuid(json.oldUuid);
|
||||
|
||||
if (this.object === undefined) {
|
||||
this.object = this.editor.objectByUuid(json.newUuid);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default SetUuidCommand;
|
|
@ -0,0 +1,66 @@
|
|||
import Command from "../Command";
|
||||
|
||||
/**
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param object THREE.Object3D
|
||||
* @param attributeName string
|
||||
* @param newValue number, string, boolean or object
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const SetValueCommand = function(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = "SetValueCommand";
|
||||
this.name = "Set " + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.attributeName = attributeName;
|
||||
this.oldValue = object !== undefined ? object[attributeName] : undefined;
|
||||
this.newValue = newValue;
|
||||
};
|
||||
|
||||
SetValueCommand.prototype = {
|
||||
execute: function() {
|
||||
this.object[this.attributeName] = this.newValue;
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
// this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
this.object[this.attributeName] = this.oldValue;
|
||||
this.editor.signals.objectChanged.dispatch(this.object);
|
||||
// this.editor.signals.sceneGraphChanged.dispatch();
|
||||
},
|
||||
|
||||
update: function(cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
const output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.attributeName = this.attributeName;
|
||||
output.oldValue = this.oldValue;
|
||||
output.newValue = this.newValue;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function(json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.attributeName = json.attributeName;
|
||||
this.oldValue = json.oldValue;
|
||||
this.newValue = json.newValue;
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
}
|
||||
};
|
||||
|
||||
export default SetValueCommand;
|
|
@ -0,0 +1,66 @@
|
|||
@import "../../theme";
|
||||
|
||||
:global {
|
||||
.f-no-select {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.m-tree {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.m-draggable {
|
||||
position: absolute;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.m-node {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
&.placeholder > * {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&.placeholder {
|
||||
border: 1px dashed $blue;
|
||||
}
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.node {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 4px 5px;
|
||||
|
||||
&.is-active {
|
||||
background-color: $selected;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.caret-right:before {
|
||||
content: "\25B8";
|
||||
}
|
||||
|
||||
.caret-down:before {
|
||||
content: "\25BE";
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,6 @@
|
|||
window.THREE = require("three");
|
||||
require("three/examples/js/controls/EditorControls");
|
||||
require("three/examples/js/controls/TransformControls");
|
||||
require("three/examples/js/loaders/GLTFLoader");
|
||||
|
||||
export default THREE;
|
|
@ -6725,6 +6725,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
|||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
|
||||
signals@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/signals/-/signals-1.0.0.tgz#65f0c1599352b35372ecaae5a250e6107376ed69"
|
||||
|
||||
single-line-log@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-1.1.2.tgz#c2f83f273a3e1a16edb0995661da0ed5ef033364"
|
||||
|
|
Загрузка…
Ссылка в новой задаче