mirror of
https://github.com/tengge1/ShadowEditor.git
synced 2026-01-25 15:08:11 +00:00
ShadowEditor核心类库。
This commit is contained in:
parent
a63c43992c
commit
67a953e41d
1
.gitignore
vendored
1
.gitignore
vendored
@ -111,3 +111,4 @@ typings/
|
||||
/ShadowEditor.ScriptEditor/obj
|
||||
/ShadowEditor.ScriptEditor/ShadowEditor.ScriptEditor.csproj.user
|
||||
/ShadowEditor.ScriptEditor/dist
|
||||
/ShadowEditor.Core/dist
|
||||
|
||||
7
ShadowEditor.Core/README.md
Normal file
7
ShadowEditor.Core/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# ShadowEditor.Core
|
||||
|
||||
包管理器,提供包的管理和动态加载功能,避免开始加载资源过多,导致载入缓慢。
|
||||
|
||||
## 依赖项
|
||||
|
||||
[ShadowEditor.PackageManager](../ShadowEditor.PackageManager/): ShadowEditor包管理器。
|
||||
133
ShadowEditor.Core/index.html
Normal file
133
ShadowEditor.Core/index.html
Normal file
@ -0,0 +1,133 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Shadow Editor</title>
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<!-- codemirror -->
|
||||
<link rel="stylesheet" href="assets/css/codemirror.css">
|
||||
<link rel="stylesheet" href="assets/css/theme/monokai.css">
|
||||
<link rel="stylesheet" href="assets/css/addon/dialog.css">
|
||||
<link rel="stylesheet" href="assets/css/addon/show-hint.css">
|
||||
<link rel="stylesheet" href="assets/css/addon/tern.css">
|
||||
|
||||
<!-- iconfont -->
|
||||
<link href="assets/css/icon/iconfont.css" rel="stylesheet" />
|
||||
|
||||
<!-- Shadow Editor -->
|
||||
<link href="assets/css/main.css" rel="stylesheet" />
|
||||
<link id="theme" href="assets/css/light.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container" tabindex="10"></div>
|
||||
|
||||
<!-- three.js -->
|
||||
<script src="assets/js/three.js"></script>
|
||||
<script src="assets/js/stats.min.js"></script>
|
||||
<script src="assets/js/three.bas.js"></script>
|
||||
<script src="assets/js/SPE.js"></script>
|
||||
<script src="assets/js/mmdparser.min.js"></script>
|
||||
<script src="assets/js/ammo.js"></script>
|
||||
<script src="assets/js/VolumetricFire.js"></script>
|
||||
<script src="assets/js/utils/BufferGeometryUtils.js"></script>
|
||||
<script src="assets/js/shaders/NormalMapShader.js"></script>
|
||||
<script src="assets/js/ShaderTerrain.js"></script>
|
||||
|
||||
<!-- controls -->
|
||||
<script src="assets/js/controls/EditorControls.js"></script>
|
||||
<script src="assets/js/controls/TransformControls.js"></script>
|
||||
|
||||
<!-- third-party -->
|
||||
<script src="assets/js/jszip.min.js"></script>
|
||||
<script src="assets/js/lzma.js"></script>
|
||||
<script src="assets/js/ctm.js"></script>
|
||||
<script src="assets/js/inflate.min.js"></script>
|
||||
<script src="assets/js/gl-matrix.js"></script>
|
||||
<script src="assets/js/pako.js"></script>
|
||||
<script src="assets/js/SimplexNoise.js"></script>
|
||||
<script src="assets/js/ImprovedNoise.js"></script>
|
||||
<script src="assets/js/GPUComputationRenderer.js"></script>
|
||||
|
||||
<script src="assets/js/codemirror.js"></script>
|
||||
<script src="assets/js/mode/javascript.js"></script>
|
||||
<script src="assets/js/mode/glsl.js"></script>
|
||||
<script src="assets/js/esprima.js"></script>
|
||||
<script src="assets/js/jsonlint.js"></script>
|
||||
<script src="assets/js/glslprep.min.js"></script>
|
||||
<script src="assets/js/addon/dialog.js"></script>
|
||||
<script src="assets/js/addon/show-hint.js"></script>
|
||||
<script src="assets/js/addon/tern.js"></script>
|
||||
|
||||
<script src="assets/js/acorn/acorn.js"></script>
|
||||
<script src="assets/js/acorn/acorn_loose.js"></script>
|
||||
<script src="assets/js/acorn/walk.js"></script>
|
||||
|
||||
<script src="assets/js/ternjs/polyfill.js"></script>
|
||||
<script src="assets/js/ternjs/signal.js"></script>
|
||||
<script src="assets/js/ternjs/tern.js"></script>
|
||||
<script src="assets/js/ternjs/def.js"></script>
|
||||
<script src="assets/js/ternjs/comment.js"></script>
|
||||
<script src="assets/js/ternjs/infer.js"></script>
|
||||
<script src="assets/js/ternjs/doc_comment.js"></script>
|
||||
<script src="assets/js/tern-threejs/threejs.js"></script>
|
||||
|
||||
<!-- geometries -->
|
||||
<script src="assets/js/geometries/TeapotBufferGeometry.js"></script>
|
||||
|
||||
<!-- loaders -->
|
||||
<script src="assets/js/loaders/AMFLoader.js"></script>
|
||||
<script src="assets/js/loaders/AWDLoader.js"></script>
|
||||
<script src="assets/js/loaders/BabylonLoader.js"></script>
|
||||
<script src="assets/js/loaders/BinaryLoader.js"></script>
|
||||
<script src="assets/js/loaders/ColladaLoader2.js"></script>
|
||||
<script src="assets/js/loaders/FBXLoader.js"></script>
|
||||
<script src="assets/js/loaders/GLTFLoader.js"></script>
|
||||
<script src="assets/js/loaders/KMZLoader.js"></script>
|
||||
<script src="assets/js/loaders/MD2Loader.js"></script>
|
||||
<script src="assets/js/loaders/OBJLoader.js"></script>
|
||||
<script src="assets/js/loaders/PLYLoader.js"></script>
|
||||
<script src="assets/js/loaders/STLLoader.js"></script>
|
||||
<script src="assets/js/loaders/TGALoader.js"></script>
|
||||
<script src="assets/js/loaders/VTKLoader.js"></script>
|
||||
<script src="assets/js/loaders/ctm/CTMLoader.js"></script>
|
||||
<script src="assets/js/loaders/MMDLoader.js"></script>
|
||||
|
||||
<!-- exporters -->
|
||||
<script src="assets/js/exporters/GLTFExporter.js"></script>
|
||||
<script src="assets/js/exporters/OBJExporter.js"></script>
|
||||
<script src="assets/js/exporters/PLYExporter.js"></script>
|
||||
<script src="assets/js/exporters/STLBinaryExporter.js"></script>
|
||||
<script src="assets/js/exporters/STLExporter.js"></script>
|
||||
|
||||
<!-- objects -->
|
||||
<script src="assets/js/objects/Sky.js"></script>
|
||||
<script src="assets/js/objects/Reflector.js"></script>
|
||||
<script src="assets/js/objects/Lensflare.js"></script>
|
||||
|
||||
<!-- animation -->
|
||||
<script src="assets/js/animation/CCDIKSolver.js"></script>
|
||||
<script src="assets/js/animation/MMDPhysics.js"></script>
|
||||
|
||||
<!-- Shadow Editor -->
|
||||
<script src="dist/ShadowEditor.js"></script>
|
||||
<script>
|
||||
var container = null;
|
||||
var app = null;
|
||||
|
||||
var start = function () {
|
||||
container = document.getElementById('container');
|
||||
app = new Shadow.Application(container, {
|
||||
server: location.origin
|
||||
});
|
||||
app.start();
|
||||
};
|
||||
|
||||
window.onload = start;
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
42
ShadowEditor.Core/rollup.config.js
Normal file
42
ShadowEditor.Core/rollup.config.js
Normal file
@ -0,0 +1,42 @@
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
|
||||
function glsl() {
|
||||
return {
|
||||
transform(code, id) {
|
||||
if (/\.glsl$/.test(id) === false) return;
|
||||
|
||||
var transformedCode = 'export default ' + JSON.stringify(
|
||||
code
|
||||
.replace(/[ \t]*\/\/.*\n/g, '') // remove //
|
||||
.replace(/[ \t]*\/\*[\s\S]*?\*\//g, '') // remove /* */
|
||||
.replace(/\n{2,}/g, '\n') // # \n+ to \n
|
||||
) + ';';
|
||||
return {
|
||||
code: transformedCode,
|
||||
map: {
|
||||
mappings: ''
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
input: 'ShadowEditor.Core/src/index.js',
|
||||
output: {
|
||||
indent: '\t',
|
||||
format: 'umd',
|
||||
name: 'Shadow',
|
||||
file: 'ShadowEditor.Core/dist/ShadowEditor.Core.js'
|
||||
},
|
||||
treeshake: true,
|
||||
external: [],
|
||||
plugins: [
|
||||
glsl(),
|
||||
resolve({
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules'
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
151
ShadowEditor.Core/src/Application.js
Normal file
151
ShadowEditor.Core/src/Application.js
Normal file
@ -0,0 +1,151 @@
|
||||
import Options from './Options';
|
||||
|
||||
import { UI } from './third_party';
|
||||
import EventDispatcher from './event/EventDispatcher';
|
||||
|
||||
import Menubar from './editor/menubar/Menubar';
|
||||
import Toolbar from './editor/Toolbar';
|
||||
import Viewport from './editor/Viewport';
|
||||
import Sidebar from './editor/sidebar/Sidebar';
|
||||
import Sidebar2 from './editor/sidebar2/Sidebar';
|
||||
import BottomPanel from './editor/bottom/BottomPanel';
|
||||
import StatusBar from './editor/StatusBar';
|
||||
import ScriptEditor from './editor/script/ScriptEditor';
|
||||
import Player from './player/Player';
|
||||
|
||||
import Editor from './editor/Editor';
|
||||
import Physics from './editor/Physics';
|
||||
|
||||
import API from './api/API';
|
||||
|
||||
/**
|
||||
* 应用程序
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function Application(container, options) {
|
||||
|
||||
// 容器
|
||||
this.container = container;
|
||||
this.width = this.container.clientWidth;
|
||||
this.height = this.container.clientHeight;
|
||||
|
||||
// 配置
|
||||
this.options = new Options(options);
|
||||
|
||||
// 事件
|
||||
this.event = new EventDispatcher(this);
|
||||
this.call = this.event.call.bind(this.event);
|
||||
this.on = this.event.on.bind(this.event);
|
||||
|
||||
var params = { app: this };
|
||||
|
||||
// 用户界面
|
||||
this.ui = UI;
|
||||
this.menubar = new Menubar(params); // 菜单栏
|
||||
this.toolbar = new Toolbar(params); // 工具栏
|
||||
this.viewport = new Viewport(params); // 场景编辑区
|
||||
this.sidebar = new Sidebar(params); // 侧边栏
|
||||
this.sidebar2 = new Sidebar2(params); // 侧边栏2
|
||||
this.bottomPanel = new BottomPanel(params); // 底部面板
|
||||
this.statusBar = new StatusBar(params); // 状态栏
|
||||
this.script = new ScriptEditor(params); // 脚本编辑器
|
||||
this.player = new Player(params); // 播放器面板
|
||||
|
||||
UI.create({
|
||||
xtype: 'container',
|
||||
parent: this.container,
|
||||
children: [
|
||||
new Menubar(params), {
|
||||
xtype: 'div',
|
||||
style: {
|
||||
width: '100%',
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
children: [
|
||||
this.toolbar, {
|
||||
xtype: 'div',
|
||||
style: {
|
||||
position: 'relative',
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
style: {
|
||||
position: 'relative',
|
||||
flex: 1
|
||||
},
|
||||
children: [
|
||||
this.viewport,
|
||||
this.script,
|
||||
this.player
|
||||
]
|
||||
},
|
||||
this.bottomPanel,
|
||||
this.statusBar
|
||||
]
|
||||
},
|
||||
this.sidebar2,
|
||||
this.sidebar
|
||||
]
|
||||
}
|
||||
]
|
||||
}).render();
|
||||
|
||||
// 核心
|
||||
this.editor = new Editor(this); // 编辑器
|
||||
this.physics = new Physics(params);
|
||||
|
||||
// Html5 Worker
|
||||
// var script = document.querySelector('script[src$="ShadowEditor.js" i]').src; // http://localhost:2000/dist/ShadowEditor.js
|
||||
// this.worker = new Worker(script);
|
||||
}
|
||||
|
||||
// ------------------------- 程序控制 -------------------------------
|
||||
|
||||
Application.prototype.start = function () {
|
||||
// 启动事件 - 事件要在ui创建完成后启动
|
||||
this.event.start();
|
||||
|
||||
this.call('appStart', this);
|
||||
this.call('appStarted', this);
|
||||
|
||||
// 启动物体引擎
|
||||
this.physics.start();
|
||||
|
||||
this.call('resize', this);
|
||||
|
||||
this.log('程序启动成功。');
|
||||
};
|
||||
|
||||
Application.prototype.stop = function () {
|
||||
this.call('appStop', this);
|
||||
this.call('appStoped', this);
|
||||
|
||||
this.log('程序已经停止');
|
||||
|
||||
this.event.stop();
|
||||
};
|
||||
|
||||
// ----------------------- 记录日志 --------------------------------
|
||||
|
||||
Application.prototype.log = function (content) { // 普通日志
|
||||
this.call('log', this, content);
|
||||
};
|
||||
|
||||
Application.prototype.warn = function (content) { // 警告日志
|
||||
this.call('log', this, content, 'warn');
|
||||
};
|
||||
|
||||
Application.prototype.error = function (content) { // 错误日志
|
||||
this.call('log', this, content, 'error');
|
||||
};
|
||||
|
||||
// API
|
||||
Object.assign(Application.prototype, API.prototype);
|
||||
|
||||
export default Application;
|
||||
26
ShadowEditor.Core/src/Options.js
Normal file
26
ShadowEditor.Core/src/Options.js
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 配置选项
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options 配置选项
|
||||
*/
|
||||
function Options(options) {
|
||||
options = options || {};
|
||||
|
||||
// 服务器配置
|
||||
this.server = options.server === undefined ? location.origin : options.server; // 服务端地址
|
||||
|
||||
// 外观配置
|
||||
this.theme = options.theme || 'assets/css/light.css'; // 皮肤
|
||||
|
||||
// 帮助器配置
|
||||
this.showGrid = true; // 是否显示网格
|
||||
this.showCameraHelper = true; // 是否显示相机帮助器
|
||||
this.showPointLightHelper = false; // 是否显示点光源帮助器
|
||||
this.showDirectionalLightHelper = true; // 是否显示平行光帮助器
|
||||
this.showSpotLightHelper = true; // 是否显示聚光灯帮助器
|
||||
this.showHemisphereLightHelper = true; // 是否显示半球光帮助器
|
||||
this.showRectAreaLightHelper = true; // 是否显示矩形光帮助器
|
||||
this.showSkeletonHelper = false; // 是否显示骨骼帮助器
|
||||
}
|
||||
|
||||
export default Options;
|
||||
48
ShadowEditor.Core/src/animation/Animation.js
Normal file
48
ShadowEditor.Core/src/animation/Animation.js
Normal file
@ -0,0 +1,48 @@
|
||||
import AnimationType from './AnimationType';
|
||||
|
||||
var ID = -1;
|
||||
|
||||
/**
|
||||
* 动画数据
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options 选项
|
||||
*/
|
||||
function Animation(options) {
|
||||
options = options || {};
|
||||
|
||||
// 基本信息
|
||||
this.id = options.id || null; // MongoDB _id字段
|
||||
this.uuid = options.uuid || THREE.Math.generateUUID(); // uuid
|
||||
this.name = options.name || `动画${ID--}`; // 动画名称
|
||||
this.target = options.target || null; // 动画对象uuid
|
||||
this.type = options.type || AnimationType.Tween; // 动画类型
|
||||
this.beginTime = options.beginTime || 0; // 开始时间(秒)
|
||||
this.endTime = options.endTime || 10; // 结束时间(秒)
|
||||
|
||||
// 补间动画
|
||||
this.beginStatus = options.beginStatus || 'Current'; // 开始状态(Current、Custom)
|
||||
this.beginPositionX = options.beginPositionX || 0;
|
||||
this.beginPositionY = options.beginPositionY || 0;
|
||||
this.beginPositionZ = options.beginPositionZ || 0;
|
||||
this.beginRotationX = options.beginRotationX || 0;
|
||||
this.beginRotationY = options.beginRotationY || 0;
|
||||
this.beginRotationZ = options.beginRotationZ || 0;
|
||||
this.beginScaleLock = options.beginScaleLock === undefined ? true : options.beginScaleLock;
|
||||
this.beginScaleX = options.beginScaleX || 1.0;
|
||||
this.beginScaleY = options.beginScaleY || 1.0;
|
||||
this.beginScaleZ = options.beginScaleZ || 1.0;
|
||||
this.ease = options.ease || 'linear'; // linear, quadIn, quadOut, quadInOut, cubicIn, cubicOut, cubicInOut, quartIn, quartOut, quartInOut, quintIn, quintOut, quintInOut, sineIn, sineOut, sineInOut, backIn, backOut, backInOut, circIn, circOut, circInOut, bounceIn, bounceOut, bounceInOut, elasticIn, elasticOut, elasticInOut
|
||||
this.endStatus = options.endStatus || 'Current';
|
||||
this.endPositionX = options.endPositionX || 0;
|
||||
this.endPositionY = options.endPositionY || 0;
|
||||
this.endPositionZ = options.endPositionZ || 0;
|
||||
this.endRotationX = options.endRotationX || 0;
|
||||
this.endRotationY = options.endRotationY || 0;
|
||||
this.endRotationZ = options.endRotationZ || 0;
|
||||
this.endScaleLock = options.endScaleLock === undefined ? true : options.endScaleLock;
|
||||
this.endScaleX = options.endScaleX || 1.0;
|
||||
this.endScaleY = options.endScaleY || 1.0;
|
||||
this.endScaleZ = options.endScaleZ || 1.0;
|
||||
}
|
||||
|
||||
export default Animation;
|
||||
57
ShadowEditor.Core/src/animation/AnimationGroup.js
Normal file
57
ShadowEditor.Core/src/animation/AnimationGroup.js
Normal file
@ -0,0 +1,57 @@
|
||||
import Animation from './Animation';
|
||||
|
||||
var ID = 1;
|
||||
|
||||
/**
|
||||
* 动画组
|
||||
* @param {*} options 选项
|
||||
*/
|
||||
function AnimationGroup(options) {
|
||||
options = options || {};
|
||||
this.id = options.id || null; // MongoDB _id字段
|
||||
this.uuid = options.uuid || THREE.Math.generateUUID(); // uuid
|
||||
this.name = options.name || `组-${ID++}`; // 组名
|
||||
this.type = 'AnimationGroup'; // 组类型
|
||||
this.index = options.index || ID; // 组序号
|
||||
this.animations = options.animations || []; // 组动画
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @param {*} animation
|
||||
*/
|
||||
AnimationGroup.prototype.add = function (animation) {
|
||||
this.insert(animation, this.animations.length - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param {*} animation
|
||||
* @param {*} index
|
||||
*/
|
||||
AnimationGroup.prototype.insert = function (animation, index = 0) {
|
||||
if (!(animation instanceof Animation)) {
|
||||
console.warn(`AnimationGroup: animation不是Animation的实例。`);
|
||||
return;
|
||||
}
|
||||
this.animations.splice(index, 0, animation);
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除
|
||||
* @param {*} animation
|
||||
*/
|
||||
AnimationGroup.prototype.remove = function (animation) {
|
||||
var index = this.animations.indexOf(animation);
|
||||
this.removeAt(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除
|
||||
* @param {*} index
|
||||
*/
|
||||
AnimationGroup.prototype.removeAt = function (index) {
|
||||
this.animations.splice(index, 1);
|
||||
};
|
||||
|
||||
export default AnimationGroup;
|
||||
98
ShadowEditor.Core/src/animation/AnimationManager.js
Normal file
98
ShadowEditor.Core/src/animation/AnimationManager.js
Normal file
@ -0,0 +1,98 @@
|
||||
import AnimationGroup from './AnimationGroup';
|
||||
|
||||
/**
|
||||
* 动画管理器
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} app 应用程序
|
||||
*/
|
||||
function AnimationManager(app) {
|
||||
this.app = app;
|
||||
this.animations = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 清空所有
|
||||
*/
|
||||
AnimationManager.prototype.clear = function () {
|
||||
this.animations = [];
|
||||
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var group = new AnimationGroup({
|
||||
name: `组${i + 1}`,
|
||||
index: i
|
||||
});
|
||||
this.animations.push(group);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加动画组
|
||||
* @param {*} group 动画组
|
||||
*/
|
||||
AnimationManager.prototype.add = function (group) {
|
||||
this.insert(group, this.animations.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* 插入动画组
|
||||
* @param {*} group 动画组
|
||||
* @param {*} index 索引
|
||||
*/
|
||||
AnimationManager.prototype.insert = function (group, index = 0) {
|
||||
if (!(group instanceof AnimationGroup)) {
|
||||
console.warn(`AnimationManager: group不是AnimationGroup的实例。`);
|
||||
return;
|
||||
}
|
||||
this.animations.splice(index, 0, group);
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除组
|
||||
* @param {*} group 动画组
|
||||
*/
|
||||
AnimationManager.prototype.remove = function (group) {
|
||||
var index = this.animations.indexOf(group);
|
||||
if (index > -1) {
|
||||
this.animations.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据uuid删除组
|
||||
* @param {*} uuid
|
||||
*/
|
||||
AnimationManager.prototype.removeByUUID = function (uuid) {
|
||||
var index = this.animations.findIndex(n => n.uuid === uuid);
|
||||
if (index > -1) {
|
||||
this.animations.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取动画
|
||||
*/
|
||||
AnimationManager.prototype.getAnimationGroups = function () {
|
||||
return this.animations;
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置动画
|
||||
* @param {*} animations
|
||||
*/
|
||||
AnimationManager.prototype.setAnimationGroups = function (animations) {
|
||||
this.animations = animations;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据uuid获取动画
|
||||
* @param {*} uuid
|
||||
*/
|
||||
AnimationManager.prototype.getAnimationByUUID = function (uuid) {
|
||||
var group = this.animations.filter(n => n.animations.findIndex(m => m.uuid === uuid) > -1)[0];
|
||||
if (group === undefined) {
|
||||
return null;
|
||||
}
|
||||
return group.animations.filter(n => n.uuid === uuid)[0];
|
||||
};
|
||||
|
||||
export default AnimationManager;
|
||||
22
ShadowEditor.Core/src/animation/AnimationType.js
Normal file
22
ShadowEditor.Core/src/animation/AnimationType.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 动画类型
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
var AnimationType = {
|
||||
// 补间动画
|
||||
Tween: 'Tween',
|
||||
|
||||
// 骨骼动画
|
||||
Skeletal: 'Skeletal',
|
||||
|
||||
// 音频播放
|
||||
Audio: 'Audio',
|
||||
|
||||
// 滤镜动画
|
||||
Filter: 'Filter',
|
||||
|
||||
// 粒子动画
|
||||
Particle: 'Particle'
|
||||
};
|
||||
|
||||
export default AnimationType;
|
||||
456
ShadowEditor.Core/src/animation/Ease.js
Normal file
456
ShadowEditor.Core/src/animation/Ease.js
Normal file
@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Ease
|
||||
* Visit http://createjs.com/ for documentation, updates and examples.
|
||||
*
|
||||
* Copyright (c) 2010 gskinner.com, inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module TweenJS
|
||||
* @author tweenjs / https://www.createjs.com/tweenjs
|
||||
* @link https://github.com/CreateJS/TweenJS/blob/master/src/tweenjs/Ease.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Ease class provides a collection of easing functions for use with TweenJS. It does not use the standard 4 param
|
||||
* easing signature. Instead it uses a single param which indicates the current linear ratio (0 to 1) of the tween.
|
||||
*
|
||||
* Most methods on Ease can be passed directly as easing functions:
|
||||
*
|
||||
* createjs.Tween.get(target).to({x:100}, 500, createjs.Ease.linear);
|
||||
*
|
||||
* However, methods beginning with "get" will return an easing function based on parameter values:
|
||||
*
|
||||
* createjs.Tween.get(target).to({y:200}, 500, createjs.Ease.getPowIn(2.2));
|
||||
*
|
||||
* Please see the <a href="http://www.createjs.com/Demos/TweenJS/Tween_SparkTable">spark table demo</a> for an
|
||||
* overview of the different ease types on <a href="http://tweenjs.com">TweenJS.com</a>.
|
||||
*
|
||||
* <em>Equations derived from work by Robert Penner.</em>
|
||||
* @class Ease
|
||||
* @static
|
||||
**/
|
||||
function Ease() {
|
||||
throw "Ease cannot be instantiated.";
|
||||
}
|
||||
|
||||
|
||||
// static methods and properties
|
||||
/**
|
||||
* @method linear
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.linear = function (t) { return t; };
|
||||
|
||||
/**
|
||||
* Identical to linear.
|
||||
* @method none
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.none = Ease.linear;
|
||||
|
||||
/**
|
||||
* Mimics the simple -100 to 100 easing in Adobe Flash/Animate.
|
||||
* @method get
|
||||
* @param {Number} amount A value from -1 (ease in) to 1 (ease out) indicating the strength and direction of the ease.
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.get = function (amount) {
|
||||
if (amount < -1) { amount = -1; }
|
||||
else if (amount > 1) { amount = 1; }
|
||||
return function (t) {
|
||||
if (amount == 0) { return t; }
|
||||
if (amount < 0) { return t * (t * -amount + 1 + amount); }
|
||||
return t * ((2 - t) * amount + (1 - amount));
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Configurable exponential ease.
|
||||
* @method getPowIn
|
||||
* @param {Number} pow The exponent to use (ex. 3 would return a cubic ease).
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getPowIn = function (pow) {
|
||||
return function (t) {
|
||||
return Math.pow(t, pow);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Configurable exponential ease.
|
||||
* @method getPowOut
|
||||
* @param {Number} pow The exponent to use (ex. 3 would return a cubic ease).
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getPowOut = function (pow) {
|
||||
return function (t) {
|
||||
return 1 - Math.pow(1 - t, pow);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Configurable exponential ease.
|
||||
* @method getPowInOut
|
||||
* @param {Number} pow The exponent to use (ex. 3 would return a cubic ease).
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getPowInOut = function (pow) {
|
||||
return function (t) {
|
||||
if ((t *= 2) < 1) return 0.5 * Math.pow(t, pow);
|
||||
return 1 - 0.5 * Math.abs(Math.pow(2 - t, pow));
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @method quadIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quadIn = Ease.getPowIn(2);
|
||||
/**
|
||||
* @method quadOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quadOut = Ease.getPowOut(2);
|
||||
/**
|
||||
* @method quadInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quadInOut = Ease.getPowInOut(2);
|
||||
|
||||
/**
|
||||
* @method cubicIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.cubicIn = Ease.getPowIn(3);
|
||||
/**
|
||||
* @method cubicOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.cubicOut = Ease.getPowOut(3);
|
||||
/**
|
||||
* @method cubicInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.cubicInOut = Ease.getPowInOut(3);
|
||||
|
||||
/**
|
||||
* @method quartIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quartIn = Ease.getPowIn(4);
|
||||
/**
|
||||
* @method quartOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quartOut = Ease.getPowOut(4);
|
||||
/**
|
||||
* @method quartInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quartInOut = Ease.getPowInOut(4);
|
||||
|
||||
/**
|
||||
* @method quintIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quintIn = Ease.getPowIn(5);
|
||||
/**
|
||||
* @method quintOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quintOut = Ease.getPowOut(5);
|
||||
/**
|
||||
* @method quintInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.quintInOut = Ease.getPowInOut(5);
|
||||
|
||||
/**
|
||||
* @method sineIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.sineIn = function (t) {
|
||||
return 1 - Math.cos(t * Math.PI / 2);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method sineOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.sineOut = function (t) {
|
||||
return Math.sin(t * Math.PI / 2);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method sineInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.sineInOut = function (t) {
|
||||
return -0.5 * (Math.cos(Math.PI * t) - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Configurable "back in" ease.
|
||||
* @method getBackIn
|
||||
* @param {Number} amount The strength of the ease.
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getBackIn = function (amount) {
|
||||
return function (t) {
|
||||
return t * t * ((amount + 1) * t - amount);
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @method backIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.backIn = Ease.getBackIn(1.7);
|
||||
|
||||
/**
|
||||
* Configurable "back out" ease.
|
||||
* @method getBackOut
|
||||
* @param {Number} amount The strength of the ease.
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getBackOut = function (amount) {
|
||||
return function (t) {
|
||||
return (--t * t * ((amount + 1) * t + amount) + 1);
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @method backOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.backOut = Ease.getBackOut(1.7);
|
||||
|
||||
/**
|
||||
* Configurable "back in out" ease.
|
||||
* @method getBackInOut
|
||||
* @param {Number} amount The strength of the ease.
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getBackInOut = function (amount) {
|
||||
amount *= 1.525;
|
||||
return function (t) {
|
||||
if ((t *= 2) < 1) return 0.5 * (t * t * ((amount + 1) * t - amount));
|
||||
return 0.5 * ((t -= 2) * t * ((amount + 1) * t + amount) + 2);
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @method backInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.backInOut = Ease.getBackInOut(1.7);
|
||||
|
||||
/**
|
||||
* @method circIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.circIn = function (t) {
|
||||
return -(Math.sqrt(1 - t * t) - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method circOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.circOut = function (t) {
|
||||
return Math.sqrt(1 - (--t) * t);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method circInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.circInOut = function (t) {
|
||||
if ((t *= 2) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1);
|
||||
return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method bounceIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.bounceIn = function (t) {
|
||||
return 1 - Ease.bounceOut(1 - t);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method bounceOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.bounceOut = function (t) {
|
||||
if (t < 1 / 2.75) {
|
||||
return (7.5625 * t * t);
|
||||
} else if (t < 2 / 2.75) {
|
||||
return (7.5625 * (t -= 1.5 / 2.75) * t + 0.75);
|
||||
} else if (t < 2.5 / 2.75) {
|
||||
return (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375);
|
||||
} else {
|
||||
return (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method bounceInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.bounceInOut = function (t) {
|
||||
if (t < 0.5) return Ease.bounceIn(t * 2) * .5;
|
||||
return Ease.bounceOut(t * 2 - 1) * 0.5 + 0.5;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configurable elastic ease.
|
||||
* @method getElasticIn
|
||||
* @param {Number} amplitude
|
||||
* @param {Number} period
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getElasticIn = function (amplitude, period) {
|
||||
var pi2 = Math.PI * 2;
|
||||
return function (t) {
|
||||
if (t == 0 || t == 1) return t;
|
||||
var s = period / pi2 * Math.asin(1 / amplitude);
|
||||
return -(amplitude * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * pi2 / period));
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @method elasticIn
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.elasticIn = Ease.getElasticIn(1, 0.3);
|
||||
|
||||
/**
|
||||
* Configurable elastic ease.
|
||||
* @method getElasticOut
|
||||
* @param {Number} amplitude
|
||||
* @param {Number} period
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getElasticOut = function (amplitude, period) {
|
||||
var pi2 = Math.PI * 2;
|
||||
return function (t) {
|
||||
if (t == 0 || t == 1) return t;
|
||||
var s = period / pi2 * Math.asin(1 / amplitude);
|
||||
return (amplitude * Math.pow(2, -10 * t) * Math.sin((t - s) * pi2 / period) + 1);
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @method elasticOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.elasticOut = Ease.getElasticOut(1, 0.3);
|
||||
|
||||
/**
|
||||
* Configurable elastic ease.
|
||||
* @method getElasticInOut
|
||||
* @param {Number} amplitude
|
||||
* @param {Number} period
|
||||
* @static
|
||||
* @return {Function}
|
||||
**/
|
||||
Ease.getElasticInOut = function (amplitude, period) {
|
||||
var pi2 = Math.PI * 2;
|
||||
return function (t) {
|
||||
var s = period / pi2 * Math.asin(1 / amplitude);
|
||||
if ((t *= 2) < 1) return -0.5 * (amplitude * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * pi2 / period));
|
||||
return amplitude * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * pi2 / period) * 0.5 + 1;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @method elasticInOut
|
||||
* @param {Number} t
|
||||
* @static
|
||||
* @return {Number}
|
||||
**/
|
||||
Ease.elasticInOut = Ease.getElasticInOut(1, 0.3 * 1.5);
|
||||
|
||||
export default Ease;
|
||||
12
ShadowEditor.Core/src/api/API.js
Normal file
12
ShadowEditor.Core/src/api/API.js
Normal file
@ -0,0 +1,12 @@
|
||||
import UIAPI from './UIAPI';
|
||||
|
||||
/**
|
||||
* 编辑器API
|
||||
*/
|
||||
function API() {
|
||||
|
||||
}
|
||||
|
||||
Object.assign(API.prototype, UIAPI.prototype);
|
||||
|
||||
export default API;
|
||||
12
ShadowEditor.Core/src/api/UIAPI.js
Normal file
12
ShadowEditor.Core/src/api/UIAPI.js
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 用户界面API
|
||||
*/
|
||||
function UIAPI() {
|
||||
|
||||
}
|
||||
|
||||
UIAPI.prototype.addMenu = function () {
|
||||
alert('添加菜单');
|
||||
};
|
||||
|
||||
export default UIAPI;
|
||||
56
ShadowEditor.Core/src/command/AddObjectCommand.js
Normal file
56
ShadowEditor.Core/src/command/AddObjectCommand.js
Normal file
@ -0,0 +1,56 @@
|
||||
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
|
||||
*/
|
||||
function AddObjectCommand(object) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'AddObjectCommand';
|
||||
|
||||
this.object = object;
|
||||
|
||||
if (object !== undefined) {
|
||||
this.name = '添加物体:' + object.name;
|
||||
}
|
||||
};
|
||||
|
||||
AddObjectCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(AddObjectCommand.prototype, {
|
||||
constructor: AddObjectCommand,
|
||||
|
||||
execute: function () {
|
||||
this.editor.addObject(this.object);
|
||||
this.editor.select(this.object);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.editor.removeObject(this.object);
|
||||
this.editor.deselect();
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var output = Command.prototype.toJSON.call(this);
|
||||
output.object = this.object.toJSON();
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function (json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.object.object.uuid);
|
||||
|
||||
if (this.object === undefined) {
|
||||
var loader = new THREE.ObjectLoader();
|
||||
this.object = loader.parse(json.object);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default AddObjectCommand;
|
||||
64
ShadowEditor.Core/src/command/AddScriptCommand.js
Normal file
64
ShadowEditor.Core/src/command/AddScriptCommand.js
Normal file
@ -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 script javascript object
|
||||
* @constructor
|
||||
*/
|
||||
var AddScriptCommand = function (object, script) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'AddScriptCommand';
|
||||
this.name = '添加脚本';
|
||||
|
||||
this.object = object;
|
||||
this.script = script;
|
||||
};
|
||||
|
||||
AddScriptCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(AddScriptCommand.prototype, {
|
||||
constructor: AddScriptCommand,
|
||||
|
||||
execute: function () {
|
||||
if (this.editor.scripts[this.object.uuid] === undefined) {
|
||||
this.editor.scripts[this.object.uuid] = [];
|
||||
}
|
||||
|
||||
this.editor.scripts[this.object.uuid].push(this.script);
|
||||
this.editor.app.call('scriptAdded', this, this.script);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
if (this.editor.scripts[this.object.uuid] === undefined) return;
|
||||
|
||||
var index = this.editor.scripts[this.object.uuid].indexOf(this.script);
|
||||
|
||||
if (index !== - 1) {
|
||||
this.editor.scripts[this.object.uuid].splice(index, 1);
|
||||
}
|
||||
|
||||
this.editor.app.call('scriptRemoved', this, this.script);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.script = this.script;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function (json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.script = json.script;
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
}
|
||||
});
|
||||
|
||||
export default AddScriptCommand;
|
||||
36
ShadowEditor.Core/src/command/Command.js
Normal file
36
ShadowEditor.Core/src/command/Command.js
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 命令基类
|
||||
* @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 editorRef pointer to main editor object used to initialize each command object with a reference to the editor
|
||||
* @constructor
|
||||
*/
|
||||
function Command(editorRef) {
|
||||
this.id = -1;
|
||||
this.inMemory = false;
|
||||
this.updatable = false;
|
||||
this.type = '';
|
||||
this.name = '';
|
||||
|
||||
if (editorRef !== undefined) {
|
||||
Command.editor = editorRef;
|
||||
}
|
||||
this.editor = Command.editor;
|
||||
};
|
||||
|
||||
Command.prototype.toJSON = function () {
|
||||
var output = {};
|
||||
output.type = this.type;
|
||||
output.id = this.id;
|
||||
output.name = this.name;
|
||||
return output;
|
||||
};
|
||||
|
||||
Command.prototype.fromJSON = function (json) {
|
||||
this.inMemory = true;
|
||||
this.type = json.type;
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
};
|
||||
|
||||
export default Command;
|
||||
94
ShadowEditor.Core/src/command/MoveObjectCommand.js
Normal file
94
ShadowEditor.Core/src/command/MoveObjectCommand.js
Normal file
@ -0,0 +1,94 @@
|
||||
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
|
||||
*/
|
||||
function MoveObjectCommand(object, newParent, newBefore) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'MoveObjectCommand';
|
||||
this.name = '移动物体';
|
||||
|
||||
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 = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(MoveObjectCommand.prototype, {
|
||||
constructor: MoveObjectCommand,
|
||||
|
||||
execute: function () {
|
||||
this.oldParent.remove(this.object);
|
||||
|
||||
var children = this.newParent.children;
|
||||
children.splice(this.newIndex, 0, this.object);
|
||||
this.object.parent = this.newParent;
|
||||
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.newParent.remove(this.object);
|
||||
|
||||
var children = this.oldParent.children;
|
||||
children.splice(this.oldIndex, 0, this.object);
|
||||
this.object.parent = this.oldParent;
|
||||
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
62
ShadowEditor.Core/src/command/MultiCmdsCommand.js
Normal file
62
ShadowEditor.Core/src/command/MultiCmdsCommand.js
Normal file
@ -0,0 +1,62 @@
|
||||
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
|
||||
*/
|
||||
function MultiCmdsCommand(cmdArray) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'MultiCmdsCommand';
|
||||
this.name = '多种改变';
|
||||
|
||||
this.cmdArray = (cmdArray !== undefined) ? cmdArray : [];
|
||||
};
|
||||
|
||||
MultiCmdsCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(MultiCmdsCommand.prototype, {
|
||||
constructor: MultiCmdsCommand,
|
||||
|
||||
execute: function () {
|
||||
for (var i = 0; i < this.cmdArray.length; i++) {
|
||||
this.cmdArray[i].execute();
|
||||
}
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
for (var i = this.cmdArray.length - 1; i >= 0; i--) {
|
||||
this.cmdArray[i].undo();
|
||||
}
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var output = Command.prototype.toJSON.call(this);
|
||||
|
||||
var cmds = [];
|
||||
for (var 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);
|
||||
|
||||
var cmds = json.cmds;
|
||||
for (var i = 0; i < cmds.length; i++) {
|
||||
var 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;
|
||||
87
ShadowEditor.Core/src/command/RemoveObjectCommand.js
Normal file
87
ShadowEditor.Core/src/command/RemoveObjectCommand.js
Normal file
@ -0,0 +1,87 @@
|
||||
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
|
||||
*/
|
||||
function RemoveObjectCommand(object) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'RemoveObjectCommand';
|
||||
this.name = '移除物体';
|
||||
|
||||
this.object = object;
|
||||
|
||||
this.parent = (object !== undefined) ? object.parent : undefined;
|
||||
|
||||
if (this.parent !== undefined) {
|
||||
this.index = this.parent.children.indexOf(this.object);
|
||||
}
|
||||
};
|
||||
|
||||
RemoveObjectCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(RemoveObjectCommand.prototype, {
|
||||
constructor: RemoveObjectCommand,
|
||||
|
||||
execute: function () {
|
||||
var scope = this.editor;
|
||||
this.object.traverse(function (child) {
|
||||
|
||||
scope.removeHelper(child);
|
||||
|
||||
});
|
||||
|
||||
this.parent.remove(this.object);
|
||||
this.editor.select(this.parent);
|
||||
|
||||
this.editor.app.call('objectRemoved', this, this.object);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
var scope = this.editor;
|
||||
|
||||
this.object.traverse(function (child) {
|
||||
scope.addHelper(child);
|
||||
});
|
||||
|
||||
this.parent.children.splice(this.index, 0, this.object);
|
||||
this.object.parent = this.parent;
|
||||
this.editor.select(this.object);
|
||||
|
||||
this.editor.app.call('objectAdded', this, this.object);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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) {
|
||||
var loader = new THREE.ObjectLoader();
|
||||
this.object = loader.parse(json.object);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default RemoveObjectCommand;
|
||||
68
ShadowEditor.Core/src/command/RemoveScriptCommand.js
Normal file
68
ShadowEditor.Core/src/command/RemoveScriptCommand.js
Normal file
@ -0,0 +1,68 @@
|
||||
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
|
||||
*/
|
||||
function RemoveScriptCommand(object, script) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'RemoveScriptCommand';
|
||||
this.name = '移除脚本';
|
||||
|
||||
this.object = object;
|
||||
this.script = script;
|
||||
if (this.object && this.script) {
|
||||
this.index = this.editor.scripts[this.object.uuid].indexOf(this.script);
|
||||
}
|
||||
};
|
||||
|
||||
RemoveScriptCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(RemoveScriptCommand.prototype, {
|
||||
constructor: RemoveScriptCommand,
|
||||
|
||||
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.app.call('scriptRemoved', this, 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.app.call('scriptAdded', this, this.script);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
65
ShadowEditor.Core/src/command/SetColorCommand.js
Normal file
65
ShadowEditor.Core/src/command/SetColorCommand.js
Normal file
@ -0,0 +1,65 @@
|
||||
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
|
||||
*/
|
||||
function SetColorCommand(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetColorCommand';
|
||||
this.name = '设置 ' + 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 = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetColorCommand.prototype, {
|
||||
constructor: SetColorCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object[this.attributeName].setHex(this.newValue);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object[this.attributeName].setHex(this.oldValue);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
update: function (cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
75
ShadowEditor.Core/src/command/SetGeometryCommand.js
Normal file
75
ShadowEditor.Core/src/command/SetGeometryCommand.js
Normal file
@ -0,0 +1,75 @@
|
||||
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
|
||||
*/
|
||||
function SetGeometryCommand(object, newGeometry) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetGeometryCommand';
|
||||
this.name = '设置几何体';
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.oldGeometry = (object !== undefined) ? object.geometry : undefined;
|
||||
this.newGeometry = newGeometry;
|
||||
};
|
||||
|
||||
SetGeometryCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetGeometryCommand.prototype, {
|
||||
constructor: SetGeometryCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.geometry.dispose();
|
||||
this.object.geometry = this.newGeometry;
|
||||
this.object.geometry.computeBoundingSphere();
|
||||
|
||||
this.editor.app.call('geometryChanged', this, this.object);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.geometry.dispose();
|
||||
this.object.geometry = this.oldGeometry;
|
||||
this.object.geometry.computeBoundingSphere();
|
||||
|
||||
this.editor.app.call('geometryChanged', this, this.object);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
update: function (cmd) {
|
||||
this.newGeometry = cmd.newGeometry;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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);
|
||||
|
||||
this.oldGeometry = parseGeometry(json.oldGeometry);
|
||||
this.newGeometry = parseGeometry(json.newGeometry);
|
||||
|
||||
function parseGeometry(data) {
|
||||
var loader = new THREE.ObjectLoader();
|
||||
return loader.parseGeometries([data])[data.uuid];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default SetGeometryCommand;
|
||||
64
ShadowEditor.Core/src/command/SetGeometryValueCommand.js
Normal file
64
ShadowEditor.Core/src/command/SetGeometryValueCommand.js
Normal file
@ -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 number, string, boolean or object
|
||||
* @constructor
|
||||
*/
|
||||
function SetGeometryValueCommand(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetGeometryValueCommand';
|
||||
this.name = '设置几何体.' + attributeName;
|
||||
|
||||
this.object = object;
|
||||
this.attributeName = attributeName;
|
||||
this.oldValue = (object !== undefined) ? object.geometry[attributeName] : undefined;
|
||||
this.newValue = newValue;
|
||||
};
|
||||
|
||||
SetGeometryValueCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetGeometryValueCommand.prototype, {
|
||||
constructor: SetGeometryValueCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.geometry[this.attributeName] = this.newValue;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
this.editor.app.call('geometryChanged', this);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.geometry[this.attributeName] = this.oldValue;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
this.editor.app.call('geometryChanged', this);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
65
ShadowEditor.Core/src/command/SetMaterialColorCommand.js
Normal file
65
ShadowEditor.Core/src/command/SetMaterialColorCommand.js
Normal file
@ -0,0 +1,65 @@
|
||||
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
|
||||
*/
|
||||
function SetMaterialColorCommand(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetMaterialColorCommand';
|
||||
this.name = '设置材质.' + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.attributeName = attributeName;
|
||||
this.oldValue = (object !== undefined) ? this.object.material[this.attributeName].getHex() : undefined;
|
||||
this.newValue = newValue;
|
||||
};
|
||||
|
||||
SetMaterialColorCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetMaterialColorCommand.prototype, {
|
||||
constructor: SetMaterialColorCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.material[this.attributeName].setHex(this.newValue);
|
||||
this.editor.app.call('materialChanged', this, this.object.material);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.material[this.attributeName].setHex(this.oldValue);
|
||||
this.editor.app.call('materialChanged', this, this.object.material);
|
||||
},
|
||||
|
||||
update: function (cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
64
ShadowEditor.Core/src/command/SetMaterialCommand.js
Normal file
64
ShadowEditor.Core/src/command/SetMaterialCommand.js
Normal file
@ -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 newMaterial THREE.Material
|
||||
* @constructor
|
||||
*/
|
||||
function SetMaterialCommand(object, newMaterial) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetMaterialCommand';
|
||||
this.name = '新材质';
|
||||
|
||||
this.object = object;
|
||||
this.oldMaterial = (object !== undefined) ? object.material : undefined;
|
||||
this.newMaterial = newMaterial;
|
||||
};
|
||||
|
||||
SetMaterialCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetMaterialCommand.prototype, {
|
||||
constructor: SetMaterialCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.material = this.newMaterial;
|
||||
this.editor.app.call('materialChanged', this, this.newMaterial);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.material = this.oldMaterial;
|
||||
this.editor.app.call('materialChanged', this, this.oldMaterial);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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);
|
||||
this.oldMaterial = parseMaterial(json.oldMaterial);
|
||||
this.newMaterial = parseMaterial(json.newMaterial);
|
||||
|
||||
function parseMaterial(json) {
|
||||
var loader = new THREE.ObjectLoader();
|
||||
var images = loader.parseImages(json.images);
|
||||
var textures = loader.parseTextures(json.textures, images);
|
||||
var materials = loader.parseMaterials([json], textures);
|
||||
return materials[json.uuid];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default SetMaterialCommand;
|
||||
113
ShadowEditor.Core/src/command/SetMaterialMapCommand.js
Normal file
113
ShadowEditor.Core/src/command/SetMaterialMapCommand.js
Normal file
@ -0,0 +1,113 @@
|
||||
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
|
||||
*/
|
||||
function SetMaterialMapCommand(object, mapName, newMap) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetMaterialMapCommand';
|
||||
this.name = '设置材质.' + mapName;
|
||||
|
||||
this.object = object;
|
||||
this.mapName = mapName;
|
||||
this.oldMap = (object !== undefined) ? object.material[mapName] : undefined;
|
||||
this.newMap = newMap;
|
||||
};
|
||||
|
||||
SetMaterialMapCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetMaterialMapCommand.prototype, {
|
||||
constructor: SetMaterialMapCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.material[this.mapName] = this.newMap;
|
||||
this.object.material.needsUpdate = true;
|
||||
this.editor.app.call('materialChanged', this, this.object.material);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.material[this.mapName] = this.oldMap;
|
||||
this.object.material.needsUpdate = true;
|
||||
this.editor.app.call('materialChanged', this, this.object.material);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.mapName = this.mapName;
|
||||
output.newMap = serializeMap(this.newMap);
|
||||
output.oldMap = serializeMap(this.oldMap);
|
||||
|
||||
return output;
|
||||
|
||||
// serializes a map (THREE.Texture)
|
||||
|
||||
function serializeMap(map) {
|
||||
if (map === null || map === undefined) return null;
|
||||
|
||||
var meta = {
|
||||
geometries: {},
|
||||
materials: {},
|
||||
textures: {},
|
||||
images: {}
|
||||
};
|
||||
|
||||
var json = map.toJSON(meta);
|
||||
var images = extractFromCache(meta.images);
|
||||
if (images.length > 0) json.images = images;
|
||||
json.sourceFile = map.sourceFile;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var values = [];
|
||||
for (var key in cache) {
|
||||
|
||||
var data = cache[key];
|
||||
delete data.metadata;
|
||||
values.push(data);
|
||||
|
||||
}
|
||||
return values;
|
||||
}
|
||||
},
|
||||
|
||||
fromJSON: function (json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.mapName = json.mapName;
|
||||
this.oldMap = parseTexture(json.oldMap);
|
||||
this.newMap = parseTexture(json.newMap);
|
||||
|
||||
function parseTexture(json) {
|
||||
var map = null;
|
||||
if (json !== null) {
|
||||
|
||||
var loader = new THREE.ObjectLoader();
|
||||
var images = loader.parseImages(json.images);
|
||||
var textures = loader.parseTextures([json], images);
|
||||
map = textures[json.uuid];
|
||||
map.sourceFile = json.sourceFile;
|
||||
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default SetMaterialMapCommand;
|
||||
69
ShadowEditor.Core/src/command/SetMaterialValueCommand.js
Normal file
69
ShadowEditor.Core/src/command/SetMaterialValueCommand.js
Normal file
@ -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 number, string, boolean or object
|
||||
* @constructor
|
||||
*/
|
||||
function SetMaterialValueCommand(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetMaterialValueCommand';
|
||||
this.name = '设置材质.' + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.oldValue = (object !== undefined) ? object.material[attributeName] : undefined;
|
||||
this.newValue = newValue;
|
||||
this.attributeName = attributeName;
|
||||
};
|
||||
|
||||
SetMaterialValueCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetMaterialValueCommand.prototype, {
|
||||
constructor: SetMaterialValueCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.material[this.attributeName] = this.newValue;
|
||||
this.object.material.needsUpdate = true;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
this.editor.app.call('materialChanged', this, this.object.material);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.material[this.attributeName] = this.oldValue;
|
||||
this.object.material.needsUpdate = true;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
this.editor.app.call('materialChanged', this, this.object.material);
|
||||
},
|
||||
|
||||
update: function (cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
71
ShadowEditor.Core/src/command/SetPositionCommand.js
Normal file
71
ShadowEditor.Core/src/command/SetPositionCommand.js
Normal file
@ -0,0 +1,71 @@
|
||||
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
|
||||
*/
|
||||
function SetPositionCommand(object, newPosition, optionalOldPosition) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetPositionCommand';
|
||||
this.name = '设置位置';
|
||||
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 = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetPositionCommand.prototype, {
|
||||
constructor: SetPositionCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.position.copy(this.newPosition);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.position.copy(this.oldPosition);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
update: function (command) {
|
||||
this.newPosition.copy(command.newPosition);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
71
ShadowEditor.Core/src/command/SetRotationCommand.js
Normal file
71
ShadowEditor.Core/src/command/SetRotationCommand.js
Normal file
@ -0,0 +1,71 @@
|
||||
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
|
||||
*/
|
||||
function SetRotationCommand(object, newRotation, optionalOldRotation) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetRotationCommand';
|
||||
this.name = '设置旋转';
|
||||
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 = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetRotationCommand.prototype, {
|
||||
constructor: SetRotationCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.rotation.copy(this.newRotation);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.rotation.copy(this.oldRotation);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
update: function (command) {
|
||||
this.newRotation.copy(command.newRotation);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
71
ShadowEditor.Core/src/command/SetScaleCommand.js
Normal file
71
ShadowEditor.Core/src/command/SetScaleCommand.js
Normal file
@ -0,0 +1,71 @@
|
||||
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
|
||||
*/
|
||||
function SetScaleCommand(object, newScale, optionalOldScale) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetScaleCommand';
|
||||
this.name = '设置缩放';
|
||||
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 = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetScaleCommand.prototype, {
|
||||
constructor: SetScaleCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.scale.copy(this.newScale);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.scale.copy(this.oldScale);
|
||||
this.object.updateMatrixWorld(true);
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
update: function (command) {
|
||||
this.newScale.copy(command.newScale);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
78
ShadowEditor.Core/src/command/SetSceneCommand.js
Normal file
78
ShadowEditor.Core/src/command/SetSceneCommand.js
Normal file
@ -0,0 +1,78 @@
|
||||
import Command from './Command';
|
||||
import SetUuidCommand from './SetUuidCommand';
|
||||
import SetValueCommand from './SetValueCommand';
|
||||
import AddObjectCommand from './AddObjectCommand';
|
||||
|
||||
/**
|
||||
* 设置场景命令
|
||||
* @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
|
||||
*/
|
||||
function SetSceneCommand(scene) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetSceneCommand';
|
||||
this.name = '设置场景';
|
||||
|
||||
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) {
|
||||
var child = scene.children.pop();
|
||||
this.cmdArray.push(new AddObjectCommand(child));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SetSceneCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetSceneCommand.prototype, {
|
||||
constructor: SetSceneCommand,
|
||||
|
||||
execute: function () {
|
||||
for (var i = 0; i < this.cmdArray.length; i++) {
|
||||
this.cmdArray[i].execute();
|
||||
}
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
for (var i = this.cmdArray.length - 1; i >= 0; i--) {
|
||||
this.cmdArray[i].undo();
|
||||
}
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var output = Command.prototype.toJSON.call(this);
|
||||
|
||||
var cmds = [];
|
||||
for (var 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);
|
||||
|
||||
var cmds = json.cmds;
|
||||
for (var i = 0; i < cmds.length; i++) {
|
||||
var 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;
|
||||
84
ShadowEditor.Core/src/command/SetScriptValueCommand.js
Normal file
84
ShadowEditor.Core/src/command/SetScriptValueCommand.js
Normal file
@ -0,0 +1,84 @@
|
||||
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
|
||||
* @param attributeName string
|
||||
* @param newValue string, object
|
||||
* @param cursorPosition javascript object with format {line: 2, ch: 3}
|
||||
* @param scrollInfo javascript object with values {left, top, width, height, clientWidth, clientHeight}
|
||||
* @constructor
|
||||
*/
|
||||
function SetScriptValueCommand(object, script, attributeName, newValue, cursorPosition, scrollInfo) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetScriptValueCommand';
|
||||
this.name = '设置脚本.' + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.script = script;
|
||||
|
||||
this.attributeName = attributeName;
|
||||
this.oldValue = (script !== undefined) ? script[this.attributeName] : undefined;
|
||||
this.newValue = newValue;
|
||||
this.cursorPosition = cursorPosition;
|
||||
this.scrollInfo = scrollInfo;
|
||||
};
|
||||
|
||||
SetScriptValueCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetScriptValueCommand.prototype, {
|
||||
constructor: SetScriptValueCommand,
|
||||
|
||||
execute: function () {
|
||||
this.script[this.attributeName] = this.newValue;
|
||||
|
||||
this.editor.app.call('scriptChanged', this);
|
||||
this.editor.app.call('refreshScriptEditor', this, this.object, this.script, this.cursorPosition, this.scrollInfo);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.script[this.attributeName] = this.oldValue;
|
||||
|
||||
this.editor.app.call('scriptChanged', this);
|
||||
this.editor.app.call('refreshScriptEditor', this, this.object, this.script, this.cursorPosition, this.scrollInfo);
|
||||
},
|
||||
|
||||
update: function (cmd) {
|
||||
this.cursorPosition = cmd.cursorPosition;
|
||||
this.scrollInfo = cmd.scrollInfo;
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var output = Command.prototype.toJSON.call(this);
|
||||
|
||||
output.objectUuid = this.object.uuid;
|
||||
output.index = this.editor.scripts[this.object.uuid].indexOf(this.script);
|
||||
output.attributeName = this.attributeName;
|
||||
output.oldValue = this.oldValue;
|
||||
output.newValue = this.newValue;
|
||||
output.cursorPosition = this.cursorPosition;
|
||||
output.scrollInfo = this.scrollInfo;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
fromJSON: function (json) {
|
||||
Command.prototype.fromJSON.call(this, json);
|
||||
|
||||
this.oldValue = json.oldValue;
|
||||
this.newValue = json.newValue;
|
||||
this.attributeName = json.attributeName;
|
||||
this.object = this.editor.objectByUuid(json.objectUuid);
|
||||
this.script = this.editor.scripts[json.objectUuid][json.index];
|
||||
this.cursorPosition = json.cursorPosition;
|
||||
this.scrollInfo = json.scrollInfo;
|
||||
}
|
||||
});
|
||||
|
||||
export default SetScriptValueCommand;
|
||||
62
ShadowEditor.Core/src/command/SetUuidCommand.js
Normal file
62
ShadowEditor.Core/src/command/SetUuidCommand.js
Normal file
@ -0,0 +1,62 @@
|
||||
import Command from './Command';
|
||||
|
||||
/**
|
||||
* 设置uuid命令
|
||||
* @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
|
||||
*/
|
||||
function SetUuidCommand(object, newUuid) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetUuidCommand';
|
||||
this.name = '更新UUID';
|
||||
|
||||
this.object = object;
|
||||
|
||||
this.oldUuid = (object !== undefined) ? object.uuid : undefined;
|
||||
this.newUuid = newUuid;
|
||||
};
|
||||
|
||||
SetUuidCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetUuidCommand.prototype, {
|
||||
constructor: SetUuidCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object.uuid = this.newUuid;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object.uuid = this.oldUuid;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
65
ShadowEditor.Core/src/command/SetValueCommand.js
Normal file
65
ShadowEditor.Core/src/command/SetValueCommand.js
Normal file
@ -0,0 +1,65 @@
|
||||
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
|
||||
*/
|
||||
function SetValueCommand(object, attributeName, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.type = 'SetValueCommand';
|
||||
this.name = '设置' + attributeName;
|
||||
this.updatable = true;
|
||||
|
||||
this.object = object;
|
||||
this.attributeName = attributeName;
|
||||
this.oldValue = (object !== undefined) ? object[attributeName] : undefined;
|
||||
this.newValue = newValue;
|
||||
};
|
||||
|
||||
SetValueCommand.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(SetValueCommand.prototype, {
|
||||
constructor: SetValueCommand,
|
||||
|
||||
execute: function () {
|
||||
this.object[this.attributeName] = this.newValue;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
this.object[this.attributeName] = this.oldValue;
|
||||
this.editor.app.call('objectChanged', this, this.object);
|
||||
},
|
||||
|
||||
update: function (cmd) {
|
||||
this.newValue = cmd.newValue;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var 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;
|
||||
20
ShadowEditor.Core/src/component/BaseComponent.js
Normal file
20
ShadowEditor.Core/src/component/BaseComponent.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { UI } from '../third_party';
|
||||
|
||||
/**
|
||||
* 所有组件基类
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function BaseComponent(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app
|
||||
}
|
||||
|
||||
BaseComponent.prototype = Object.create(UI.Control.prototype);
|
||||
BaseComponent.prototype.constructor = BaseComponent;
|
||||
|
||||
BaseComponent.prototype.render = function () {
|
||||
|
||||
};
|
||||
|
||||
export default BaseComponent;
|
||||
126
ShadowEditor.Core/src/component/BasicComponent.js
Normal file
126
ShadowEditor.Core/src/component/BasicComponent.js
Normal file
@ -0,0 +1,126 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
import RemoveObjectCommand from '../command/RemoveObjectCommand';
|
||||
import AddObjectCommand from '../command/AddObjectCommand';
|
||||
|
||||
/**
|
||||
* 基本信息组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function BasicComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
BasicComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
BasicComponent.prototype.constructor = BasicComponent;
|
||||
|
||||
BasicComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'basicPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
borderTop: 0,
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '基本信息'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '名称'
|
||||
}, {
|
||||
xtype: 'input',
|
||||
id: 'name',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '100px',
|
||||
fontSize: '12px'
|
||||
},
|
||||
onChange: this.onChangeName.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '类型'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'type',
|
||||
scope: this.id
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '可见性'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'visible',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeVisible.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
BasicComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
BasicComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
BasicComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('basicPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var name = UI.get('name', this.id);
|
||||
var type = UI.get('type', this.id);
|
||||
var visible = UI.get('visible', this.id);
|
||||
|
||||
name.setValue(this.selected.name);
|
||||
type.setValue(this.selected.constructor.name);
|
||||
visible.setValue(this.selected.visible);
|
||||
};
|
||||
|
||||
BasicComponent.prototype.onChangeName = function () {
|
||||
var name = UI.get('name', this.id);
|
||||
var editor = this.app.editor;
|
||||
|
||||
editor.execute(new SetValueCommand(this.selected, 'name', name.getValue()));
|
||||
};
|
||||
|
||||
BasicComponent.prototype.onChangeVisible = function () {
|
||||
this.selected.visible = UI.get('visible', this.id).getValue();
|
||||
};
|
||||
|
||||
export default BasicComponent;
|
||||
124
ShadowEditor.Core/src/component/CameraComponent.js
Normal file
124
ShadowEditor.Core/src/component/CameraComponent.js
Normal file
@ -0,0 +1,124 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
|
||||
/**
|
||||
* 相机组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function CameraComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
CameraComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
CameraComponent.prototype.constructor = CameraComponent;
|
||||
|
||||
CameraComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'cameraPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '相机组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '视场'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectFov',
|
||||
scope: this.id,
|
||||
onChange: this.onSetObjectFov.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '近点'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectNear',
|
||||
scope: this.id,
|
||||
onChange: this.onSetObjectNear.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '远点'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectFar',
|
||||
scope: this.id,
|
||||
onChange: this.onSetObjectFar.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
CameraComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
CameraComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
CameraComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('cameraPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.PerspectiveCamera) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var objectFov = UI.get('objectFov', this.id);
|
||||
var objectNear = UI.get('objectNear', this.id);
|
||||
var objectFar = UI.get('objectFar', this.id);
|
||||
|
||||
objectFov.setValue(this.selected.fov);
|
||||
objectNear.setValue(this.selected.near);
|
||||
objectFar.setValue(this.selected.far);
|
||||
};
|
||||
|
||||
CameraComponent.prototype.onSetObjectFov = function () {
|
||||
var fov = UI.get('objectFov', this.id).getValue();
|
||||
this.app.editor.execute(new SetValueCommand(this.selected, 'fov', fov));
|
||||
};
|
||||
|
||||
CameraComponent.prototype.onSetObjectNear = function () {
|
||||
var near = UI.get('objectNear', this.id).getValue();
|
||||
this.app.editor.execute(new SetValueCommand(this.selected, 'near', near));
|
||||
};
|
||||
|
||||
CameraComponent.prototype.onSetObjectFar = function () {
|
||||
var far = UI.get('objectFar', this.id).getValue();
|
||||
this.app.editor.execute(new SetValueCommand(this.selected, 'far', far));
|
||||
};
|
||||
|
||||
export default CameraComponent;
|
||||
222
ShadowEditor.Core/src/component/FireComponent.js
Normal file
222
ShadowEditor.Core/src/component/FireComponent.js
Normal file
@ -0,0 +1,222 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
|
||||
/**
|
||||
* 火焰组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function FireComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
|
||||
this.isPlaying = false;
|
||||
}
|
||||
|
||||
FireComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
FireComponent.prototype.constructor = FireComponent;
|
||||
|
||||
FireComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'firePanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '火焰组件'
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'width',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'height',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '深度'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'depth',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '切片厚度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'sliceSpacing',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label'
|
||||
}, {
|
||||
xtype: 'button',
|
||||
id: 'btnPreview',
|
||||
scope: this.id,
|
||||
text: '预览',
|
||||
onClick: this.onPreview.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
FireComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
FireComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
FireComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('firePanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected.userData.type === 'Fire') {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var width = UI.get('width', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var sliceSpacing = UI.get('sliceSpacing', this.id);
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
var fire = this.selected.userData.fire;
|
||||
width.setValue(fire.mesh.userData.width);
|
||||
height.setValue(fire.mesh.userData.height);
|
||||
depth.setValue(fire.mesh.userData.depth);
|
||||
sliceSpacing.setValue(fire.mesh.userData.sliceSpacing);
|
||||
|
||||
if (this.isPlaying) {
|
||||
btnPreview.setText('取消');
|
||||
} else {
|
||||
btnPreview.setText('预览');
|
||||
}
|
||||
};
|
||||
|
||||
FireComponent.prototype.onChange = function () {
|
||||
var width = UI.get('width', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var sliceSpacing = UI.get('sliceSpacing', this.id);
|
||||
|
||||
VolumetricFire.texturePath = 'assets/textures/VolumetricFire/';
|
||||
|
||||
var editor = this.app.editor;
|
||||
|
||||
var fire = new VolumetricFire(
|
||||
width.getValue(),
|
||||
height.getValue(),
|
||||
depth.getValue(),
|
||||
sliceSpacing.getValue(),
|
||||
editor.camera
|
||||
);
|
||||
|
||||
fire.mesh.name = this.selected.name;
|
||||
fire.mesh.position.copy(this.selected.position);
|
||||
fire.mesh.rotation.copy(this.selected.rotation);
|
||||
fire.mesh.scale.copy(this.selected.scale);
|
||||
|
||||
fire.mesh.userData.type = 'Fire';
|
||||
fire.mesh.userData.fire = fire;
|
||||
fire.mesh.userData.width = width.getValue();
|
||||
fire.mesh.userData.height = height.getValue();
|
||||
fire.mesh.userData.depth = depth.getValue();
|
||||
fire.mesh.userData.sliceSpacing = sliceSpacing.getValue();
|
||||
|
||||
var index = editor.scene.children.indexOf(this.selected);
|
||||
if (index > -1) {
|
||||
editor.scene.children[index] = fire.mesh;
|
||||
fire.mesh.parent = this.selected.parent;
|
||||
this.selected.parent = null;
|
||||
this.app.call(`objectRemoved`, this, this.selected);
|
||||
this.app.call(`objectAdded`, this, fire.mesh);
|
||||
editor.select(fire.mesh);
|
||||
this.app.call('sceneGraphChanged', this.id);
|
||||
|
||||
fire.update(0);
|
||||
}
|
||||
};
|
||||
|
||||
FireComponent.prototype.onPreview = function () {
|
||||
if (this.isPlaying) {
|
||||
this.stopPreview();
|
||||
} else {
|
||||
this.startPreview();
|
||||
}
|
||||
};
|
||||
|
||||
FireComponent.prototype.startPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = true;
|
||||
btnPreview.setText('取消');
|
||||
|
||||
this.app.on(`animate.${this.id}`, this.onAnimate.bind(this));
|
||||
};
|
||||
|
||||
FireComponent.prototype.stopPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = false;
|
||||
btnPreview.setText('预览');
|
||||
|
||||
this.app.on(`animate.${this.id}`, null);
|
||||
};
|
||||
|
||||
FireComponent.prototype.onAnimate = function (clock, deltaTime) {
|
||||
var elapsed = clock.getElapsedTime();
|
||||
|
||||
var fire = this.selected.userData.fire;
|
||||
fire.update(elapsed);
|
||||
};
|
||||
|
||||
export default FireComponent;
|
||||
99
ShadowEditor.Core/src/component/GeometryComponent.js
Normal file
99
ShadowEditor.Core/src/component/GeometryComponent.js
Normal file
@ -0,0 +1,99 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import PlaneGeometryComponent from './geometry/PlaneGeometryComponent';
|
||||
import BoxGeometryComponent from './geometry/BoxGeometryComponent';
|
||||
import CircleGeometryComponent from './geometry/CircleGeometryComponent';
|
||||
import CylinderGeometryComponent from './geometry/CylinderGeometryComponent';
|
||||
import SphereGeometryComponent from './geometry/SphereGeometryComponent';
|
||||
import IcosahedronGeometryComponent from './geometry/IcosahedronGeometryComponent';
|
||||
import TorusGeometryComponent from './geometry/TorusGeometryComponent';
|
||||
import TorusKnotGeometryComponent from './geometry/TorusKnotGeometryComponent';
|
||||
import LatheGeometryComponent from './geometry/LatheGeometryComponent';
|
||||
import TeapotGeometryComponent from './geometry/TeapotGeometryComponent';
|
||||
|
||||
/**
|
||||
* 几何体组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function GeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
}
|
||||
|
||||
GeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
GeometryComponent.prototype.constructor = GeometryComponent;
|
||||
|
||||
GeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '几何组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '类型'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'name',
|
||||
scope: this.id,
|
||||
text: ''
|
||||
}]
|
||||
},
|
||||
new PlaneGeometryComponent({ app: this.app }),
|
||||
new BoxGeometryComponent({ app: this.app }),
|
||||
new CircleGeometryComponent({ app: this.app }),
|
||||
new CylinderGeometryComponent({ app: this.app }),
|
||||
new SphereGeometryComponent({ app: this.app }),
|
||||
new IcosahedronGeometryComponent({ app: this.app }),
|
||||
new TorusGeometryComponent({ app: this.app }),
|
||||
new TorusKnotGeometryComponent({ app: this.app }),
|
||||
new LatheGeometryComponent({ app: this.app }),
|
||||
new TeapotGeometryComponent({ app: this.app })
|
||||
]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
};
|
||||
|
||||
GeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
GeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
|
||||
var name = UI.get('name', this.id);
|
||||
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh) {
|
||||
container.dom.style.display = '';
|
||||
if (editor.selected.geometry instanceof THREE.TeapotBufferGeometry) {
|
||||
name.setValue('TeapotBufferGeometry');
|
||||
} else {
|
||||
name.setValue(editor.selected.geometry.constructor.name);
|
||||
}
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
name.setValue('');
|
||||
}
|
||||
};
|
||||
|
||||
export default GeometryComponent;
|
||||
158
ShadowEditor.Core/src/component/LMeshComponent.js
Normal file
158
ShadowEditor.Core/src/component/LMeshComponent.js
Normal file
@ -0,0 +1,158 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
import RemoveObjectCommand from '../command/RemoveObjectCommand';
|
||||
import AddObjectCommand from '../command/AddObjectCommand';
|
||||
|
||||
/**
|
||||
* LMesh组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function LMeshComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
|
||||
this.isPlaying = false;
|
||||
}
|
||||
|
||||
LMeshComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
LMeshComponent.prototype.constructor = LMeshComponent;
|
||||
|
||||
LMeshComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'lmeshPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: 'LMesh组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '动画'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'anims',
|
||||
scope: this.id,
|
||||
onChange: this.onSelectAnim.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label'
|
||||
}, {
|
||||
xtype: 'button',
|
||||
id: 'btnPreview',
|
||||
scope: this.id,
|
||||
text: '预览',
|
||||
onClick: this.onPreview.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('lmeshPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected.userData.type === 'lol') {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var anims = UI.get('anims', this.id);
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
var model = this.selected.userData.model;
|
||||
var animNames = model.getAnimations();
|
||||
|
||||
var options = {
|
||||
|
||||
};
|
||||
|
||||
animNames.forEach(n => {
|
||||
options[n] = n;
|
||||
});
|
||||
|
||||
anims.setOptions(options);
|
||||
anims.setValue(animNames[0]);
|
||||
|
||||
if (this.isPlaying) {
|
||||
btnPreview.setText('取消');
|
||||
} else {
|
||||
btnPreview.setText('预览');
|
||||
}
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.onSelectAnim = function () {
|
||||
var anims = UI.get('anims', this.id);
|
||||
|
||||
var model = this.selected.userData.model;
|
||||
model.setAnimation(anims.getValue());
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.onPreview = function () {
|
||||
if (this.isPlaying) {
|
||||
this.stopPreview();
|
||||
} else {
|
||||
this.startPreview();
|
||||
}
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.startPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = true;
|
||||
btnPreview.setText('取消');
|
||||
|
||||
this.onSelectAnim();
|
||||
|
||||
this.app.on(`animate.${this.id}`, this.onAnimate.bind(this));
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.stopPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = false;
|
||||
btnPreview.setText('预览');
|
||||
|
||||
this.app.on(`animate.${this.id}`, null);
|
||||
};
|
||||
|
||||
LMeshComponent.prototype.onAnimate = function (clock, deltaTime) {
|
||||
var model = this.selected.userData.model;
|
||||
model.update(clock.getElapsedTime() * 1000);
|
||||
};
|
||||
|
||||
export default LMeshComponent;
|
||||
342
ShadowEditor.Core/src/component/LightComponent.js
Normal file
342
ShadowEditor.Core/src/component/LightComponent.js
Normal file
@ -0,0 +1,342 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
|
||||
/**
|
||||
* 光源组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function LightComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
LightComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
LightComponent.prototype.constructor = LightComponent;
|
||||
|
||||
LightComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'lightPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '光源组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectColorRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '颜色'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'objectColor',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeColor.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectIntensityRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '强度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectIntensity',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeIntensity.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectDistanceRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '距离'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectDistance',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeDistance.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectAngleRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '角度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectAngle',
|
||||
scope: this.id,
|
||||
precision: 3,
|
||||
range: [0, Math.PI / 2],
|
||||
onChange: this.onChangeAngle.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectPenumbraRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半阴影'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectPenumbra',
|
||||
scope: this.id,
|
||||
range: [0, 1],
|
||||
onChange: this.onChangePenumbra.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectDecayRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '衰减'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectDecay',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeDecay.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectSkyColorRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '天空颜色'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'objectSkyColor',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeSkyColor.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectGroundColorRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '地面颜色'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'objectGroundColor',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeGroundColor.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectWidthRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectWidth',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeWidth.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectHeightRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectHeight',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeHeight.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
LightComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
LightComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
LightComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('lightPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Light) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var objectColorRow = UI.get('objectColorRow', this.id);
|
||||
var objectIntensityRow = UI.get('objectIntensityRow', this.id);
|
||||
var objectDistanceRow = UI.get('objectDistanceRow', this.id);
|
||||
var objectAngleRow = UI.get('objectAngleRow', this.id);
|
||||
var objectPenumbraRow = UI.get('objectPenumbraRow', this.id);
|
||||
var objectDecayRow = UI.get('objectDecayRow', this.id);
|
||||
var objectSkyColorRow = UI.get('objectSkyColorRow', this.id);
|
||||
var objectGroundColorRow = UI.get('objectGroundColorRow', this.id);
|
||||
var objectWidthRow = UI.get('objectWidthRow', this.id);
|
||||
var objectHeightRow = UI.get('objectHeightRow', this.id);
|
||||
|
||||
var objectColor = UI.get('objectColor', this.id);
|
||||
var objectIntensity = UI.get('objectIntensity', this.id);
|
||||
var objectDistance = UI.get('objectDistance', this.id);
|
||||
var objectAngle = UI.get('objectAngle', this.id);
|
||||
var objectPenumbra = UI.get('objectPenumbra', this.id);
|
||||
var objectDecay = UI.get('objectDecay', this.id);
|
||||
var objectSkyColor = UI.get('objectSkyColor', this.id);
|
||||
var objectGroundColor = UI.get('objectGroundColor', this.id);
|
||||
var objectWidth = UI.get('objectWidth', this.id);
|
||||
var objectHeight = UI.get('objectHeight', this.id);
|
||||
|
||||
if (this.selected instanceof THREE.HemisphereLight) {
|
||||
objectColorRow.dom.style.display = 'none';
|
||||
} else {
|
||||
objectColorRow.dom.style.display = '';
|
||||
objectColor.setValue(`#${this.selected.color.getHexString()}`);
|
||||
}
|
||||
|
||||
objectIntensityRow.dom.style.display = '';
|
||||
objectIntensity.setValue(this.selected.intensity);
|
||||
|
||||
if (this.selected instanceof THREE.PointLight || this.selected instanceof THREE.SpotLight) {
|
||||
objectDistanceRow.dom.style.display = '';
|
||||
objectDecayRow.dom.style.display = '';
|
||||
objectDistance.setValue(this.selected.distance);
|
||||
objectDecay.setValue(this.selected.decay);
|
||||
} else {
|
||||
objectDistanceRow.dom.style.display = 'none';
|
||||
objectDecayRow.dom.style.display = 'none';
|
||||
}
|
||||
|
||||
if (this.selected instanceof THREE.SpotLight) {
|
||||
objectAngleRow.dom.style.display = '';
|
||||
objectPenumbraRow.dom.style.display = '';
|
||||
objectAngle.setValue(this.selected.angle);
|
||||
objectPenumbra.setValue(this.selected.penumbra);
|
||||
} else {
|
||||
objectAngleRow.dom.style.display = 'none';
|
||||
objectPenumbraRow.dom.style.display = 'none';
|
||||
}
|
||||
|
||||
if (this.selected instanceof THREE.HemisphereLight) {
|
||||
objectSkyColorRow.dom.style.display = '';
|
||||
objectGroundColorRow.dom.style.display = '';
|
||||
objectSkyColor.setValue(`#${this.selected.color.getHexString()}`);
|
||||
objectGroundColor.setValue(`#${this.selected.groundColor.getHexString()}`);
|
||||
} else {
|
||||
objectSkyColorRow.dom.style.display = 'none';
|
||||
objectGroundColorRow.dom.style.display = 'none';
|
||||
}
|
||||
|
||||
if (this.selected instanceof THREE.RectAreaLight) {
|
||||
objectWidthRow.dom.style.display = '';
|
||||
objectHeightRow.dom.style.display = '';
|
||||
objectWidth.setValue(this.selected.width);
|
||||
objectHeight.setValue(this.selected.height);
|
||||
} else {
|
||||
objectWidthRow.dom.style.display = 'none';
|
||||
objectHeightRow.dom.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeColor = function () {
|
||||
var objectColor = UI.get('objectColor', this.id);
|
||||
this.selected.color = new THREE.Color(objectColor.getHexValue());
|
||||
var helper = this.selected.children.filter(n => n.userData.type === 'helper')[0];
|
||||
if (helper) {
|
||||
helper.material.color = this.selected.color;
|
||||
}
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeIntensity = function () {
|
||||
var objectIntensity = UI.get('objectIntensity', this.id);
|
||||
this.selected.intensity = objectIntensity.getValue();
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeDistance = function () {
|
||||
var objectDistance = UI.get('objectDistance', this.id);
|
||||
this.selected.distance = objectDistance.getValue();
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeAngle = function () {
|
||||
var objectAngle = UI.get('objectAngle', this.id);
|
||||
this.selected.angle = objectAngle.getValue();
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangePenumbra = function () {
|
||||
var objectPenumbra = UI.get('objectPenumbra', this.id);
|
||||
this.selected.penumbra = objectPenumbra.getValue();
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeDecay = function () {
|
||||
var objectDecay = UI.get('objectDecay', this.id);
|
||||
this.selected.decay = objectDecay.getValue();
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeSkyColor = function () {
|
||||
var objectSkyColor = UI.get('objectSkyColor', this.id);
|
||||
this.selected.color = new THREE.Color(objectSkyColor.getHexValue());
|
||||
|
||||
var sky = this.selected.children.filter(n => n.userData.type === 'sky')[0];
|
||||
if (sky) {
|
||||
sky.material.uniforms.topColor.value = this.selected.color;
|
||||
}
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeGroundColor = function () {
|
||||
var objectGroundColor = UI.get('objectGroundColor', this.id);
|
||||
this.selected.groundColor = new THREE.Color(objectGroundColor.getHexValue());
|
||||
|
||||
var sky = this.selected.children.filter(n => n.userData.type === 'sky')[0];
|
||||
if (sky) {
|
||||
sky.material.uniforms.bottomColor.value = this.selected.groundColor;
|
||||
}
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeWidth = function () {
|
||||
var objectWidth = UI.get('objectWidth', this.id);
|
||||
this.selected.width = objectWidth.getValue();
|
||||
};
|
||||
|
||||
LightComponent.prototype.onChangeHeight = function () {
|
||||
var objectHeight = UI.get('objectHeight', this.id);
|
||||
this.selected.height = objectHeight.getValue();
|
||||
};
|
||||
|
||||
export default LightComponent;
|
||||
114
ShadowEditor.Core/src/component/MMDComponent.js
Normal file
114
ShadowEditor.Core/src/component/MMDComponent.js
Normal file
@ -0,0 +1,114 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
import RemoveObjectCommand from '../command/RemoveObjectCommand';
|
||||
import AddObjectCommand from '../command/AddObjectCommand';
|
||||
import MMDWindow from '../editor/window/MMDWindow';
|
||||
|
||||
/**
|
||||
* MMD模型组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function MMDComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
MMDComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
MMDComponent.prototype.constructor = MMDComponent;
|
||||
|
||||
MMDComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'mmdPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: 'MMD模型'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '模型动画'
|
||||
}, {
|
||||
xtype: 'input',
|
||||
id: 'animation',
|
||||
scope: this.id,
|
||||
disabled: true,
|
||||
style: {
|
||||
width: '80px',
|
||||
fontSize: '12px'
|
||||
}
|
||||
}, {
|
||||
xtype: 'button',
|
||||
text: '选择',
|
||||
onClick: this.selectAnimation.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
MMDComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
MMDComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
MMDComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('mmdPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && (editor.selected.userData.Type === 'pmd' || editor.selected.userData.Type === 'pmx')) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var animation = UI.get('animation', this.id);
|
||||
|
||||
if (this.selected.userData.Animation) {
|
||||
animation.setValue(this.selected.userData.Animation.Name);
|
||||
}
|
||||
};
|
||||
|
||||
MMDComponent.prototype.selectAnimation = function () {
|
||||
if (this.mmdWindow === undefined) {
|
||||
this.mmdWindow = new MMDWindow({
|
||||
app: this.app,
|
||||
onSelect: this.onSelectAnimation.bind(this)
|
||||
});
|
||||
this.mmdWindow.render();
|
||||
}
|
||||
this.mmdWindow.show();
|
||||
};
|
||||
|
||||
MMDComponent.prototype.onSelectAnimation = function (data) {
|
||||
this.selected.userData.Animation = {};
|
||||
Object.assign(this.selected.userData.Animation, data);
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
export default MMDComponent;
|
||||
1443
ShadowEditor.Core/src/component/MaterialComponent.js
Normal file
1443
ShadowEditor.Core/src/component/MaterialComponent.js
Normal file
File diff suppressed because it is too large
Load Diff
583
ShadowEditor.Core/src/component/ParticleEmitterComponent.js
Normal file
583
ShadowEditor.Core/src/component/ParticleEmitterComponent.js
Normal file
@ -0,0 +1,583 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
|
||||
/**
|
||||
* 粒子发射器组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function ParticleEmitterComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
|
||||
this.isPlaying = false;
|
||||
}
|
||||
|
||||
ParticleEmitterComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
ParticleEmitterComponent.prototype.constructor = ParticleEmitterComponent;
|
||||
|
||||
ParticleEmitterComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'particleEmitterPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '粒子发射器'
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '位置'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'positionX',
|
||||
scope: this.id,
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'positionY',
|
||||
scope: this.id,
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'positionZ',
|
||||
scope: this.id,
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '位置发散'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'positionSpreadX',
|
||||
scope: this.id,
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'positionSpreadY',
|
||||
scope: this.id,
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'positionSpreadZ',
|
||||
scope: this.id,
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '速度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'velocityX',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeVelocity.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'velocityY',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeVelocity.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'velocityZ',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeVelocity.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '速度发散'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'velocitySpreadX',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeVelocity.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'velocitySpreadY',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeVelocity.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'velocitySpreadZ',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeVelocity.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '加速度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'accelerationX',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeAcceleration.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'accelerationY',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeAcceleration.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'accelerationZ',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeAcceleration.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '加速度发散'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'accelerationSpreadX',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeAcceleration.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'accelerationSpreadY',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeAcceleration.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'accelerationSpreadZ',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeAcceleration.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '颜色1'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'color1',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeColor.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '颜色2'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'color2',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeColor.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '颜色3'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'color3',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeColor.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '颜色4'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'color4',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeColor.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '尺寸'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'size',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeSize.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '尺寸发散'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'sizeSpread',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeSize.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '纹理'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'texture',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeTexture.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '粒子数量'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
range: [1, Infinity],
|
||||
id: 'particleCount',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeParticleCount.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '持续时长'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'maxAge',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeMaxAge.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '持续时长发散'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'maxAgeSpread',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeMaxAgeSpread.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label'
|
||||
}, {
|
||||
xtype: 'button',
|
||||
id: 'btnPreview',
|
||||
scope: this.id,
|
||||
text: '预览',
|
||||
onClick: this.onPreview.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('particleEmitterPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected.userData.type === 'ParticleEmitter') {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var positionX = UI.get('positionX', this.id);
|
||||
var positionY = UI.get('positionY', this.id);
|
||||
var positionZ = UI.get('positionZ', this.id);
|
||||
|
||||
var positionSpreadX = UI.get('positionSpreadX', this.id);
|
||||
var positionSpreadY = UI.get('positionSpreadY', this.id);
|
||||
var positionSpreadZ = UI.get('positionSpreadZ', this.id);
|
||||
|
||||
var velocityX = UI.get('velocityX', this.id);
|
||||
var velocityY = UI.get('velocityY', this.id);
|
||||
var velocityZ = UI.get('velocityZ', this.id);
|
||||
|
||||
var velocitySpreadX = UI.get('velocitySpreadX', this.id);
|
||||
var velocitySpreadY = UI.get('velocitySpreadY', this.id);
|
||||
var velocitySpreadZ = UI.get('velocitySpreadZ', this.id);
|
||||
|
||||
var accelerationX = UI.get('accelerationX', this.id);
|
||||
var accelerationY = UI.get('accelerationY', this.id);
|
||||
var accelerationZ = UI.get('accelerationZ', this.id);
|
||||
|
||||
var accelerationSpreadX = UI.get('accelerationSpreadX', this.id);
|
||||
var accelerationSpreadY = UI.get('accelerationSpreadY', this.id);
|
||||
var accelerationSpreadZ = UI.get('accelerationSpreadZ', this.id);
|
||||
|
||||
var color1 = UI.get('color1', this.id);
|
||||
var color2 = UI.get('color2', this.id);
|
||||
var color3 = UI.get('color3', this.id);
|
||||
var color4 = UI.get('color4', this.id);
|
||||
|
||||
var size = UI.get('size', this.id);
|
||||
var sizeSpread = UI.get('sizeSpread', this.id);
|
||||
var texture = UI.get('texture', this.id);
|
||||
var particleCount = UI.get('particleCount', this.id);
|
||||
var maxAge = UI.get('maxAge', this.id);
|
||||
var maxAgeSpread = UI.get('maxAgeSpread', this.id);
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
positionX.setValue(emitter.position.value.x);
|
||||
positionY.setValue(emitter.position.value.y);
|
||||
positionZ.setValue(emitter.position.value.z);
|
||||
|
||||
positionSpreadX.setValue(emitter.position.spread.x);
|
||||
positionSpreadY.setValue(emitter.position.spread.y);
|
||||
positionSpreadZ.setValue(emitter.position.spread.z);
|
||||
|
||||
velocityX.setValue(emitter.velocity.value.x);
|
||||
velocityY.setValue(emitter.velocity.value.y);
|
||||
velocityZ.setValue(emitter.velocity.value.z);
|
||||
|
||||
velocitySpreadX.setValue(emitter.velocity.spread.x);
|
||||
velocitySpreadY.setValue(emitter.velocity.spread.y);
|
||||
velocitySpreadZ.setValue(emitter.velocity.spread.z);
|
||||
|
||||
accelerationX.setValue(emitter.acceleration.value.x);
|
||||
accelerationY.setValue(emitter.acceleration.value.y);
|
||||
accelerationZ.setValue(emitter.acceleration.value.z);
|
||||
|
||||
accelerationSpreadX.setValue(emitter.acceleration.spread.x);
|
||||
accelerationSpreadY.setValue(emitter.acceleration.spread.y);
|
||||
accelerationSpreadZ.setValue(emitter.acceleration.spread.z);
|
||||
|
||||
color1.setValue(`#${emitter.color.value[0].getHexString()}`);
|
||||
color2.setValue(`#${emitter.color.value[1].getHexString()}`);
|
||||
color3.setValue(`#${emitter.color.value[2].getHexString()}`);
|
||||
color4.setValue(`#${emitter.color.value[3].getHexString()}`);
|
||||
|
||||
size.setValue(emitter.size.value[0]);
|
||||
sizeSpread.setValue(emitter.size.spread[0]);
|
||||
texture.setValue(group.texture);
|
||||
particleCount.setValue(emitter.particleCount);
|
||||
maxAge.setValue(emitter.maxAge.value);
|
||||
maxAgeSpread.setValue(emitter.maxAge.spread);
|
||||
|
||||
if (this.isPlaying) {
|
||||
btnPreview.setText('取消');
|
||||
} else {
|
||||
btnPreview.setText('预览');
|
||||
}
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangePosition = function () {
|
||||
var positionX = UI.get('positionX', this.id);
|
||||
var positionY = UI.get('positionY', this.id);
|
||||
var positionZ = UI.get('positionZ', this.id);
|
||||
|
||||
var positionSpreadX = UI.get('positionSpreadX', this.id);
|
||||
var positionSpreadY = UI.get('positionSpreadY', this.id);
|
||||
var positionSpreadZ = UI.get('positionSpreadZ', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
emitter.position.value.x = positionX.getValue();
|
||||
emitter.position.value.y = positionY.getValue();
|
||||
emitter.position.value.z = positionZ.getValue();
|
||||
|
||||
emitter.position.spread.x = positionSpreadX.getValue();
|
||||
emitter.position.spread.y = positionSpreadY.getValue();
|
||||
emitter.position.spread.z = positionSpreadZ.getValue();
|
||||
|
||||
emitter.updateFlags.position = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeVelocity = function () {
|
||||
var velocityX = UI.get('velocityX', this.id);
|
||||
var velocityY = UI.get('velocityY', this.id);
|
||||
var velocityZ = UI.get('velocityZ', this.id);
|
||||
|
||||
var velocitySpreadX = UI.get('velocitySpreadX', this.id);
|
||||
var velocitySpreadY = UI.get('velocitySpreadY', this.id);
|
||||
var velocitySpreadZ = UI.get('velocitySpreadZ', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
emitter.velocity.value.x = velocityX.getValue();
|
||||
emitter.velocity.value.y = velocityY.getValue();
|
||||
emitter.velocity.value.z = velocityZ.getValue();
|
||||
|
||||
emitter.velocity.spread.x = velocitySpreadX.getValue();
|
||||
emitter.velocity.spread.y = velocitySpreadY.getValue();
|
||||
emitter.velocity.spread.z = velocitySpreadZ.getValue();
|
||||
|
||||
emitter.updateFlags.velocity = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeAcceleration = function () {
|
||||
var accelerationX = UI.get('accelerationX', this.id);
|
||||
var accelerationY = UI.get('accelerationY', this.id);
|
||||
var accelerationZ = UI.get('accelerationZ', this.id);
|
||||
|
||||
var accelerationSpreadX = UI.get('accelerationSpreadX', this.id);
|
||||
var accelerationSpreadY = UI.get('accelerationSpreadY', this.id);
|
||||
var accelerationSpreadZ = UI.get('accelerationSpreadZ', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
emitter.acceleration.value.x = accelerationX.getValue();
|
||||
emitter.acceleration.value.y = accelerationY.getValue();
|
||||
emitter.acceleration.value.z = accelerationZ.getValue();
|
||||
|
||||
emitter.acceleration.spread.x = accelerationSpreadX.getValue();
|
||||
emitter.acceleration.spread.y = accelerationSpreadY.getValue();
|
||||
emitter.acceleration.spread.z = accelerationSpreadZ.getValue();
|
||||
|
||||
emitter.updateFlags.acceleration = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeColor = function () {
|
||||
var color1 = UI.get('color1', this.id);
|
||||
var color2 = UI.get('color2', this.id);
|
||||
var color3 = UI.get('color3', this.id);
|
||||
var color4 = UI.get('color4', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
emitter.color.value[0] = new THREE.Color(color1.getHexValue());
|
||||
emitter.color.value[1] = new THREE.Color(color2.getHexValue());
|
||||
emitter.color.value[2] = new THREE.Color(color3.getHexValue());
|
||||
emitter.color.value[3] = new THREE.Color(color4.getHexValue());
|
||||
|
||||
emitter.updateFlags.color = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeSize = function () {
|
||||
var size = UI.get('size', this.id);
|
||||
var sizeSpread = UI.get('sizeSpread', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
for (var i = 0; i < emitter.size.value.length; i++) {
|
||||
emitter.size.value[i] = size.getValue();
|
||||
emitter.size.spread[i] = sizeSpread.getValue();
|
||||
}
|
||||
|
||||
emitter.updateFlags.size = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeTexture = function () {
|
||||
var texture = UI.get('texture', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
texture = texture.getValue();
|
||||
texture.needsUpdate = true;
|
||||
|
||||
group.texture = texture;
|
||||
group.material.uniforms.texture.value = texture;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeParticleCount = function () {
|
||||
var particleCount = UI.get('particleCount', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
emitter.particleCount = particleCount.getValue();
|
||||
|
||||
emitter.updateFlags.params = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeMaxAge = function () {
|
||||
var maxAge = UI.get('maxAge', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
emitter.maxAge.value = maxAge.getValue();
|
||||
|
||||
emitter.updateFlags.params = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onChangeMaxAgeSpread = function () {
|
||||
var maxAgeSpread = UI.get('maxAgeSpread', this.id);
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = group.emitters[0];
|
||||
|
||||
emitter.maxAge.spread = maxAgeSpread.getValue();
|
||||
|
||||
emitter.updateFlags.params = true;
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onPreview = function () {
|
||||
if (this.isPlaying) {
|
||||
this.stopPreview();
|
||||
} else {
|
||||
this.startPreview();
|
||||
}
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.startPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = true;
|
||||
btnPreview.setText('取消');
|
||||
|
||||
this.app.on(`animate.${this.id}`, this.onAnimate.bind(this));
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.stopPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = false;
|
||||
btnPreview.setText('预览');
|
||||
|
||||
var group = this.selected.userData.group;
|
||||
var emitter = this.selected.userData.emitter;
|
||||
|
||||
group.removeEmitter(emitter);
|
||||
group.addEmitter(emitter);
|
||||
group.tick(0);
|
||||
|
||||
this.app.on(`animate.${this.id}`, null);
|
||||
};
|
||||
|
||||
ParticleEmitterComponent.prototype.onAnimate = function (clock, deltaTime) {
|
||||
var group = this.selected.userData.group;
|
||||
group.tick(deltaTime);
|
||||
};
|
||||
|
||||
export default ParticleEmitterComponent;
|
||||
258
ShadowEditor.Core/src/component/ReflectorComponent.js
Normal file
258
ShadowEditor.Core/src/component/ReflectorComponent.js
Normal file
@ -0,0 +1,258 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
import RemoveObjectCommand from '../command/RemoveObjectCommand';
|
||||
import AddObjectCommand from '../command/AddObjectCommand';
|
||||
|
||||
/**
|
||||
* 反光组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function ReflectorComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
ReflectorComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
ReflectorComponent.prototype.constructor = ReflectorComponent;
|
||||
|
||||
ReflectorComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'reflectorPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '反光组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '反光'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'reflect',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeReflect.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'colorRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '颜色'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'color',
|
||||
scope: this.id,
|
||||
value: 0xffffff,
|
||||
onChange: this.onChangeReflect.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'sizeRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '贴图尺寸'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'size',
|
||||
scope: this.id,
|
||||
options: {
|
||||
512: '512*512',
|
||||
1024: '1024*1024',
|
||||
2048: '2048*2048'
|
||||
},
|
||||
value: '1024',
|
||||
onChange: this.onChangeReflect.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'clipBiasRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '裁剪偏移'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'clipBias',
|
||||
scope: this.id,
|
||||
value: 0,
|
||||
onChange: this.onChangeReflect.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'recursionRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '递归'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'recursion',
|
||||
scope: this.id,
|
||||
value: false,
|
||||
onChange: this.onChangeReflect.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
ReflectorComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
ReflectorComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
ReflectorComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('reflectorPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var colorRow = UI.get('colorRow', this.id);
|
||||
var sizeRow = UI.get('sizeRow', this.id);
|
||||
var clipBiasRow = UI.get('clipBiasRow', this.id);
|
||||
var recursionRow = UI.get('recursionRow', this.id);
|
||||
|
||||
var reflect = UI.get('reflect', this.id);
|
||||
var color = UI.get('color', this.id);
|
||||
var size = UI.get('size', this.id);
|
||||
var clipBias = UI.get('clipBias', this.id);
|
||||
var recursion = UI.get('recursion', this.id);
|
||||
|
||||
reflect.setValue(this.selected instanceof THREE.Reflector);
|
||||
|
||||
if (this.selected instanceof THREE.Reflector) {
|
||||
colorRow.dom.style.display = '';
|
||||
sizeRow.dom.style.display = '';
|
||||
clipBiasRow.dom.style.display = '';
|
||||
recursionRow.dom.style.display = '';
|
||||
color.setHexValue(this.selected.userData.color);
|
||||
size.setValue(this.selected.userData.size);
|
||||
clipBias.setValue(this.selected.userData.clipBias);
|
||||
recursion.setValue(this.selected.userData.recursion);
|
||||
} else {
|
||||
colorRow.dom.style.display = 'none';
|
||||
sizeRow.dom.style.display = 'none';
|
||||
clipBiasRow.dom.style.display = 'none';
|
||||
recursionRow.dom.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
ReflectorComponent.prototype.onChangeReflect = function () {
|
||||
var reflect = UI.get('reflect', this.id);
|
||||
var color = UI.get('color', this.id);
|
||||
var size = UI.get('size', this.id);
|
||||
var clipBias = UI.get('clipBias', this.id);
|
||||
var recursion = UI.get('recursion', this.id);
|
||||
|
||||
var editor = this.app.editor;
|
||||
|
||||
if (reflect.getValue()) {
|
||||
color = color.getHexValue();
|
||||
|
||||
if (!(this.selected instanceof THREE.Reflector) && !Array.isArray(this.selected.material) && this.selected.material.color) {
|
||||
color = this.selected.material.color.getHex();
|
||||
}
|
||||
|
||||
var reflector = new THREE.Reflector(this.selected.geometry, {
|
||||
color: color,
|
||||
textureWidth: parseInt(size.getValue()),
|
||||
textureHeight: parseInt(size.getValue()),
|
||||
clipBias: clipBias.getValue(),
|
||||
recursion: recursion.getValue() ? 1 : 0
|
||||
});
|
||||
|
||||
reflector.name = this.selected.name;
|
||||
reflector.position.copy(this.selected.position);
|
||||
reflector.rotation.copy(this.selected.rotation);
|
||||
reflector.scale.copy(this.selected.scale);
|
||||
reflector.castShadow = this.selected.castShadow;
|
||||
reflector.receiveShadow = this.selected.receiveShadow;
|
||||
|
||||
if (this.selected instanceof THREE.Reflector) {
|
||||
Object.assign(reflector.userData, this.selected.userData);
|
||||
} else {
|
||||
Object.assign(reflector.userData, this.selected.userData, {
|
||||
mesh: this.selected
|
||||
});
|
||||
}
|
||||
|
||||
reflector.userData.color = color;
|
||||
reflector.userData.size = size.getValue();
|
||||
reflector.userData.clipBias = clipBias.getValue();
|
||||
reflector.userData.recursion = recursion.getValue();
|
||||
|
||||
var index = editor.scene.children.indexOf(this.selected);
|
||||
if (index > -1) {
|
||||
editor.scene.children[index] = reflector;
|
||||
reflector.parent = this.selected.parent;
|
||||
this.selected.parent = null;
|
||||
this.app.call(`objectRemoved`, this, this.selected);
|
||||
this.app.call(`objectAdded`, this, reflector);
|
||||
editor.select(reflector);
|
||||
this.app.call('sceneGraphChanged', this.id);
|
||||
}
|
||||
} else {
|
||||
if (this.selected instanceof THREE.Reflector) {
|
||||
var mesh = this.selected.userData.mesh;
|
||||
this.selected.userData.mesh = null;
|
||||
|
||||
mesh.name = this.selected.name;
|
||||
mesh.position.copy(this.selected.position);
|
||||
mesh.rotation.copy(this.selected.rotation);
|
||||
mesh.scale.copy(this.selected.scale);
|
||||
mesh.castShadow = this.selected.castShadow;
|
||||
mesh.receiveShadow = this.selected.receiveShadow;
|
||||
|
||||
if (!Array.isArray(mesh.material) && mesh.material.color) {
|
||||
mesh.material.color = new THREE.Color(color.getHexValue());
|
||||
}
|
||||
|
||||
Object.assign(mesh.userData, this.selected.userData);
|
||||
|
||||
var index = editor.scene.children.indexOf(this.selected);
|
||||
if (index > -1) {
|
||||
editor.scene.children[index] = mesh;
|
||||
mesh.parent = this.selected.parent;
|
||||
this.selected.parent = null;
|
||||
this.app.call(`objectRemoved`, this, this.selected);
|
||||
this.app.call(`objectAdded`, this, mesh);
|
||||
editor.select(mesh);
|
||||
this.app.call('sceneGraphChanged', this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default ReflectorComponent;
|
||||
613
ShadowEditor.Core/src/component/SceneComponent.js
Normal file
613
ShadowEditor.Core/src/component/SceneComponent.js
Normal file
@ -0,0 +1,613 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import Converter from '../utils/Converter';
|
||||
import Ajax from '../utils/Ajax';
|
||||
import TextureWindow from '../editor/window/TextureWindow';
|
||||
|
||||
/**
|
||||
* 场景组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function SceneComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
SceneComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
SceneComponent.prototype.constructor = SceneComponent;
|
||||
|
||||
SceneComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'scenePanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '场景组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '背景'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'backgroundType',
|
||||
scope: this.id,
|
||||
options: {
|
||||
'Color': '纯色',
|
||||
'Image': '背景图片',
|
||||
'SkyBox': '立体贴图'
|
||||
},
|
||||
onChange: this.onChangeBackgroundType.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundColorRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '背景颜色'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'backgroundColor',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundImageRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '背景图片'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'backgroundImage',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundPosXRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'x轴正向'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'backgroundPosX',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundNegXRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'x轴负向'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'backgroundNegX',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundPosYRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'y轴正向'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'backgroundPosY',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundNegYRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'y轴负向'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'backgroundNegY',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundPosZRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'z轴正向'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'backgroundPosZ',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'backgroundNegZRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'z轴负向'
|
||||
}, {
|
||||
xtype: 'texture',
|
||||
id: 'backgroundNegZ',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'cubeTextureCommandRow',
|
||||
scope: this.id,
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'button',
|
||||
text: '获取',
|
||||
onClick: this.onLoadCubeTexture.bind(this)
|
||||
}, {
|
||||
xtype: 'button',
|
||||
text: '上传',
|
||||
style: {
|
||||
marginLeft: '8px'
|
||||
},
|
||||
onClick: this.onSaveCubeTexture.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '雾'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'fogType',
|
||||
scope: this.id,
|
||||
options: {
|
||||
'None': '无',
|
||||
'Fog': '线性',
|
||||
'FogExp2': '指数型'
|
||||
},
|
||||
onChange: this.onChangeFogType.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'fogColorRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '雾颜色'
|
||||
}, {
|
||||
xtype: 'color',
|
||||
id: 'fogColor',
|
||||
scope: this.id,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'fogNearRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '雾近点'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'fogNear',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'fogFarRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '雾远点'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'fogFar',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'fogDensityRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '雾浓度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'fogDensity',
|
||||
scope: this.id,
|
||||
range: [0, 0.1],
|
||||
precision: 3,
|
||||
onChange: this.update.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
SceneComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SceneComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SceneComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('scenePanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Scene) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
var scene = this.selected;
|
||||
|
||||
// 背景
|
||||
var backgroundColorRow = UI.get('backgroundColorRow', this.id);
|
||||
var backgroundImageRow = UI.get('backgroundImageRow', this.id);
|
||||
var backgroundPosXRow = UI.get('backgroundPosXRow', this.id);
|
||||
var backgroundNegXRow = UI.get('backgroundNegXRow', this.id);
|
||||
var backgroundPosYRow = UI.get('backgroundPosYRow', this.id);
|
||||
var backgroundNegYRow = UI.get('backgroundNegYRow', this.id);
|
||||
var backgroundPosZRow = UI.get('backgroundPosZRow', this.id);
|
||||
var backgroundNegZRow = UI.get('backgroundNegZRow', this.id);
|
||||
|
||||
var backgroundType = UI.get('backgroundType', this.id);
|
||||
var backgroundColor = UI.get('backgroundColor', this.id);
|
||||
var backgroundImage = UI.get('backgroundImage', this.id);
|
||||
var backgroundPosX = UI.get('backgroundPosX', this.id);
|
||||
var backgroundNegX = UI.get('backgroundNegX', this.id);
|
||||
var backgroundPosY = UI.get('backgroundPosY', this.id);
|
||||
var backgroundNegY = UI.get('backgroundNegY', this.id);
|
||||
var backgroundPosZ = UI.get('backgroundPosZ', this.id);
|
||||
var backgroundNegZ = UI.get('backgroundNegZ', this.id);
|
||||
|
||||
backgroundType.setValue(`${scene.background instanceof THREE.CubeTexture ? 'SkyBox' : (scene.background instanceof THREE.Texture ? 'Image' : 'Color')}`);
|
||||
|
||||
backgroundColorRow.dom.style.display = scene.background instanceof THREE.Color ? '' : 'none';
|
||||
backgroundColor.setValue(`#${scene.background instanceof THREE.Color ? scene.background.getHexString() : 'aaaaaa'}`);
|
||||
|
||||
backgroundImageRow.dom.style.display = (scene.background instanceof THREE.Texture && !(scene.background instanceof THREE.CubeTexture)) ? '' : 'none';
|
||||
backgroundImage.setValue((scene.background instanceof THREE.Texture && !(scene.background instanceof THREE.CubeTexture)) ? scene.background : null);
|
||||
|
||||
backgroundPosXRow.dom.style.display = scene.background instanceof THREE.CubeTexture ? '' : 'none';
|
||||
backgroundNegXRow.dom.style.display = scene.background instanceof THREE.CubeTexture ? '' : 'none';
|
||||
backgroundPosYRow.dom.style.display = scene.background instanceof THREE.CubeTexture ? '' : 'none';
|
||||
backgroundNegYRow.dom.style.display = scene.background instanceof THREE.CubeTexture ? '' : 'none';
|
||||
backgroundPosZRow.dom.style.display = scene.background instanceof THREE.CubeTexture ? '' : 'none';
|
||||
backgroundNegZRow.dom.style.display = scene.background instanceof THREE.CubeTexture ? '' : 'none';
|
||||
|
||||
backgroundPosX.setValue(scene.background instanceof THREE.CubeTexture ? new THREE.Texture(scene.background.image[0]) : null);
|
||||
backgroundNegX.setValue(scene.background instanceof THREE.CubeTexture ? new THREE.Texture(scene.background.image[1]) : null);
|
||||
backgroundPosY.setValue(scene.background instanceof THREE.CubeTexture ? new THREE.Texture(scene.background.image[2]) : null);
|
||||
backgroundNegY.setValue(scene.background instanceof THREE.CubeTexture ? new THREE.Texture(scene.background.image[3]) : null);
|
||||
backgroundPosZ.setValue(scene.background instanceof THREE.CubeTexture ? new THREE.Texture(scene.background.image[4]) : null);
|
||||
backgroundNegZ.setValue(scene.background instanceof THREE.CubeTexture ? new THREE.Texture(scene.background.image[5]) : null);
|
||||
|
||||
// 雾效
|
||||
var fogColorRow = UI.get('fogColorRow', this.id);
|
||||
var fogNearRow = UI.get('fogNearRow', this.id);
|
||||
var fogFarRow = UI.get('fogFarRow', this.id);
|
||||
var fogDensityRow = UI.get('fogDensityRow', this.id);
|
||||
|
||||
var fogType = UI.get('fogType', this.id);
|
||||
var fogColor = UI.get('fogColor', this.id);
|
||||
var fogNear = UI.get('fogNear', this.id);
|
||||
var fogFar = UI.get('fogFar', this.id);
|
||||
var fogDensity = UI.get('fogDensity', this.id);
|
||||
|
||||
fogType.setValue(scene.fog == null ? 'None' : ((scene.fog instanceof THREE.FogExp2) ? 'FogExp2' : 'Fog'));
|
||||
|
||||
fogColorRow.dom.style.display = scene.fog == null ? 'none' : '';
|
||||
fogColor.setValue(`#${scene.fog == null ? 'aaaaaa' : scene.fog.color.getHexString()}`);
|
||||
|
||||
fogNearRow.dom.style.display = (scene.fog && scene.fog instanceof THREE.Fog) ? '' : 'none';
|
||||
fogNear.setValue((scene.fog && scene.fog instanceof THREE.Fog) ? scene.fog.near : 0.1);
|
||||
|
||||
fogFarRow.dom.style.display = (scene.fog && scene.fog instanceof THREE.Fog) ? '' : 'none';
|
||||
fogFar.setValue((scene.fog && scene.fog instanceof THREE.Fog) ? scene.fog.far : 50);
|
||||
|
||||
fogDensityRow.dom.style.display = (scene.fog && scene.fog instanceof THREE.FogExp2) ? '' : 'none';
|
||||
fogDensity.setValue((scene.fog && scene.fog instanceof THREE.FogExp2) ? fog.density : 0.05);
|
||||
};
|
||||
|
||||
SceneComponent.prototype.onChangeBackgroundType = function () { // 切换背景类型
|
||||
var backgroundType = UI.get('backgroundType', this.id);
|
||||
|
||||
var backgroundColorRow = UI.get('backgroundColorRow', this.id);
|
||||
var backgroundImageRow = UI.get('backgroundImageRow', this.id);
|
||||
var backgroundPosXRow = UI.get('backgroundPosXRow', this.id);
|
||||
var backgroundNegXRow = UI.get('backgroundNegXRow', this.id);
|
||||
var backgroundPosYRow = UI.get('backgroundPosYRow', this.id);
|
||||
var backgroundNegYRow = UI.get('backgroundNegYRow', this.id);
|
||||
var backgroundPosZRow = UI.get('backgroundPosZRow', this.id);
|
||||
var backgroundNegZRow = UI.get('backgroundNegZRow', this.id);
|
||||
|
||||
var cubeTextureCommandRow = UI.get('cubeTextureCommandRow', this.id);
|
||||
|
||||
switch (backgroundType.getValue()) {
|
||||
case 'Color':
|
||||
backgroundColorRow.dom.style.display = '';
|
||||
backgroundImageRow.dom.style.display = 'none';
|
||||
backgroundPosXRow.dom.style.display = 'none';
|
||||
backgroundNegXRow.dom.style.display = 'none';
|
||||
backgroundPosYRow.dom.style.display = 'none';
|
||||
backgroundNegYRow.dom.style.display = 'none';
|
||||
backgroundPosZRow.dom.style.display = 'none';
|
||||
backgroundNegZRow.dom.style.display = 'none';
|
||||
cubeTextureCommandRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'Image':
|
||||
backgroundColorRow.dom.style.display = 'none';
|
||||
backgroundImageRow.dom.style.display = '';
|
||||
backgroundPosXRow.dom.style.display = 'none';
|
||||
backgroundNegXRow.dom.style.display = 'none';
|
||||
backgroundPosYRow.dom.style.display = 'none';
|
||||
backgroundNegYRow.dom.style.display = 'none';
|
||||
backgroundPosZRow.dom.style.display = 'none';
|
||||
backgroundNegZRow.dom.style.display = 'none';
|
||||
cubeTextureCommandRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'SkyBox':
|
||||
backgroundColorRow.dom.style.display = 'none';
|
||||
backgroundImageRow.dom.style.display = 'none';
|
||||
backgroundPosXRow.dom.style.display = '';
|
||||
backgroundNegXRow.dom.style.display = '';
|
||||
backgroundPosYRow.dom.style.display = '';
|
||||
backgroundNegYRow.dom.style.display = '';
|
||||
backgroundPosZRow.dom.style.display = '';
|
||||
backgroundNegZRow.dom.style.display = '';
|
||||
cubeTextureCommandRow.dom.style.display = '';
|
||||
break;
|
||||
}
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
SceneComponent.prototype.onLoadCubeTexture = function () { // 加载立体贴图
|
||||
if (this.textureWindow === undefined) {
|
||||
this.textureWindow = new TextureWindow({
|
||||
app: this.app,
|
||||
onSelect: this.onSelectCubeTexture.bind(this)
|
||||
});
|
||||
this.textureWindow.render();
|
||||
}
|
||||
this.textureWindow.show();
|
||||
};
|
||||
|
||||
SceneComponent.prototype.onSelectCubeTexture = function (model) {
|
||||
if (model.Type !== 'cube') {
|
||||
UI.msg('只允许选择立体贴图!');
|
||||
return;
|
||||
}
|
||||
|
||||
var urls = model.Url.split(';');
|
||||
|
||||
var loader = new THREE.TextureLoader();
|
||||
|
||||
var promises = urls.map(url => {
|
||||
return new Promise(resolve => {
|
||||
loader.load(`${this.app.options.server}${url}`, texture => {
|
||||
resolve(texture);
|
||||
}, undefined, error => {
|
||||
console.error(error);
|
||||
UI.msg('立体贴图获取失败!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(promises).then(textures => {
|
||||
UI.get('backgroundPosX', this.id).setValue(textures[0]);
|
||||
UI.get('backgroundNegX', this.id).setValue(textures[1]);
|
||||
UI.get('backgroundPosY', this.id).setValue(textures[2]);
|
||||
UI.get('backgroundNegY', this.id).setValue(textures[3]);
|
||||
UI.get('backgroundPosZ', this.id).setValue(textures[4]);
|
||||
UI.get('backgroundNegZ', this.id).setValue(textures[5]);
|
||||
|
||||
this.textureWindow.hide();
|
||||
this.update();
|
||||
});
|
||||
};
|
||||
|
||||
SceneComponent.prototype.onSaveCubeTexture = function () { // 保存立体贴图
|
||||
var texturePosX = UI.get('backgroundPosX', this.id).getValue();
|
||||
var textureNegX = UI.get('backgroundNegX', this.id).getValue();
|
||||
var texturePosY = UI.get('backgroundPosY', this.id).getValue();
|
||||
var textureNegY = UI.get('backgroundNegY', this.id).getValue();
|
||||
var texturePosZ = UI.get('backgroundPosZ', this.id).getValue();
|
||||
var textureNegZ = UI.get('backgroundNegZ', this.id).getValue();
|
||||
|
||||
if (!texturePosX || !textureNegX || !texturePosY || !textureNegY || !texturePosZ || !textureNegZ) {
|
||||
UI.msg(`请上传所有立体贴图后再点击保存!`);
|
||||
return;
|
||||
}
|
||||
|
||||
var posXSrc = texturePosX.image.src;
|
||||
var negXSrc = textureNegX.image.src;
|
||||
var posYSrc = texturePosY.image.src;
|
||||
var negYSrc = textureNegY.image.src;
|
||||
var posZSrc = texturePosZ.image.src;
|
||||
var negZSrc = textureNegZ.image.src;
|
||||
|
||||
if (posXSrc.startsWith('http') || negXSrc.startsWith('http') || posYSrc.startsWith('http') || negYSrc.startsWith('http') || posZSrc.startsWith('http') || negZSrc.startsWith('http')) {
|
||||
UI.msg(`立体贴图已经存在于服务端,无需重复上传。`);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: 下面代码转换出的DataURL太大,不行!!!
|
||||
|
||||
// 如果src是服务端地址,则需要转成DataURL
|
||||
// if (posXSrc.startsWith('http')) {
|
||||
// posXSrc = Converter.canvasToDataURL(Converter.imageToCanvas(texturePosX.image));
|
||||
// }
|
||||
|
||||
// if (negXSrc.startsWith('http')) {
|
||||
// negXSrc = Converter.canvasToDataURL(Converter.imageToCanvas(textureNegX.image));
|
||||
// }
|
||||
|
||||
// if (posYSrc.startsWith('http')) {
|
||||
// posYSrc = Converter.canvasToDataURL(Converter.imageToCanvas(texturePosY.image));
|
||||
// }
|
||||
|
||||
// if (negYSrc.startsWith('http')) {
|
||||
// negYSrc = Converter.canvasToDataURL(Converter.imageToCanvas(textureNegY.image));
|
||||
// }
|
||||
|
||||
// if (posZSrc.startsWith('http')) {
|
||||
// posZSrc = Converter.canvasToDataURL(Converter.imageToCanvas(texturePosZ.image));
|
||||
// }
|
||||
|
||||
// if (negZSrc.startsWith('http')) {
|
||||
// negZSrc = Converter.canvasToDataURL(Converter.imageToCanvas(textureNegZ.image));
|
||||
// }
|
||||
|
||||
var promises = [
|
||||
Converter.dataURLtoFile(posXSrc, 'posX'),
|
||||
Converter.dataURLtoFile(negXSrc, 'negX'),
|
||||
Converter.dataURLtoFile(posYSrc, 'posY'),
|
||||
Converter.dataURLtoFile(negYSrc, 'negY'),
|
||||
Converter.dataURLtoFile(posZSrc, 'posZ'),
|
||||
Converter.dataURLtoFile(negZSrc, 'negZ'),
|
||||
];
|
||||
|
||||
Promise.all(promises).then(files => {
|
||||
Ajax.post(`${this.app.options.server}/api/Texture/Add`, {
|
||||
posX: files[0],
|
||||
negX: files[1],
|
||||
posY: files[2],
|
||||
negY: files[3],
|
||||
posZ: files[4],
|
||||
negZ: files[5],
|
||||
}, result => {
|
||||
var obj = JSON.parse(result);
|
||||
UI.msg(obj.Msg);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SceneComponent.prototype.onChangeFogType = function () { // 切换雾类型
|
||||
var fogType = UI.get('fogType', this.id);
|
||||
var fogColorRow = UI.get('fogColorRow', this.id);
|
||||
var fogNearRow = UI.get('fogNearRow', this.id);
|
||||
var fogFarRow = UI.get('fogFarRow', this.id);
|
||||
var fogDensityRow = UI.get('fogDensityRow', this.id);
|
||||
|
||||
switch (fogType.getValue()) {
|
||||
case 'None':
|
||||
fogColorRow.dom.style.display = 'none';
|
||||
fogNearRow.dom.style.display = 'none';
|
||||
fogFarRow.dom.style.display = 'none';
|
||||
fogDensityRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'Fog':
|
||||
fogColorRow.dom.style.display = '';
|
||||
fogNearRow.dom.style.display = '';
|
||||
fogFarRow.dom.style.display = '';
|
||||
fogDensityRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'FogExp2':
|
||||
fogColorRow.dom.style.display = '';
|
||||
fogNearRow.dom.style.display = 'none';
|
||||
fogFarRow.dom.style.display = 'none';
|
||||
fogDensityRow.dom.style.display = '';
|
||||
break;
|
||||
}
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
SceneComponent.prototype.update = function () {
|
||||
var scene = this.selected;
|
||||
|
||||
// 背景
|
||||
var backgroundType = UI.get('backgroundType', this.id).getValue();
|
||||
var backgroundColor = UI.get('backgroundColor', this.id).getHexValue();
|
||||
var backgroundImage = UI.get('backgroundImage', this.id).getValue();
|
||||
var backgroundPosX = UI.get('backgroundPosX', this.id).getValue();
|
||||
var backgroundNegX = UI.get('backgroundNegX', this.id).getValue();
|
||||
var backgroundPosY = UI.get('backgroundPosY', this.id).getValue();
|
||||
var backgroundNegY = UI.get('backgroundNegY', this.id).getValue();
|
||||
var backgroundPosZ = UI.get('backgroundPosZ', this.id).getValue();
|
||||
var backgroundNegZ = UI.get('backgroundNegZ', this.id).getValue();
|
||||
|
||||
switch (backgroundType) {
|
||||
case 'Color':
|
||||
scene.background = new THREE.Color(backgroundColor);
|
||||
break;
|
||||
case 'Image':
|
||||
if (backgroundImage) {
|
||||
scene.background = backgroundImage;
|
||||
}
|
||||
break;
|
||||
case 'SkyBox':
|
||||
if (backgroundPosX && backgroundNegX && backgroundPosY && backgroundNegY && backgroundPosZ && backgroundNegZ) {
|
||||
scene.background = new THREE.CubeTexture([
|
||||
backgroundPosX.image,
|
||||
backgroundNegX.image,
|
||||
backgroundPosY.image,
|
||||
backgroundNegY.image,
|
||||
backgroundPosZ.image,
|
||||
backgroundNegZ.image
|
||||
]);
|
||||
scene.background.needsUpdate = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 雾
|
||||
var fogType = UI.get('fogType', this.id).getValue();
|
||||
var fogColor = UI.get('fogColor', this.id).getHexValue();
|
||||
var fogNear = UI.get('fogNear', this.id).getValue();
|
||||
var fogFar = UI.get('fogFar', this.id).getValue();
|
||||
var fogDensity = UI.get('fogDensity', this.id).getValue();
|
||||
|
||||
switch (fogType) {
|
||||
case 'None':
|
||||
scene.fog = null;
|
||||
break;
|
||||
case 'Fog':
|
||||
scene.fog = new THREE.Fog(fogColor, fogNear, fogFar);
|
||||
break;
|
||||
case 'FogExp2':
|
||||
scene.fog = new THREE.FogExp2(fogColor, fogDensity);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export default SceneComponent;
|
||||
363
ShadowEditor.Core/src/component/ShadowComponent.js
Normal file
363
ShadowEditor.Core/src/component/ShadowComponent.js
Normal file
@ -0,0 +1,363 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
|
||||
/**
|
||||
* 阴影组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function ShadowComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
ShadowComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
ShadowComponent.prototype.constructor = ShadowComponent;
|
||||
|
||||
ShadowComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'shadowPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '阴影组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectShadowRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '阴影'
|
||||
}, {
|
||||
xtype: 'boolean',
|
||||
id: 'objectCastShadow',
|
||||
scope: this.id,
|
||||
value: false,
|
||||
text: '产生',
|
||||
onChange: this.onChangeCastShadow.bind(this)
|
||||
}, {
|
||||
xtype: 'boolean',
|
||||
id: 'objectReceiveShadow',
|
||||
scope: this.id,
|
||||
value: false,
|
||||
text: '接收',
|
||||
onChange: this.onChangeReceiveShadow.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectShadowRadiusRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectShadowRadius',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeShadowRadius.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectMapSizeRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '贴图尺寸'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'objectMapSize',
|
||||
scope: this.id,
|
||||
options: {
|
||||
512: '512*512',
|
||||
1024: '1024*1024',
|
||||
2048: '2048*2048'
|
||||
},
|
||||
value: 512,
|
||||
onChange: this.onChangeMapSize.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectBiasRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '偏差'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectBias',
|
||||
scope: this.id,
|
||||
value: 0,
|
||||
range: [0, 1],
|
||||
onChange: this.onChangeBias.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectCameraLeftRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '相机左'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectCameraLeft',
|
||||
scope: this.id,
|
||||
value: -5,
|
||||
onChange: this.onChangeCameraLeft.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectCameraRightRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '相机右'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectCameraRight',
|
||||
scope: this.id,
|
||||
value: 5,
|
||||
onChange: this.onChangeCameraRight.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectCameraTopRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '相机上'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectCameraTop',
|
||||
scope: this.id,
|
||||
value: 5,
|
||||
onChange: this.onChangeCameraTop.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectCameraBottomRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '相机下'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectCameraBottom',
|
||||
scope: this.id,
|
||||
value: -5,
|
||||
onChange: this.onChangeCameraBottom.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectCameraNearRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '相机近'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectCameraNear',
|
||||
scope: this.id,
|
||||
value: 0.5,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeCameraNear.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'objectCameraFarRow',
|
||||
scope: this.id,
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '相机远'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectCameraFar',
|
||||
scope: this.id,
|
||||
value: 0.5,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeCameraFar.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('shadowPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && (editor.selected instanceof THREE.Mesh || editor.selected instanceof THREE.DirectionalLight || editor.selected instanceof THREE.PointLight || editor.selected instanceof THREE.SpotLight)) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var objectShadowRadiusRow = UI.get('objectShadowRadiusRow', this.id);
|
||||
var objectMapSizeRow = UI.get('objectMapSizeRow', this.id);
|
||||
var objectBiasRow = UI.get('objectBiasRow', this.id);
|
||||
var objectCameraLeftRow = UI.get('objectCameraLeftRow', this.id);
|
||||
var objectCameraRightRow = UI.get('objectCameraRightRow', this.id);
|
||||
var objectCameraTopRow = UI.get('objectCameraTopRow', this.id);
|
||||
var objectCameraBottomRow = UI.get('objectCameraBottomRow', this.id);
|
||||
var objectCameraNearRow = UI.get('objectCameraNearRow', this.id);
|
||||
var objectCameraFarRow = UI.get('objectCameraFarRow', this.id);
|
||||
|
||||
var objectCastShadow = UI.get('objectCastShadow', this.id);
|
||||
var objectReceiveShadow = UI.get('objectReceiveShadow', this.id);
|
||||
var objectShadowRadius = UI.get('objectShadowRadius', this.id);
|
||||
var objectMapSize = UI.get('objectMapSize', this.id);
|
||||
var objectBias = UI.get('objectBias', this.id);
|
||||
var objectCameraLeft = UI.get('objectCameraLeft', this.id);
|
||||
var objectCameraRight = UI.get('objectCameraRight', this.id);
|
||||
var objectCameraTop = UI.get('objectCameraTop', this.id);
|
||||
var objectCameraBottom = UI.get('objectCameraBottom', this.id);
|
||||
var objectCameraNear = UI.get('objectCameraNear', this.id);
|
||||
var objectCameraFar = UI.get('objectCameraFar', this.id);
|
||||
|
||||
objectCastShadow.setValue(this.selected.castShadow);
|
||||
|
||||
if (this.selected instanceof THREE.Light) {
|
||||
objectReceiveShadow.dom.style.display = 'none';
|
||||
objectShadowRadiusRow.dom.style.display = '';
|
||||
objectMapSizeRow.dom.style.display = '';
|
||||
objectBiasRow.dom.style.display = '';
|
||||
objectCameraLeftRow.dom.style.display = '';
|
||||
objectCameraRightRow.dom.style.display = '';
|
||||
objectCameraTopRow.dom.style.display = '';
|
||||
objectCameraBottomRow.dom.style.display = '';
|
||||
objectCameraNearRow.dom.style.display = '';
|
||||
objectCameraFarRow.dom.style.display = '';
|
||||
|
||||
objectShadowRadius.setValue(this.selected.shadow.radius);
|
||||
var mapSize = this.selected.shadow.mapSize;
|
||||
objectMapSize.setValue(mapSize.x);
|
||||
objectBias.setValue(this.selected.shadow.bias);
|
||||
objectCameraLeft.setValue(this.selected.shadow.camera.left);
|
||||
objectCameraRight.setValue(this.selected.shadow.camera.right);
|
||||
objectCameraTop.setValue(this.selected.shadow.camera.top);
|
||||
objectCameraBottom.setValue(this.selected.shadow.camera.bottom);
|
||||
objectCameraNear.setValue(this.selected.shadow.camera.near);
|
||||
objectCameraFar.setValue(this.selected.shadow.camera.far);
|
||||
} else {
|
||||
objectReceiveShadow.dom.style.display = '';
|
||||
objectShadowRadiusRow.dom.style.display = 'none';
|
||||
objectMapSizeRow.dom.style.display = 'none';
|
||||
objectBiasRow.dom.style.display = 'none';
|
||||
objectCameraLeftRow.dom.style.display = 'none';
|
||||
objectCameraRightRow.dom.style.display = 'none';
|
||||
objectCameraTopRow.dom.style.display = 'none';
|
||||
objectCameraBottomRow.dom.style.display = 'none';
|
||||
objectCameraNearRow.dom.style.display = 'none';
|
||||
objectCameraFarRow.dom.style.display = 'none';
|
||||
|
||||
objectReceiveShadow.setValue(this.selected.receiveShadow);
|
||||
}
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeCastShadow = function () {
|
||||
var objectCastShadow = UI.get('objectCastShadow', this.id);
|
||||
this.selected.castShadow = objectCastShadow.getValue();
|
||||
if (this.selected instanceof THREE.Mesh) {
|
||||
this.updateMaterial(this.selected.material);
|
||||
}
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeReceiveShadow = function () {
|
||||
var objectReceiveShadow = UI.get('objectReceiveShadow', this.id);
|
||||
this.selected.receiveShadow = objectReceiveShadow.getValue();
|
||||
if (this.selected instanceof THREE.Mesh) {
|
||||
this.updateMaterial(this.selected.material);
|
||||
}
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeShadowRadius = function () {
|
||||
var objectShadowRadius = UI.get('objectShadowRadius', this.id);
|
||||
this.selected.shadow.radius = objectShadowRadius.getValue();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.updateMaterial = function (material) {
|
||||
if (Array.isArray(material)) {
|
||||
material.forEach(n => {
|
||||
n.needsUpdate = true;
|
||||
});
|
||||
} else {
|
||||
material.needsUpdate = true;
|
||||
}
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeMapSize = function () {
|
||||
var objectMapSize = UI.get('objectMapSize', this.id);
|
||||
var mapSize = objectMapSize.getValue();
|
||||
this.selected.shadow.mapSize.x = this.selected.shadow.mapSize.y = parseInt(mapSize);
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeBias = function () {
|
||||
var objectBias = UI.get('objectBias', this.id);
|
||||
this.selected.shadow.bias = objectBias.getValue();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeCameraLeft = function () {
|
||||
var objectCameraLeft = UI.get('objectCameraLeft', this.id);
|
||||
this.selected.shadow.camera.left = objectCameraLeft.getValue();
|
||||
this.selected.shadow.camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeCameraRight = function () {
|
||||
var objectCameraRight = UI.get('objectCameraRight', this.id);
|
||||
this.selected.shadow.camera.right = objectCameraRight.getValue();
|
||||
this.selected.shadow.camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeCameraTop = function () {
|
||||
var objectCameraTop = UI.get('objectCameraTop', this.id);
|
||||
this.selected.shadow.camera.top = objectCameraTop.getValue();
|
||||
this.selected.shadow.camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeCameraBottom = function () {
|
||||
var objectCameraBottom = UI.get('objectCameraBottom', this.id);
|
||||
this.selected.shadow.camera.bottom = objectCameraBottom.getValue();
|
||||
this.selected.shadow.camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeCameraNear = function () {
|
||||
var objectCameraNear = UI.get('objectCameraNear', this.id);
|
||||
this.selected.shadow.camera.near = objectCameraNear.getValue();
|
||||
this.selected.shadow.camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
ShadowComponent.prototype.onChangeCameraFar = function () {
|
||||
var objectCameraFar = UI.get('objectCameraFar', this.id);
|
||||
this.selected.shadow.camera.far = objectCameraFar.getValue();
|
||||
this.selected.shadow.camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
export default ShadowComponent;
|
||||
118
ShadowEditor.Core/src/component/SmokeComponent.js
Normal file
118
ShadowEditor.Core/src/component/SmokeComponent.js
Normal file
@ -0,0 +1,118 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
|
||||
/**
|
||||
* 烟组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function SmokeComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
|
||||
this.isPlaying = false;
|
||||
}
|
||||
|
||||
SmokeComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
SmokeComponent.prototype.constructor = SmokeComponent;
|
||||
|
||||
SmokeComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'smokePanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '烟组件'
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label'
|
||||
}, {
|
||||
xtype: 'button',
|
||||
id: 'btnPreview',
|
||||
scope: this.id,
|
||||
text: '预览',
|
||||
onClick: this.onPreview.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
SmokeComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SmokeComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SmokeComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('smokePanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected.userData.type === 'Smoke') {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
if (this.isPlaying) {
|
||||
btnPreview.setText('取消');
|
||||
} else {
|
||||
btnPreview.setText('预览');
|
||||
}
|
||||
};
|
||||
|
||||
SmokeComponent.prototype.onPreview = function () {
|
||||
if (this.isPlaying) {
|
||||
this.stopPreview();
|
||||
} else {
|
||||
this.startPreview();
|
||||
}
|
||||
};
|
||||
|
||||
SmokeComponent.prototype.startPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = true;
|
||||
btnPreview.setText('取消');
|
||||
|
||||
this.app.on(`animate.${this.id}`, this.onAnimate.bind(this));
|
||||
};
|
||||
|
||||
SmokeComponent.prototype.stopPreview = function () {
|
||||
var btnPreview = UI.get('btnPreview', this.id);
|
||||
|
||||
this.isPlaying = false;
|
||||
btnPreview.setText('预览');
|
||||
|
||||
this.app.on(`animate.${this.id}`, null);
|
||||
};
|
||||
|
||||
SmokeComponent.prototype.onAnimate = function (clock, deltaTime) {
|
||||
var elapsed = clock.getElapsedTime();
|
||||
this.selected.update(elapsed);
|
||||
};
|
||||
|
||||
export default SmokeComponent;
|
||||
238
ShadowEditor.Core/src/component/TransformComponent.js
Normal file
238
ShadowEditor.Core/src/component/TransformComponent.js
Normal file
@ -0,0 +1,238 @@
|
||||
import BaseComponent from './BaseComponent';
|
||||
import SetValueCommand from '../command/SetValueCommand';
|
||||
import SetPositionCommand from '../command/SetPositionCommand';
|
||||
import SetRotationCommand from '../command/SetRotationCommand';
|
||||
import SetScaleCommand from '../command/SetScaleCommand';
|
||||
|
||||
/**
|
||||
* 位移组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function TransformComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
TransformComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
TransformComponent.prototype.constructor = TransformComponent;
|
||||
|
||||
TransformComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'transformPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '位移组件'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '平移'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectPositionX',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectPositionY',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectPositionZ',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangePosition.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '旋转'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectRotationX',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangeRotation.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectRotationY',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangeRotation.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectRotationZ',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangeRotation.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '缩放'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'objectScaleLock',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: '50px'
|
||||
}
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectScaleX',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangeScale.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectScaleY',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangeScale.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'objectScaleZ',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChangeScale.bind(this)
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
TransformComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TransformComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TransformComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('transformPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var objectPositionX = UI.get('objectPositionX', this.id);
|
||||
var objectPositionY = UI.get('objectPositionY', this.id);
|
||||
var objectPositionZ = UI.get('objectPositionZ', this.id);
|
||||
|
||||
var objectRotationX = UI.get('objectRotationX', this.id);
|
||||
var objectRotationY = UI.get('objectRotationY', this.id);
|
||||
var objectRotationZ = UI.get('objectRotationZ', this.id);
|
||||
|
||||
var objectScaleX = UI.get('objectScaleX', this.id);
|
||||
var objectScaleY = UI.get('objectScaleY', this.id);
|
||||
var objectScaleZ = UI.get('objectScaleZ', this.id);
|
||||
|
||||
objectPositionX.setValue(this.selected.position.x);
|
||||
objectPositionY.setValue(this.selected.position.y);
|
||||
objectPositionZ.setValue(this.selected.position.z);
|
||||
|
||||
objectRotationX.setValue(this.selected.rotation.x * 180 / Math.PI);
|
||||
objectRotationY.setValue(this.selected.rotation.y * 180 / Math.PI);
|
||||
objectRotationZ.setValue(this.selected.rotation.z * 180 / Math.PI);
|
||||
|
||||
objectScaleX.setValue(this.selected.scale.x);
|
||||
objectScaleY.setValue(this.selected.scale.y);
|
||||
objectScaleZ.setValue(this.selected.scale.z);
|
||||
};
|
||||
|
||||
TransformComponent.prototype.onChangePosition = function () {
|
||||
var x = UI.get('objectPositionX', this.id).getValue();
|
||||
var y = UI.get('objectPositionY', this.id).getValue();
|
||||
var z = UI.get('objectPositionZ', this.id).getValue();
|
||||
|
||||
this.app.editor.execute(new SetPositionCommand(this.selected, new THREE.Vector3(x, y, z)));
|
||||
};
|
||||
|
||||
TransformComponent.prototype.onChangeRotation = function () {
|
||||
var x = UI.get('objectRotationX', this.id).getValue();
|
||||
var y = UI.get('objectRotationY', this.id).getValue();
|
||||
var z = UI.get('objectRotationZ', this.id).getValue();
|
||||
|
||||
this.app.editor.execute(new SetRotationCommand(this.selected, new THREE.Euler(x * Math.PI / 180, y * Math.PI / 180, z * Math.PI / 180)));
|
||||
};
|
||||
|
||||
TransformComponent.prototype.onChangeScale = function (value) {
|
||||
var x = UI.get('objectScaleX', this.id).getValue();
|
||||
var y = UI.get('objectScaleY', this.id).getValue();
|
||||
var z = UI.get('objectScaleZ', this.id).getValue();
|
||||
var locked = UI.get('objectScaleLock', this.id).getValue();
|
||||
|
||||
if (locked) {
|
||||
this.app.editor.execute(new SetScaleCommand(this.selected, new THREE.Vector3(value, value, value)));
|
||||
} else {
|
||||
this.app.editor.execute(new SetScaleCommand(this.selected, new THREE.Vector3(x, y, z)));
|
||||
}
|
||||
};
|
||||
|
||||
export default TransformComponent;
|
||||
@ -0,0 +1,194 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
|
||||
/**
|
||||
* 动画基本信息组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function BasicAnimationComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
BasicAnimationComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
BasicAnimationComponent.prototype.constructor = BasicAnimationComponent;
|
||||
|
||||
BasicAnimationComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'basicAnimationPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
borderTop: 0,
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '基本信息'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '名称'
|
||||
}, {
|
||||
xtype: 'input',
|
||||
id: 'name',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '120px'
|
||||
},
|
||||
onInput: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '目标'
|
||||
}, {
|
||||
xtype: 'input',
|
||||
id: 'target',
|
||||
scope: this.id,
|
||||
disabled: true,
|
||||
style: {
|
||||
width: '80px',
|
||||
marginRight: '8px'
|
||||
}
|
||||
}, {
|
||||
xtype: 'button',
|
||||
id: 'btnSetTarget',
|
||||
scope: this.id,
|
||||
text: '设置',
|
||||
onClick: this.onSetTarget.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '类型'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'type',
|
||||
scope: this.id,
|
||||
options: {
|
||||
Tween: '补间动画',
|
||||
Skeletal: '骨骼动画',
|
||||
Audio: '播放音乐',
|
||||
Filter: '滤镜动画',
|
||||
Particle: '粒子动画'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '开始时间'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginTime',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '结束时间'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endTime',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`animationSelected.${this.id}`, this.onAnimationSelected.bind(this));
|
||||
this.app.on(`animationChanged.${this.id}`, this.onAnimationChanged.bind(this));
|
||||
};
|
||||
|
||||
BasicAnimationComponent.prototype.onAnimationSelected = function (animation) {
|
||||
this.updateUI(animation);
|
||||
};
|
||||
|
||||
BasicAnimationComponent.prototype.onAnimationChanged = function (animation) {
|
||||
this.updateUI(animation);
|
||||
};
|
||||
|
||||
BasicAnimationComponent.prototype.updateUI = function (animation) {
|
||||
var container = UI.get('basicAnimationPanel', this.id);
|
||||
if (animation) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.animation = animation;
|
||||
|
||||
var name = UI.get('name', this.id);
|
||||
var target = UI.get('target', this.id);
|
||||
var type = UI.get('type', this.id);
|
||||
var beginTime = UI.get('beginTime', this.id);
|
||||
var endTime = UI.get('endTime', this.id);
|
||||
|
||||
name.setValue(this.animation.name);
|
||||
|
||||
if (this.animation.target === null) {
|
||||
target.setValue('(无)');
|
||||
} else {
|
||||
var obj = this.app.editor.objectByUuid(this.animation.target);
|
||||
if (obj === null) {
|
||||
target.setValue('(无)');
|
||||
console.warn(`BasicAnimationComponent: 动作物体${this.animation.target}在场景中不存在。`);
|
||||
} else {
|
||||
target.setValue(obj.name);
|
||||
}
|
||||
}
|
||||
|
||||
type.setValue(this.animation.type);
|
||||
beginTime.setValue(this.animation.beginTime);
|
||||
endTime.setValue(this.animation.endTime);
|
||||
};
|
||||
|
||||
BasicAnimationComponent.prototype.onSetTarget = function () {
|
||||
var selected = this.app.editor.selected;
|
||||
if (selected == null) {
|
||||
this.animation.target = null;
|
||||
} else {
|
||||
this.animation.target = selected.uuid;
|
||||
}
|
||||
|
||||
this.app.call('animationChanged', this, this.animation);
|
||||
};
|
||||
|
||||
BasicAnimationComponent.prototype.onChange = function () {
|
||||
var name = UI.get('name', this.id);
|
||||
var type = UI.get('type', this.id);
|
||||
var beginTime = UI.get('beginTime', this.id);
|
||||
var endTime = UI.get('endTime', this.id);
|
||||
|
||||
this.animation.name = name.getValue();
|
||||
this.animation.type = type.getValue();
|
||||
this.animation.beginTime = beginTime.getValue();
|
||||
this.animation.endTime = endTime.getValue();
|
||||
|
||||
this.app.call('animationChanged', this, this.animation);
|
||||
};
|
||||
|
||||
export default BasicAnimationComponent;
|
||||
@ -0,0 +1,556 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
|
||||
/**
|
||||
* 补间动画组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function TweenAnimationComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
TweenAnimationComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
TweenAnimationComponent.prototype.constructor = TweenAnimationComponent;
|
||||
|
||||
TweenAnimationComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'tweenAnimationPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '补间动画'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '开始状态'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'beginStatus',
|
||||
scope: this.id,
|
||||
options: {
|
||||
Current: '当前状态',
|
||||
Custom: '自定义'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'beginPositionRow',
|
||||
scope: this.id,
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '平移'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginPositionX',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginPositionY',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginPositionZ',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'beginRotationRow',
|
||||
scope: this.id,
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '旋转'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginRotationX',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginRotationY',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginRotationZ',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'beginScaleRow',
|
||||
scope: this.id,
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '缩放'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'beginScaleLock',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: '50px'
|
||||
}
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginScaleX',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginScaleY',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'beginScaleZ',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '插值函数'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'ease',
|
||||
scope: this.id,
|
||||
options: {
|
||||
linear: 'Linear',
|
||||
quadIn: 'Quad In',
|
||||
quadOut: 'Quad Out',
|
||||
quadInOut: 'Quad In Out',
|
||||
cubicIn: 'Cubic In',
|
||||
cubicOut: 'Cubic Out',
|
||||
cubicInOut: 'Cubic InOut',
|
||||
quartIn: 'Quart In',
|
||||
quartOut: 'Quart Out',
|
||||
quartInOut: 'Quart InOut',
|
||||
quintIn: 'Quint In',
|
||||
quintOut: 'Quint Out',
|
||||
quintInOut: 'Quint In Out',
|
||||
sineIn: 'Sine In',
|
||||
sineOut: 'Sine Out',
|
||||
sineInOut: 'Sine In Out',
|
||||
backIn: 'Back In',
|
||||
backOut: 'Back Out',
|
||||
backInOut: 'Back In Out',
|
||||
circIn: 'Circ In',
|
||||
circOut: 'Circ Out',
|
||||
circInOut: 'Circ In Out',
|
||||
bounceIn: 'Bounce In',
|
||||
bounceOut: 'Bounce Out',
|
||||
bounceInOut: 'Bounce In Out',
|
||||
elasticIn: 'Elastic In',
|
||||
elasticOut: 'Elastic Out',
|
||||
elasticInOut: 'Elastic In Out'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '结束状态'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'endStatus',
|
||||
scope: this.id,
|
||||
options: {
|
||||
Current: '当前状态',
|
||||
Custom: '自定义'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'endPositionRow',
|
||||
scope: this.id,
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '平移'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endPositionX',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endPositionY',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endPositionZ',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'endRotationRow',
|
||||
scope: this.id,
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '旋转'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endRotationX',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endRotationY',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endRotationZ',
|
||||
scope: this.id,
|
||||
step: 10,
|
||||
unit: '°',
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
id: 'endScaleRow',
|
||||
scope: this.id,
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '缩放'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'endScaleLock',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: '50px'
|
||||
}
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endScaleX',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endScaleY',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'endScaleZ',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0.01, Infinity],
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`animationSelected.${this.id}`, this.onAnimationSelected.bind(this));
|
||||
this.app.on(`animationChanged.${this.id}`, this.onAnimationChanged.bind(this));
|
||||
};
|
||||
|
||||
TweenAnimationComponent.prototype.onAnimationSelected = function (animation) {
|
||||
this.updateUI(animation);
|
||||
};
|
||||
|
||||
TweenAnimationComponent.prototype.onAnimationChanged = function (animation) {
|
||||
this.updateUI(animation);
|
||||
};
|
||||
|
||||
TweenAnimationComponent.prototype.updateUI = function (animation) {
|
||||
var container = UI.get('tweenAnimationPanel', this.id);
|
||||
if (animation && animation.type === 'Tween') {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.animation = animation;
|
||||
|
||||
var beginPositionRow = UI.get('beginPositionRow', this.id);
|
||||
var beginRotationRow = UI.get('beginRotationRow', this.id);
|
||||
var beginScaleRow = UI.get('beginScaleRow', this.id);
|
||||
var endPositionRow = UI.get('endPositionRow', this.id);
|
||||
var endRotationRow = UI.get('endRotationRow', this.id);
|
||||
var endScaleRow = UI.get('endScaleRow', this.id);
|
||||
|
||||
var beginStatus = UI.get('beginStatus', this.id);
|
||||
var beginPositionX = UI.get('beginPositionX', this.id);
|
||||
var beginPositionY = UI.get('beginPositionY', this.id);
|
||||
var beginPositionZ = UI.get('beginPositionZ', this.id);
|
||||
var beginRotationX = UI.get('beginRotationX', this.id);
|
||||
var beginRotationY = UI.get('beginRotationY', this.id);
|
||||
var beginRotationZ = UI.get('beginRotationZ', this.id);
|
||||
var beginScaleLock = UI.get('beginScaleLock', this.id);
|
||||
var beginScaleX = UI.get('beginScaleX', this.id);
|
||||
var beginScaleY = UI.get('beginScaleY', this.id);
|
||||
var beginScaleZ = UI.get('beginScaleZ', this.id);
|
||||
var ease = UI.get('ease', this.id);
|
||||
var endStatus = UI.get('endStatus', this.id);
|
||||
var endPositionX = UI.get('endPositionX', this.id);
|
||||
var endPositionY = UI.get('endPositionY', this.id);
|
||||
var endPositionZ = UI.get('endPositionZ', this.id);
|
||||
var endRotationX = UI.get('endRotationX', this.id);
|
||||
var endRotationY = UI.get('endRotationY', this.id);
|
||||
var endRotationZ = UI.get('endRotationZ', this.id);
|
||||
var endScaleLock = UI.get('endScaleLock', this.id);
|
||||
var endScaleX = UI.get('endScaleX', this.id);
|
||||
var endScaleY = UI.get('endScaleY', this.id);
|
||||
var endScaleZ = UI.get('endScaleZ', this.id);
|
||||
|
||||
switch (this.animation.beginStatus) {
|
||||
case 'Current':
|
||||
beginPositionRow.dom.style.display = 'none';
|
||||
beginRotationRow.dom.style.display = 'none';
|
||||
beginScaleRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'Custom':
|
||||
beginPositionRow.dom.style.display = '';
|
||||
beginRotationRow.dom.style.display = '';
|
||||
beginScaleRow.dom.style.display = '';
|
||||
break;
|
||||
}
|
||||
|
||||
switch (this.animation.endStatus) {
|
||||
case 'Current':
|
||||
endPositionRow.dom.style.display = 'none';
|
||||
endRotationRow.dom.style.display = 'none';
|
||||
endScaleRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'Custom':
|
||||
endPositionRow.dom.style.display = '';
|
||||
endRotationRow.dom.style.display = '';
|
||||
endScaleRow.dom.style.display = '';
|
||||
break;
|
||||
}
|
||||
|
||||
beginStatus.setValue(this.animation.beginStatus);
|
||||
beginPositionX.setValue(this.animation.beginPositionX);
|
||||
beginPositionY.setValue(this.animation.beginPositionY);
|
||||
beginPositionZ.setValue(this.animation.beginPositionZ);
|
||||
beginRotationX.setValue(this.animation.beginRotationX * 180 / Math.PI);
|
||||
beginRotationY.setValue(this.animation.beginRotationY * 180 / Math.PI);
|
||||
beginRotationZ.setValue(this.animation.beginRotationZ * 180 / Math.PI);
|
||||
beginScaleLock.setValue(this.animation.beginScaleLock);
|
||||
beginScaleX.setValue(this.animation.beginScaleX);
|
||||
beginScaleY.setValue(this.animation.beginScaleY);
|
||||
beginScaleZ.setValue(this.animation.beginScaleZ);
|
||||
ease.setValue(this.animation.ease);
|
||||
endStatus.setValue(this.animation.endStatus);
|
||||
endPositionX.setValue(this.animation.endPositionX);
|
||||
endPositionY.setValue(this.animation.endPositionY);
|
||||
endPositionZ.setValue(this.animation.endPositionZ);
|
||||
endRotationX.setValue(this.animation.endRotationX * 180 / Math.PI);
|
||||
endRotationY.setValue(this.animation.endRotationY * 180 / Math.PI);
|
||||
endRotationZ.setValue(this.animation.endRotationZ * 180 / Math.PI);
|
||||
endScaleLock.setValue(this.animation.endScaleLock);
|
||||
endScaleX.setValue(this.animation.endScaleX);
|
||||
endScaleY.setValue(this.animation.endScaleY);
|
||||
endScaleZ.setValue(this.animation.endScaleZ);
|
||||
};
|
||||
|
||||
TweenAnimationComponent.prototype.onChange = function () {
|
||||
var beginPositionRow = UI.get('beginPositionRow', this.id);
|
||||
var beginRotationRow = UI.get('beginRotationRow', this.id);
|
||||
var beginScaleRow = UI.get('beginScaleRow', this.id);
|
||||
var endPositionRow = UI.get('endPositionRow', this.id);
|
||||
var endRotationRow = UI.get('endRotationRow', this.id);
|
||||
var endScaleRow = UI.get('endScaleRow', this.id);
|
||||
|
||||
var beginStatus = UI.get('beginStatus', this.id);
|
||||
var beginPositionX = UI.get('beginPositionX', this.id);
|
||||
var beginPositionY = UI.get('beginPositionY', this.id);
|
||||
var beginPositionZ = UI.get('beginPositionZ', this.id);
|
||||
var beginRotationX = UI.get('beginRotationX', this.id);
|
||||
var beginRotationY = UI.get('beginRotationY', this.id);
|
||||
var beginRotationZ = UI.get('beginRotationZ', this.id);
|
||||
var beginScaleLock = UI.get('beginScaleLock', this.id);
|
||||
var beginScaleX = UI.get('beginScaleX', this.id);
|
||||
var beginScaleY = UI.get('beginScaleY', this.id);
|
||||
var beginScaleZ = UI.get('beginScaleZ', this.id);
|
||||
var ease = UI.get('ease', this.id);
|
||||
var endStatus = UI.get('endStatus', this.id);
|
||||
var endPositionX = UI.get('endPositionX', this.id);
|
||||
var endPositionY = UI.get('endPositionY', this.id);
|
||||
var endPositionZ = UI.get('endPositionZ', this.id);
|
||||
var endRotationX = UI.get('endRotationX', this.id);
|
||||
var endRotationY = UI.get('endRotationY', this.id);
|
||||
var endRotationZ = UI.get('endRotationZ', this.id);
|
||||
var endScaleLock = UI.get('endScaleLock', this.id);
|
||||
var endScaleX = UI.get('endScaleX', this.id);
|
||||
var endScaleY = UI.get('endScaleY', this.id);
|
||||
var endScaleZ = UI.get('endScaleZ', this.id);
|
||||
|
||||
switch (beginStatus.getValue()) {
|
||||
case 'Current':
|
||||
beginPositionRow.dom.style.display = 'none';
|
||||
beginRotationRow.dom.style.display = 'none';
|
||||
beginScaleRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'Custom':
|
||||
beginPositionRow.dom.style.display = '';
|
||||
beginRotationRow.dom.style.display = '';
|
||||
beginScaleRow.dom.style.display = '';
|
||||
break;
|
||||
}
|
||||
|
||||
switch (endStatus.getValue()) {
|
||||
case 'Current':
|
||||
endPositionRow.dom.style.display = 'none';
|
||||
endRotationRow.dom.style.display = 'none';
|
||||
endScaleRow.dom.style.display = 'none';
|
||||
break;
|
||||
case 'Custom':
|
||||
endPositionRow.dom.style.display = '';
|
||||
endRotationRow.dom.style.display = '';
|
||||
endScaleRow.dom.style.display = '';
|
||||
break;
|
||||
}
|
||||
|
||||
this.animation.beginStatus = beginStatus.getValue();
|
||||
this.animation.beginPositionX = beginPositionX.getValue();
|
||||
this.animation.beginPositionY = beginPositionY.getValue();
|
||||
this.animation.beginPositionZ = beginPositionZ.getValue();
|
||||
this.animation.beginRotationX = beginRotationX.getValue() * Math.PI / 180;
|
||||
this.animation.beginRotationY = beginRotationY.getValue() * Math.PI / 180;
|
||||
this.animation.beginRotationZ = beginRotationZ.getValue() * Math.PI / 180;
|
||||
this.animation.beginScaleLock = beginScaleLock.getValue();
|
||||
this.animation.beginScaleX = beginScaleX.getValue();
|
||||
this.animation.beginScaleY = beginScaleY.getValue();
|
||||
this.animation.beginScaleZ = beginScaleZ.getValue();
|
||||
this.animation.ease = ease.getValue();
|
||||
this.animation.endStatus = endStatus.getValue();
|
||||
this.animation.endPositionX = endPositionX.getValue();
|
||||
this.animation.endPositionY = endPositionY.getValue();
|
||||
this.animation.endPositionZ = endPositionZ.getValue();
|
||||
this.animation.endRotationX = endRotationX.getValue() * Math.PI / 180;
|
||||
this.animation.endRotationY = endRotationY.getValue() * Math.PI / 180;
|
||||
this.animation.endRotationZ = endRotationZ.getValue() * Math.PI / 180;
|
||||
this.animation.endScaleLock = endScaleLock.getValue();
|
||||
this.animation.endScaleX = endScaleX.getValue();
|
||||
this.animation.endScaleY = endScaleY.getValue();
|
||||
this.animation.endScaleZ = endScaleZ.getValue();
|
||||
|
||||
this.app.call('animationChanged', this, this.animation);
|
||||
};
|
||||
|
||||
export default TweenAnimationComponent;
|
||||
@ -0,0 +1,92 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 音频监听器组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function AudioListenerComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
AudioListenerComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
AudioListenerComponent.prototype.constructor = AudioListenerComponent;
|
||||
|
||||
AudioListenerComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'audioListenerPanel',
|
||||
scope: this.id,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '音频监听器'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '主音量'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'masterVolume',
|
||||
scope: this.id,
|
||||
range: [0, 1],
|
||||
value: 1,
|
||||
onChange: this.onChangeMasterVolume.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
AudioListenerComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
AudioListenerComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
AudioListenerComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('audioListenerPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.PerspectiveCamera && editor.selected.children.indexOf(editor.audioListener) > -1) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.audioListener;
|
||||
|
||||
var masterVolume = UI.get('masterVolume', this.id);
|
||||
|
||||
masterVolume.setValue(this.selected.getMasterVolume());
|
||||
};
|
||||
|
||||
AudioListenerComponent.prototype.onChangeMasterVolume = function () {
|
||||
var masterVolume = UI.get('masterVolume', this.id);
|
||||
|
||||
this.selected.setMasterVolume(masterVolume.getValue());
|
||||
};
|
||||
|
||||
export default AudioListenerComponent;
|
||||
@ -0,0 +1,216 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
import AudioWindow from '../../editor/window/AudioWindow';
|
||||
|
||||
/**
|
||||
* 背景音乐组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function BackgroundMusicComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
BackgroundMusicComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
BackgroundMusicComponent.prototype.constructor = BackgroundMusicComponent;
|
||||
|
||||
BackgroundMusicComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'backgroundMusicPanel',
|
||||
scope: this.id,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '背景音乐'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '音频'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'name',
|
||||
scope: this.id,
|
||||
text: '(无)',
|
||||
style: {
|
||||
width: '80px',
|
||||
border: '1px solid #ddd',
|
||||
marginRight: '8px'
|
||||
}
|
||||
}, {
|
||||
xtype: 'button',
|
||||
text: '选择',
|
||||
onClick: this.onSelect.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '自动播放'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'autoplay',
|
||||
scope: this.id,
|
||||
value: false,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '循环播放'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'loop',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '音量'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'volumn',
|
||||
scope: this.id,
|
||||
range: [0, 1],
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label'
|
||||
}, {
|
||||
xtype: 'button',
|
||||
id: 'btnPlay',
|
||||
scope: this.id,
|
||||
text: '播放',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
onClick: this.onPlay.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
BackgroundMusicComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
BackgroundMusicComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
BackgroundMusicComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('backgroundMusicPanel', this.id);
|
||||
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Audio) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var name = UI.get('name', this.id);
|
||||
var autoplay = UI.get('autoplay', this.id);
|
||||
var loop = UI.get('loop', this.id);
|
||||
var volumn = UI.get('volumn', this.id);
|
||||
var btnPlay = UI.get('btnPlay', this.id);
|
||||
|
||||
name.setValue(this.selected.userData.Name);
|
||||
autoplay.setValue(this.selected.userData.autoplay);
|
||||
loop.setValue(this.selected.getLoop());
|
||||
volumn.setValue(this.selected.getVolume());
|
||||
|
||||
if (this.selected.buffer) {
|
||||
btnPlay.dom.style.display = '';
|
||||
if (this.selected.isPlaying) {
|
||||
btnPlay.setText('停止');
|
||||
} else {
|
||||
btnPlay.setText('播放');
|
||||
}
|
||||
} else {
|
||||
btnPlay.dom.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundMusicComponent.prototype.onSelect = function () {
|
||||
if (this.window === undefined) {
|
||||
this.window = new AudioWindow({
|
||||
app: this.app,
|
||||
onSelect: this.onChange.bind(this)
|
||||
});
|
||||
this.window.render();
|
||||
}
|
||||
this.window.show();
|
||||
};
|
||||
|
||||
BackgroundMusicComponent.prototype.onChange = function (obj) {
|
||||
var name = UI.get('name', this.id);
|
||||
var autoplay = UI.get('autoplay', this.id);
|
||||
var loop = UI.get('loop', this.id);
|
||||
var volumn = UI.get('volumn', this.id);
|
||||
var btnPlay = UI.get('btnPlay', this.id);
|
||||
|
||||
if (obj) { // 仅选择窗口会传递obj参数
|
||||
Object.assign(this.selected.userData, obj);
|
||||
var loader = new THREE.AudioLoader();
|
||||
loader.load(obj.Url, buffer => {
|
||||
this.selected.setBuffer(buffer);
|
||||
btnPlay.dom.style.display = '';
|
||||
});
|
||||
}
|
||||
|
||||
this.selected.userData.autoplay = autoplay.getValue(); // 这里不能给this.selected赋值,否则音频会自动播放
|
||||
this.selected.setLoop(loop.getValue());
|
||||
this.selected.setVolume(volumn.getValue());
|
||||
this.updateUI();
|
||||
|
||||
if (this.window) {
|
||||
this.window.hide();
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundMusicComponent.prototype.onPlay = function () {
|
||||
var btnPlay = UI.get('btnPlay', this.id);
|
||||
|
||||
if (this.selected.buffer) {
|
||||
btnPlay.dom.style.display = '';
|
||||
if (this.selected.isPlaying) {
|
||||
this.selected.stop();
|
||||
btnPlay.setText('播放');
|
||||
} else {
|
||||
this.selected.play();
|
||||
btnPlay.setText('停止');
|
||||
}
|
||||
} else {
|
||||
btnPlay.dom.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
export default BackgroundMusicComponent;
|
||||
169
ShadowEditor.Core/src/component/geometry/BoxGeometryComponent.js
Normal file
169
ShadowEditor.Core/src/component/geometry/BoxGeometryComponent.js
Normal file
@ -0,0 +1,169 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 正方体组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function BoxGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
BoxGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
BoxGeometryComponent.prototype.constructor = BoxGeometryComponent;
|
||||
|
||||
BoxGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'width',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'height',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '深度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'depth',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'widthSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'heightSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '深度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'depthSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
BoxGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
BoxGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
BoxGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.BoxBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var width = UI.get('width', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
var depthSegments = UI.get('depthSegments', this.id);
|
||||
|
||||
width.setValue(this.selected.geometry.parameters.width);
|
||||
height.setValue(this.selected.geometry.parameters.height);
|
||||
depth.setValue(this.selected.geometry.parameters.depth);
|
||||
widthSegments.setValue(this.selected.geometry.parameters.widthSegments);
|
||||
heightSegments.setValue(this.selected.geometry.parameters.heightSegments);
|
||||
depthSegments.setValue(this.selected.geometry.parameters.depthSegments);
|
||||
};
|
||||
|
||||
BoxGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var width = UI.get('width', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
var depthSegments = UI.get('depthSegments', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.BoxBufferGeometry(
|
||||
width.getValue(),
|
||||
height.getValue(),
|
||||
depth.getValue(),
|
||||
widthSegments.getValue(),
|
||||
heightSegments.getValue(),
|
||||
depthSegments.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default BoxGeometryComponent;
|
||||
@ -0,0 +1,132 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 圆形组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function CircleGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
CircleGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
CircleGeometryComponent.prototype.constructor = CircleGeometryComponent;
|
||||
|
||||
CircleGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radius',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'segments',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
range: [3, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '开始弧度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'thetaStart',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '转过弧度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'thetaLength',
|
||||
scope: this.id,
|
||||
value: Math.PI * 2,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
CircleGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
CircleGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
CircleGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.CircleBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var radius = UI.get('radius', this.id);
|
||||
var segments = UI.get('segments', this.id);
|
||||
var thetaStart = UI.get('thetaStart', this.id);
|
||||
var thetaLength = UI.get('thetaLength', this.id);
|
||||
|
||||
radius.setValue(this.selected.geometry.parameters.radius);
|
||||
segments.setValue(this.selected.geometry.parameters.segments);
|
||||
thetaStart.setValue(this.selected.geometry.parameters.thetaStart === undefined ? 0 : this.selected.geometry.parameters.thetaStart);
|
||||
thetaLength.setValue(this.selected.geometry.parameters.thetaLength === undefined ? Math.PI * 2 : this.selected.geometry.parameters.thetaLength);
|
||||
};
|
||||
|
||||
CircleGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var radius = UI.get('radius', this.id);
|
||||
var segments = UI.get('segments', this.id);
|
||||
var thetaStart = UI.get('thetaStart', this.id);
|
||||
var thetaLength = UI.get('thetaLength', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.CircleBufferGeometry(
|
||||
radius.getValue(),
|
||||
segments.getValue(),
|
||||
thetaStart.getValue(),
|
||||
thetaLength.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default CircleGeometryComponent;
|
||||
@ -0,0 +1,165 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 圆柱组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function CylinderGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
CylinderGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
CylinderGeometryComponent.prototype.constructor = CylinderGeometryComponent;
|
||||
|
||||
CylinderGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '顶部半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radiusTop',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '底部半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radiusBottom',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'height',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '圆形分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'radialSegments',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'heightSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '两端开口'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'openEnded',
|
||||
scope: this.id,
|
||||
value: false,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
CylinderGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
CylinderGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
CylinderGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.CylinderBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var radiusTop = UI.get('radiusTop', this.id);
|
||||
var radiusBottom = UI.get('radiusBottom', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var radialSegments = UI.get('radialSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
var openEnded = UI.get('openEnded', this.id);
|
||||
|
||||
radiusTop.setValue(this.selected.geometry.parameters.radiusTop);
|
||||
radiusBottom.setValue(this.selected.geometry.parameters.radiusBottom);
|
||||
height.setValue(this.selected.geometry.parameters.height);
|
||||
radialSegments.setValue(this.selected.geometry.parameters.radialSegments);
|
||||
heightSegments.setValue(this.selected.geometry.parameters.heightSegments);
|
||||
openEnded.setValue(this.selected.geometry.parameters.openEnded);
|
||||
};
|
||||
|
||||
CylinderGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var radiusTop = UI.get('radiusTop', this.id);
|
||||
var radiusBottom = UI.get('radiusBottom', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var radialSegments = UI.get('radialSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
var openEnded = UI.get('openEnded', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.CylinderBufferGeometry(
|
||||
radiusTop.getValue(),
|
||||
radiusBottom.getValue(),
|
||||
height.getValue(),
|
||||
radialSegments.getValue(),
|
||||
heightSegments.getValue(),
|
||||
openEnded.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default CylinderGeometryComponent;
|
||||
@ -0,0 +1,100 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 二十面体组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function IcosahedronGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
IcosahedronGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
IcosahedronGeometryComponent.prototype.constructor = IcosahedronGeometryComponent;
|
||||
|
||||
IcosahedronGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radius',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '面片分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'detail',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [0, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
IcosahedronGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
IcosahedronGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
IcosahedronGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.IcosahedronBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var radius = UI.get('radius', this.id);
|
||||
var detail = UI.get('detail', this.id);
|
||||
|
||||
radius.setValue(this.selected.geometry.parameters.radius);
|
||||
detail.setValue(this.selected.geometry.parameters.detail);
|
||||
};
|
||||
|
||||
IcosahedronGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var radius = UI.get('radius', this.id);
|
||||
var detail = UI.get('detail', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.IcosahedronBufferGeometry(
|
||||
radius.getValue(),
|
||||
detail.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default IcosahedronGeometryComponent;
|
||||
@ -0,0 +1,118 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 车床组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function LatheGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
LatheGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
LatheGeometryComponent.prototype.constructor = LatheGeometryComponent;
|
||||
|
||||
LatheGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '径向分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'segments',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '开始弧度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'phiStart',
|
||||
scope: this.id,
|
||||
value: 0,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '转过弧度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'phiLength',
|
||||
scope: this.id,
|
||||
value: Math.PI * 2,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
LatheGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
LatheGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
LatheGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.LatheBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var segments = UI.get('segments', this.id);
|
||||
var phiStart = UI.get('phiStart', this.id);
|
||||
var phiLength = UI.get('phiLength', this.id);
|
||||
|
||||
segments.setValue(this.selected.geometry.parameters.segments);
|
||||
phiStart.setValue(this.selected.geometry.parameters.phiStart);
|
||||
phiLength.setValue(this.selected.geometry.parameters.phiLength);
|
||||
};
|
||||
|
||||
LatheGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var segments = UI.get('segments', this.id);
|
||||
var phiStart = UI.get('phiStart', this.id);
|
||||
var phiLength = UI.get('phiLength', this.id);
|
||||
|
||||
var points = this.selected.geometry.parameters.points;
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.LatheBufferGeometry(
|
||||
points,
|
||||
segments.getValue(),
|
||||
phiStart.getValue(),
|
||||
phiLength.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default LatheGeometryComponent;
|
||||
@ -0,0 +1,136 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 平板组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function PlaneGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
PlaneGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
PlaneGeometryComponent.prototype.constructor = PlaneGeometryComponent;
|
||||
|
||||
PlaneGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'width',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'height',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'widthSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'heightSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
PlaneGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
PlaneGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
PlaneGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.PlaneBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var width = UI.get('width', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
|
||||
width.setValue(this.selected.geometry.parameters.width);
|
||||
height.setValue(this.selected.geometry.parameters.height);
|
||||
widthSegments.setValue(this.selected.geometry.parameters.widthSegments);
|
||||
heightSegments.setValue(this.selected.geometry.parameters.heightSegments);
|
||||
};
|
||||
|
||||
PlaneGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var width = UI.get('width', this.id);
|
||||
var height = UI.get('height', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.PlaneBufferGeometry(
|
||||
width.getValue(),
|
||||
height.getValue(),
|
||||
widthSegments.getValue(),
|
||||
heightSegments.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default PlaneGeometryComponent;
|
||||
@ -0,0 +1,181 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 球体组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function SphereGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
SphereGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
SphereGeometryComponent.prototype.constructor = SphereGeometryComponent;
|
||||
|
||||
SphereGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radius',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'widthSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '高度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'heightSegments',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '开始经度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'phiStart',
|
||||
scope: this.id,
|
||||
value: 0,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '转过经度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'phiLength',
|
||||
scope: this.id,
|
||||
value: Math.PI * 2,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '开始纬度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'thetaStart',
|
||||
scope: this.id,
|
||||
value: 0,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '转过纬度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'thetaLength',
|
||||
scope: this.id,
|
||||
value: Math.PI / 2,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
SphereGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SphereGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SphereGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.SphereBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var radius = UI.get('radius', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
var phiStart = UI.get('phiStart', this.id);
|
||||
var phiLength = UI.get('phiLength', this.id);
|
||||
var thetaStart = UI.get('thetaStart', this.id);
|
||||
var thetaLength = UI.get('thetaLength', this.id);
|
||||
|
||||
radius.setValue(this.selected.geometry.parameters.radius);
|
||||
widthSegments.setValue(this.selected.geometry.parameters.widthSegments);
|
||||
heightSegments.setValue(this.selected.geometry.parameters.heightSegments);
|
||||
phiStart.setValue(this.selected.geometry.parameters.phiStart);
|
||||
phiLength.setValue(this.selected.geometry.parameters.phiLength);
|
||||
thetaStart.setValue(this.selected.geometry.parameters.thetaStart);
|
||||
thetaLength.setValue(this.selected.geometry.parameters.thetaLength);
|
||||
};
|
||||
|
||||
SphereGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var radius = UI.get('radius', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var heightSegments = UI.get('heightSegments', this.id);
|
||||
var phiStart = UI.get('phiStart', this.id);
|
||||
var phiLength = UI.get('phiLength', this.id);
|
||||
var thetaStart = UI.get('thetaStart', this.id);
|
||||
var thetaLength = UI.get('thetaLength', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.SphereBufferGeometry(
|
||||
radius.getValue(),
|
||||
widthSegments.getValue(),
|
||||
heightSegments.getValue(),
|
||||
phiStart.getValue(),
|
||||
phiLength.getValue(),
|
||||
thetaStart.getValue(),
|
||||
thetaLength.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default SphereGeometryComponent;
|
||||
@ -0,0 +1,197 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 茶壶组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function TeapotGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
TeapotGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
TeapotGeometryComponent.prototype.constructor = TeapotGeometryComponent;
|
||||
|
||||
TeapotGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '尺寸'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'size',
|
||||
scope: this.id,
|
||||
value: 3,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'segments',
|
||||
scope: this.id,
|
||||
value: 10,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '壶底'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'bottom',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '壶盖'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'lid',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '壶身'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'body',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '壶盖填满'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'fitLid',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '布林'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'blinn',
|
||||
scope: this.id,
|
||||
value: true,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
TeapotGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TeapotGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TeapotGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.TeapotBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var size = UI.get('size', this.id);
|
||||
var segments = UI.get('segments', this.id);
|
||||
var bottom = UI.get('bottom', this.id);
|
||||
var lid = UI.get('lid', this.id);
|
||||
var body = UI.get('body', this.id);
|
||||
var fitLid = UI.get('fitLid', this.id);
|
||||
var blinn = UI.get('blinn', this.id);
|
||||
|
||||
size.setValue(this.selected.geometry.parameters.size);
|
||||
segments.setValue(this.selected.geometry.parameters.segments);
|
||||
bottom.setValue(this.selected.geometry.parameters.bottom);
|
||||
lid.setValue(this.selected.geometry.parameters.lid);
|
||||
body.setValue(this.selected.geometry.parameters.body);
|
||||
fitLid.setValue(this.selected.geometry.parameters.fitLid);
|
||||
blinn.setValue(this.selected.geometry.parameters.blinn);
|
||||
};
|
||||
|
||||
TeapotGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var size = UI.get('size', this.id);
|
||||
var segments = UI.get('segments', this.id);
|
||||
var bottom = UI.get('bottom', this.id);
|
||||
var lid = UI.get('lid', this.id);
|
||||
var body = UI.get('body', this.id);
|
||||
var fitLid = UI.get('fitLid', this.id);
|
||||
var blinn = UI.get('blinn', this.id);
|
||||
|
||||
var geometry = new THREE.TeapotBufferGeometry(
|
||||
size.getValue(),
|
||||
segments.getValue(),
|
||||
bottom.getValue(),
|
||||
lid.getValue(),
|
||||
body.getValue(),
|
||||
fitLid.getValue(),
|
||||
blinn.getValue()
|
||||
);
|
||||
|
||||
geometry.type = 'TeapotBufferGeometry';
|
||||
|
||||
geometry.parameters = {
|
||||
size: size.getValue(),
|
||||
segments: segments.getValue(),
|
||||
bottom: bottom.getValue(),
|
||||
lid: lid.getValue(),
|
||||
body: body.getValue(),
|
||||
fitLid: fitLid.getValue(),
|
||||
blinn: blinn.getValue()
|
||||
};
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, geometry));
|
||||
};
|
||||
|
||||
export default TeapotGeometryComponent;
|
||||
@ -0,0 +1,149 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 花托组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function TorusGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
TorusGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
TorusGeometryComponent.prototype.constructor = TorusGeometryComponent;
|
||||
|
||||
TorusGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radius',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '管粗'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'tube',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '管粗分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'radialSegments',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'tubularSegments',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '旋转弧度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'arc',
|
||||
scope: this.id,
|
||||
value: Math.PI * 2,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
TorusGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TorusGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TorusGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.TorusBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var radius = UI.get('radius', this.id);
|
||||
var tube = UI.get('tube', this.id);
|
||||
var radialSegments = UI.get('radialSegments', this.id);
|
||||
var tubularSegments = UI.get('tubularSegments', this.id);
|
||||
var arc = UI.get('arc', this.id);
|
||||
|
||||
radius.setValue(this.selected.geometry.parameters.radius);
|
||||
tube.setValue(this.selected.geometry.parameters.tube);
|
||||
radialSegments.setValue(this.selected.geometry.parameters.radialSegments);
|
||||
tubularSegments.setValue(this.selected.geometry.parameters.tubularSegments);
|
||||
arc.setValue(this.selected.geometry.parameters.arc);
|
||||
};
|
||||
|
||||
TorusGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var radius = UI.get('radius', this.id);
|
||||
var tube = UI.get('tube', this.id);
|
||||
var radialSegments = UI.get('radialSegments', this.id);
|
||||
var tubularSegments = UI.get('tubularSegments', this.id);
|
||||
var arc = UI.get('arc', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.TorusBufferGeometry(
|
||||
radius.getValue(),
|
||||
tube.getValue(),
|
||||
radialSegments.getValue(),
|
||||
tubularSegments.getValue(),
|
||||
arc.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default TorusGeometryComponent;
|
||||
@ -0,0 +1,165 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import SetGeometryCommand from '../../command/SetGeometryCommand';
|
||||
|
||||
/**
|
||||
* 环面纽结组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function TorusKnotGeometryComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
TorusKnotGeometryComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
TorusKnotGeometryComponent.prototype.constructor = TorusKnotGeometryComponent;
|
||||
|
||||
TorusKnotGeometryComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'geometryPanel',
|
||||
scope: this.id,
|
||||
style: {
|
||||
borderTop: 0,
|
||||
marginTop: '8px',
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radius',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '管粗'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'tube',
|
||||
scope: this.id,
|
||||
value: 1,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '管长分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'tubularSegments',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '管粗分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'radialSegments',
|
||||
scope: this.id,
|
||||
value: 16,
|
||||
range: [1, Infinity],
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '管长弧度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'p',
|
||||
scope: this.id,
|
||||
value: 20,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '扭曲弧度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'q',
|
||||
scope: this.id,
|
||||
value: 20,
|
||||
onChange: this.onChangeGeometry.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
TorusKnotGeometryComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TorusKnotGeometryComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TorusKnotGeometryComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('geometryPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.geometry instanceof THREE.TorusKnotBufferGeometry) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var radius = UI.get('radius', this.id);
|
||||
var tube = UI.get('tube', this.id);
|
||||
var tubularSegments = UI.get('tubularSegments', this.id);
|
||||
var radialSegments = UI.get('radialSegments', this.id);
|
||||
var p = UI.get('p', this.id);
|
||||
var q = UI.get('q', this.id);
|
||||
|
||||
radius.setValue(this.selected.geometry.parameters.radius);
|
||||
tube.setValue(this.selected.geometry.parameters.tube);
|
||||
tubularSegments.setValue(this.selected.geometry.parameters.tubularSegments);
|
||||
radialSegments.setValue(this.selected.geometry.parameters.radialSegments);
|
||||
p.setValue(this.selected.geometry.parameters.p);
|
||||
q.setValue(this.selected.geometry.parameters.q);
|
||||
};
|
||||
|
||||
TorusKnotGeometryComponent.prototype.onChangeGeometry = function () {
|
||||
var radius = UI.get('radius', this.id);
|
||||
var tube = UI.get('tube', this.id);
|
||||
var tubularSegments = UI.get('tubularSegments', this.id);
|
||||
var radialSegments = UI.get('radialSegments', this.id);
|
||||
var p = UI.get('p', this.id);
|
||||
var q = UI.get('q', this.id);
|
||||
|
||||
this.app.editor.execute(new SetGeometryCommand(this.selected, new THREE.TorusKnotBufferGeometry(
|
||||
radius.getValue(),
|
||||
tube.getValue(),
|
||||
tubularSegments.getValue(),
|
||||
radialSegments.getValue(),
|
||||
p.getValue(),
|
||||
q.getValue()
|
||||
)));
|
||||
};
|
||||
|
||||
export default TorusKnotGeometryComponent;
|
||||
175
ShadowEditor.Core/src/component/object/PerlinTerrainComponent.js
Normal file
175
ShadowEditor.Core/src/component/object/PerlinTerrainComponent.js
Normal file
@ -0,0 +1,175 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import PerlinTerrain from '../../object/terrain/PerlinTerrain';
|
||||
|
||||
/**
|
||||
* 柏林地形组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function PerlinTerrainComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
PerlinTerrainComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
PerlinTerrainComponent.prototype.constructor = PerlinTerrainComponent;
|
||||
|
||||
PerlinTerrainComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'perlinPanel',
|
||||
scope: this.id,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '柏林地形'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'width',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 1000,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '深度'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'depth',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 1000,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'widthSegments',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 256,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '深度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'depthSegments',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 256,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '质量'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'quality',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 80,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
PerlinTerrainComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
PerlinTerrainComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
PerlinTerrainComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('perlinPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof PerlinTerrain) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var width = UI.get('width', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var depthSegments = UI.get('depthSegments', this.id);
|
||||
var quality = UI.get('quality', this.id);
|
||||
|
||||
width.setValue(this.selected.userData.width);
|
||||
depth.setValue(this.selected.userData.depth);
|
||||
widthSegments.setValue(this.selected.userData.widthSegments);
|
||||
depthSegments.setValue(this.selected.userData.depthSegments);
|
||||
quality.setValue(this.selected.userData.quality);
|
||||
};
|
||||
|
||||
PerlinTerrainComponent.prototype.onChange = function () {
|
||||
var width = UI.get('width', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var depthSegments = UI.get('depthSegments', this.id);
|
||||
var quality = UI.get('quality', this.id);
|
||||
|
||||
var terrain = new PerlinTerrain(
|
||||
width.getValue(),
|
||||
depth.getValue(),
|
||||
widthSegments.getValue(),
|
||||
depthSegments.getValue(),
|
||||
quality.getValue()
|
||||
);
|
||||
|
||||
var editor = this.app.editor;
|
||||
|
||||
var index = editor.scene.children.indexOf(this.selected);
|
||||
if (index > -1) {
|
||||
editor.scene.children[index] = terrain;
|
||||
terrain.parent = this.selected.parent;
|
||||
this.selected.parent = null;
|
||||
this.app.call(`objectRemoved`, this, this.selected);
|
||||
this.app.call(`objectAdded`, this, terrain);
|
||||
editor.select(terrain);
|
||||
this.app.call('sceneGraphChanged', this.id);
|
||||
}
|
||||
};
|
||||
|
||||
export default PerlinTerrainComponent;
|
||||
174
ShadowEditor.Core/src/component/object/SkyComponent.js
Normal file
174
ShadowEditor.Core/src/component/object/SkyComponent.js
Normal file
@ -0,0 +1,174 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import Sky from '../../object/component/Sky';
|
||||
|
||||
/**
|
||||
* 天空组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function SkyComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
SkyComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
SkyComponent.prototype.constructor = SkyComponent;
|
||||
|
||||
SkyComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'skyPanel',
|
||||
scope: this.id,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '天空'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '浑浊度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'turbidity',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 10,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '瑞利'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'rayleigh',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 2,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '亮度'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'luminance',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 1,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'Mie系数'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'mieCoefficient',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 0.005,
|
||||
unit: '%',
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: 'Mie方向'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'mieDirectionalG',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 0.005,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
SkyComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SkyComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
SkyComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('skyPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof Sky) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var turbidity = UI.get('turbidity', this.id);
|
||||
var rayleigh = UI.get('rayleigh', this.id);
|
||||
var luminance = UI.get('luminance', this.id);
|
||||
var mieCoefficient = UI.get('mieCoefficient', this.id);
|
||||
var mieDirectionalG = UI.get('mieDirectionalG', this.id);
|
||||
|
||||
turbidity.setValue(this.selected.userData.turbidity);
|
||||
rayleigh.setValue(this.selected.userData.rayleigh);
|
||||
luminance.setValue(this.selected.userData.luminance);
|
||||
mieCoefficient.setValue(this.selected.userData.mieCoefficient * 100);
|
||||
mieDirectionalG.setValue(this.selected.userData.mieDirectionalG);
|
||||
};
|
||||
|
||||
SkyComponent.prototype.onChange = function () {
|
||||
var turbidity = UI.get('turbidity', this.id);
|
||||
var rayleigh = UI.get('rayleigh', this.id);
|
||||
var luminance = UI.get('luminance', this.id);
|
||||
var mieCoefficient = UI.get('mieCoefficient', this.id);
|
||||
var mieDirectionalG = UI.get('mieDirectionalG', this.id);
|
||||
|
||||
this.selected.userData.turbidity = turbidity.getValue();
|
||||
this.selected.userData.rayleigh = rayleigh.getValue();
|
||||
this.selected.userData.luminance = luminance.getValue();
|
||||
this.selected.userData.mieCoefficient = mieCoefficient.getValue() / 100;
|
||||
this.selected.userData.mieDirectionalG = mieDirectionalG.getValue();
|
||||
|
||||
var sky = this.selected.children.filter(n => n instanceof THREE.Sky)[0];
|
||||
if (sky) {
|
||||
var uniforms = sky.material.uniforms;
|
||||
uniforms.turbidity.value = turbidity.getValue();
|
||||
uniforms.rayleigh.value = rayleigh.getValue();
|
||||
uniforms.luminance.value = luminance.getValue();
|
||||
uniforms.mieCoefficient.value = mieCoefficient.getValue() / 100;
|
||||
uniforms.mieDirectionalG.value = mieDirectionalG.getValue();
|
||||
sky.material.needsUpdate = true;
|
||||
}
|
||||
|
||||
this.app.call(`objectSelected`, this, this.selected);
|
||||
};
|
||||
|
||||
export default SkyComponent;
|
||||
140
ShadowEditor.Core/src/component/physics/PhysicsWorldComponent.js
Normal file
140
ShadowEditor.Core/src/component/physics/PhysicsWorldComponent.js
Normal file
@ -0,0 +1,140 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
|
||||
/**
|
||||
* 基本信息组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function PhysicsWorldComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
PhysicsWorldComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
PhysicsWorldComponent.prototype.constructor = PhysicsWorldComponent;
|
||||
|
||||
PhysicsWorldComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'objectPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '物理环境'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '碰撞配置'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'configType',
|
||||
scope: this.id,
|
||||
options: {
|
||||
'btDefaultCollisionConfiguration': '默认碰撞配置', // 无法使用布料
|
||||
'btSoftBodyRigidBodyCollisionConfiguration': '软体刚体碰撞配置'
|
||||
},
|
||||
onChange: this.onChangeConfigType.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '重力常数'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'gravityConstantX',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeGravityConstant.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'gravityConstantY',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeGravityConstant.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'gravityConstantZ',
|
||||
scope: this.id,
|
||||
onChange: this.onChangeGravityConstant.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
PhysicsWorldComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
PhysicsWorldComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
PhysicsWorldComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('objectPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Scene) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = app.physics;
|
||||
|
||||
var configType = UI.get('configType', this.id);
|
||||
var gravityConstantX = UI.get('gravityConstantX', this.id);
|
||||
var gravityConstantY = UI.get('gravityConstantY', this.id);
|
||||
var gravityConstantZ = UI.get('gravityConstantZ', this.id);
|
||||
|
||||
if (this.selected.collisionConfiguration instanceof Ammo.btSoftBodyRigidBodyCollisionConfiguration) {
|
||||
configType.setValue('btSoftBodyRigidBodyCollisionConfiguration');
|
||||
} else {
|
||||
configType.setValue('btDefaultCollisionConfiguration');
|
||||
}
|
||||
|
||||
var gravity = this.selected.world.getGravity();
|
||||
gravityConstantX.setValue(gravity.x());
|
||||
gravityConstantY.setValue(gravity.y());
|
||||
gravityConstantZ.setValue(gravity.z());
|
||||
};
|
||||
|
||||
PhysicsWorldComponent.prototype.onChangeConfigType = function () {
|
||||
var configType = UI.get('configType', this.id);
|
||||
if (configType === 'btSoftBodyRigidBodyCollisionConfiguration') {
|
||||
this.selected.collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration();
|
||||
} else {
|
||||
this.selected.collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
|
||||
}
|
||||
this.selected.dispatcher = new Ammo.btCollisionDispatcher(this.selected.collisionConfiguration);
|
||||
this.selected.world.dispatcher = this.selected.dispatcher;
|
||||
};
|
||||
|
||||
PhysicsWorldComponent.prototype.onChangeGravityConstant = function () {
|
||||
var gravityConstantX = UI.get('gravityConstantX', this.id);
|
||||
var gravityConstantY = UI.get('gravityConstantY', this.id);
|
||||
var gravityConstantZ = UI.get('gravityConstantZ', this.id);
|
||||
|
||||
var gravity = new Ammo.btVector3(gravityConstantX.getValue(), gravityConstantY.getValue(), gravityConstantZ.getValue());
|
||||
this.selected.world.setGravity(gravity);
|
||||
this.selected.world.getWorldInfo().set_m_gravity(gravity);
|
||||
};
|
||||
|
||||
export default PhysicsWorldComponent;
|
||||
159
ShadowEditor.Core/src/component/physics/RigidBodyComponent.js
Normal file
159
ShadowEditor.Core/src/component/physics/RigidBodyComponent.js
Normal file
@ -0,0 +1,159 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
|
||||
/**
|
||||
* 刚体组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function RigidBodyComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
RigidBodyComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
RigidBodyComponent.prototype.constructor = RigidBodyComponent;
|
||||
|
||||
RigidBodyComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'rigidBodyPanel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '刚体'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '形状'
|
||||
}, {
|
||||
xtype: 'input',
|
||||
id: 'shape',
|
||||
scope: this.id,
|
||||
disabled: true,
|
||||
style: {
|
||||
width: '100px',
|
||||
fontSize: '12px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '质量'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'mass',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '100px',
|
||||
fontSize: '12px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '惯性'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'inertiaX',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'inertiaY',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'inertiaZ',
|
||||
scope: this.id,
|
||||
style: {
|
||||
width: '40px'
|
||||
},
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
RigidBodyComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
RigidBodyComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
RigidBodyComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('rigidBodyPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Mesh && editor.selected.userData.physics) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var shape = UI.get('shape', this.id);
|
||||
var mass = UI.get('mass', this.id);
|
||||
var inertiaX = UI.get('inertiaX', this.id);
|
||||
var inertiaY = UI.get('inertiaY', this.id);
|
||||
var inertiaZ = UI.get('inertiaZ', this.id);
|
||||
|
||||
var physics = this.selected.userData.physics;
|
||||
|
||||
shape.setValue(physics.shape);
|
||||
mass.setValue(physics.mass);
|
||||
inertiaX.setValue(physics.inertia.x);
|
||||
inertiaY.setValue(physics.inertia.y);
|
||||
inertiaZ.setValue(physics.inertia.z);
|
||||
};
|
||||
|
||||
RigidBodyComponent.prototype.onChange = function () {
|
||||
var shape = UI.get('shape', this.id);
|
||||
var mass = UI.get('mass', this.id);
|
||||
var inertiaX = UI.get('inertiaX', this.id);
|
||||
var inertiaY = UI.get('inertiaY', this.id);
|
||||
var inertiaZ = UI.get('inertiaZ', this.id);
|
||||
|
||||
var physics = this.selected.userData.physics;
|
||||
|
||||
physics.shape = shape.getValue();
|
||||
physics.mass = mass.getValue();
|
||||
physics.inertia.x = inertiaX.getValue();
|
||||
physics.inertia.y = inertiaY.getValue();
|
||||
physics.inertia.z = inertiaZ.getValue();
|
||||
};
|
||||
|
||||
export default RigidBodyComponent;
|
||||
@ -0,0 +1,5 @@
|
||||
precision mediump float;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
precision mediump float;
|
||||
|
||||
uniform mat4 modelViewMatrix;
|
||||
uniform mat4 projectionMatrix;
|
||||
|
||||
attribute vec3 position;
|
||||
|
||||
void main() {
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
175
ShadowEditor.Core/src/component/water/WaterComponent.js
Normal file
175
ShadowEditor.Core/src/component/water/WaterComponent.js
Normal file
@ -0,0 +1,175 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
import PerlinTerrain from '../../object/terrain/PerlinTerrain';
|
||||
|
||||
/**
|
||||
* 水组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function WaterComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
WaterComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
WaterComponent.prototype.constructor = WaterComponent;
|
||||
|
||||
WaterComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
id: 'perlinPanel',
|
||||
scope: this.id,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
width: '100%',
|
||||
color: '#555',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
text: '柏林地形'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'width',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 1000,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '深度'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'depth',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 1000,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '宽度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'widthSegments',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 256,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '深度分段'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'depthSegments',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 256,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '质量'
|
||||
}, {
|
||||
xtype: 'int',
|
||||
id: 'quality',
|
||||
scope: this.id,
|
||||
range: [0, Infinity],
|
||||
value: 80,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
WaterComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
WaterComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
WaterComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('perlinPanel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof PerlinTerrain) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var width = UI.get('width', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var depthSegments = UI.get('depthSegments', this.id);
|
||||
var quality = UI.get('quality', this.id);
|
||||
|
||||
width.setValue(this.selected.userData.width);
|
||||
depth.setValue(this.selected.userData.depth);
|
||||
widthSegments.setValue(this.selected.userData.widthSegments);
|
||||
depthSegments.setValue(this.selected.userData.depthSegments);
|
||||
quality.setValue(this.selected.userData.quality);
|
||||
};
|
||||
|
||||
WaterComponent.prototype.onChange = function () {
|
||||
var width = UI.get('width', this.id);
|
||||
var depth = UI.get('depth', this.id);
|
||||
var widthSegments = UI.get('widthSegments', this.id);
|
||||
var depthSegments = UI.get('depthSegments', this.id);
|
||||
var quality = UI.get('quality', this.id);
|
||||
|
||||
var terrain = new PerlinTerrain(
|
||||
width.getValue(),
|
||||
depth.getValue(),
|
||||
widthSegments.getValue(),
|
||||
depthSegments.getValue(),
|
||||
quality.getValue()
|
||||
);
|
||||
|
||||
var editor = this.app.editor;
|
||||
|
||||
var index = editor.scene.children.indexOf(this.selected);
|
||||
if (index > -1) {
|
||||
editor.scene.children[index] = terrain;
|
||||
terrain.parent = this.selected.parent;
|
||||
this.selected.parent = null;
|
||||
this.app.call(`objectRemoved`, this, this.selected);
|
||||
this.app.call(`objectAdded`, this, terrain);
|
||||
editor.select(terrain);
|
||||
this.app.call('sceneGraphChanged', this.id);
|
||||
}
|
||||
};
|
||||
|
||||
export default WaterComponent;
|
||||
213
ShadowEditor.Core/src/core/History.js
Normal file
213
ShadowEditor.Core/src/core/History.js
Normal file
@ -0,0 +1,213 @@
|
||||
import Command from '../command/Command';
|
||||
import { UI } from '../third_party';
|
||||
|
||||
/**
|
||||
* 历史记录
|
||||
* @author dforrer / https://github.com/dforrer
|
||||
* Developed as part of a project at University of Applied Sciences and Arts Northwestern Switzerland (www.fhnw.ch)
|
||||
*/
|
||||
function History(editor) {
|
||||
this.app = editor.app;
|
||||
|
||||
this.editor = editor;
|
||||
this.undos = [];
|
||||
this.redos = [];
|
||||
this.lastCmdTime = new Date();
|
||||
this.idCounter = 0;
|
||||
|
||||
Command.call(this, editor);
|
||||
};
|
||||
|
||||
History.prototype = Object.create(Command.prototype);
|
||||
|
||||
Object.assign(History.prototype, {
|
||||
|
||||
constructor: History,
|
||||
|
||||
execute: function (cmd, optionalName) {
|
||||
|
||||
var lastCmd = this.undos[this.undos.length - 1];
|
||||
var timeDifference = new Date().getTime() - this.lastCmdTime.getTime();
|
||||
|
||||
var 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;
|
||||
|
||||
this.lastCmdTime = new Date();
|
||||
|
||||
// clearing all the redo-commands
|
||||
|
||||
this.redos = [];
|
||||
this.app.call('historyChanged', this, cmd);
|
||||
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
var 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.app.call('historyChanged', this, cmd);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
},
|
||||
|
||||
redo: function () {
|
||||
var 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.app.call('historyChanged', this, cmd);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
var history = {};
|
||||
history.undos = [];
|
||||
history.redos = [];
|
||||
|
||||
// Append Undos to History
|
||||
for (var 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 (var i = 0; i < this.redos.length; i++) {
|
||||
if (this.redos[i].hasOwnProperty("json")) {
|
||||
history.redos.push(this.redos[i].json);
|
||||
}
|
||||
}
|
||||
|
||||
return history;
|
||||
},
|
||||
|
||||
fromJSON: function (json) {
|
||||
if (json === undefined) return;
|
||||
|
||||
for (var i = 0; i < json.undos.length; i++) {
|
||||
var cmdJSON = json.undos[i];
|
||||
var 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 (var i = 0; i < json.redos.length; i++) {
|
||||
var cmdJSON = json.redos[i];
|
||||
var 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.app.call('historyChanged', this, this.undos[this.undos.length - 1]);
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this.undos = [];
|
||||
this.redos = [];
|
||||
this.idCounter = 0;
|
||||
|
||||
this.app.call('historyChanged', this);
|
||||
},
|
||||
|
||||
goToState: function (id) {
|
||||
var 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 {
|
||||
while (true) {
|
||||
cmd = this.undos[this.undos.length - 1]; // next cmd to pop
|
||||
if (cmd === undefined || id === cmd.id) break;
|
||||
cmd = this.undo();
|
||||
}
|
||||
}
|
||||
|
||||
this.editor.app.call('sceneGraphChanged', this);
|
||||
this.editor.app.call('historyChanged', this, cmd);
|
||||
},
|
||||
|
||||
enableSerialization: function (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);
|
||||
|
||||
var cmd = this.redo();
|
||||
while (cmd !== undefined) {
|
||||
if (!cmd.hasOwnProperty("json")) {
|
||||
cmd.json = cmd.toJSON();
|
||||
}
|
||||
cmd = this.redo();
|
||||
}
|
||||
|
||||
this.goToState(id);
|
||||
}
|
||||
});
|
||||
|
||||
export default History;
|
||||
21
ShadowEditor.Core/src/core/ServerModelUserData.js
Normal file
21
ShadowEditor.Core/src/core/ServerModelUserData.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 模型对象userData字段保存数据模板
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function UserData() {
|
||||
this.ID = ''; // 模型ID,例如:5b7e38653fb63b0b50d332eb
|
||||
|
||||
this.Name = ''; // 模型名称
|
||||
this.TotalPinYin = ''; // 模型名称全拼
|
||||
this.FirstPinYin = ''; // 模型名称拼音首字母
|
||||
|
||||
this.Type = ''; // 模型类型
|
||||
this.Url = ''; // 模型文件url
|
||||
this.Thumbnail = ''; // 模型缩略图
|
||||
|
||||
this.Server = true; // 是否从服务端加载
|
||||
|
||||
this.CustomData = {}; // 自定义数据
|
||||
}
|
||||
|
||||
export default UserData;
|
||||
75
ShadowEditor.Core/src/core/Storage.js
Normal file
75
ShadowEditor.Core/src/core/Storage.js
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 本地存储
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
function Storage() {
|
||||
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
||||
|
||||
if (indexedDB === undefined) {
|
||||
console.warn('Storage: IndexedDB不可用。');
|
||||
return { init: function () { }, get: function () { }, set: function () { }, clear: function () { } };
|
||||
}
|
||||
|
||||
var name = 'threejs-editor';
|
||||
var version = 1;
|
||||
|
||||
var database;
|
||||
|
||||
return {
|
||||
init: function (callback) {
|
||||
var request = indexedDB.open(name, version);
|
||||
request.onupgradeneeded = function (event) {
|
||||
var 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) {
|
||||
var transaction = database.transaction(['states'], 'readwrite');
|
||||
var objectStore = transaction.objectStore('states');
|
||||
var request = objectStore.get(0);
|
||||
request.onsuccess = function (event) {
|
||||
callback(event.target.result);
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
set: function (data, callback) {
|
||||
var start = performance.now();
|
||||
|
||||
var transaction = database.transaction(['states'], 'readwrite');
|
||||
var objectStore = transaction.objectStore('states');
|
||||
var request = objectStore.put(data, 0);
|
||||
request.onsuccess = function (event) {
|
||||
console.log('[' + /\d\d\:\d\d\:\d\d/.exec(new Date())[0] + ']', '保存到IndexedDB中。 ' + (performance.now() - start).toFixed(2) + 'ms');
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
if (database === undefined) return;
|
||||
|
||||
var transaction = database.transaction(['states'], 'readwrite');
|
||||
var objectStore = transaction.objectStore('states');
|
||||
var request = objectStore.clear();
|
||||
request.onsuccess = function (event) {
|
||||
console.log('[' + /\d\d\:\d\d\:\d\d/.exec(new Date())[0] + ']', '清空IndexedDB。');
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default Storage;
|
||||
9
ShadowEditor.Core/src/core/UserData.js
Normal file
9
ShadowEditor.Core/src/core/UserData.js
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 场景对象自定义数据类型
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function UserData() {
|
||||
type: 'obj'
|
||||
}
|
||||
|
||||
export default UserData;
|
||||
14
ShadowEditor.Core/src/core/userData/AudioUserDataItem.js
Normal file
14
ShadowEditor.Core/src/core/userData/AudioUserDataItem.js
Normal file
@ -0,0 +1,14 @@
|
||||
import UserDataItemBase from './UserDataItemBase';
|
||||
|
||||
function AudioUserDataItem(options) {
|
||||
UserDataItemBase.call(this, options);
|
||||
|
||||
this.type = 'audio';
|
||||
this.name = options.name || '(未知)';
|
||||
this.url = options.url || '';
|
||||
};
|
||||
|
||||
AudioUserDataItem.prototype = Object.create(UserDataItemBase.prototype);
|
||||
AudioUserDataItem.prototype.constructor = AudioUserDataItem;
|
||||
|
||||
export default AudioUserDataItem;
|
||||
8
ShadowEditor.Core/src/core/userData/UserDataItemBase.js
Normal file
8
ShadowEditor.Core/src/core/userData/UserDataItemBase.js
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 用户自定义数据部件基类
|
||||
*/
|
||||
function UserDataItemBase(options) {
|
||||
this.type = options.type || 'unknown';
|
||||
}
|
||||
|
||||
export default UserDataItemBase;
|
||||
396
ShadowEditor.Core/src/editor/Editor.js
Normal file
396
ShadowEditor.Core/src/editor/Editor.js
Normal file
@ -0,0 +1,396 @@
|
||||
import History from '../core/History';
|
||||
import Storage from '../core/Storage';
|
||||
import AnimationManager from '../animation/AnimationManager';
|
||||
|
||||
/**
|
||||
* 编辑器
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function Editor(app) {
|
||||
this.app = app;
|
||||
this.app.editor = this;
|
||||
|
||||
// 基础
|
||||
this.history = new History(this);
|
||||
this.storage = new Storage();
|
||||
|
||||
// 场景
|
||||
this.scene = new THREE.Scene();
|
||||
this.scene.name = '场景';
|
||||
this.scene.background = new THREE.Color(0xaaaaaa);
|
||||
|
||||
this.sceneHelpers = new THREE.Scene();
|
||||
|
||||
this.sceneID = null; // 当前场景ID
|
||||
this.sceneName = null; // 当前场景名称
|
||||
|
||||
// 相机
|
||||
this.DEFAULT_CAMERA = new THREE.PerspectiveCamera(50, 1, 0.1, 10000);
|
||||
this.DEFAULT_CAMERA.name = '默认相机';
|
||||
this.DEFAULT_CAMERA.userData.isDefault = true;
|
||||
this.DEFAULT_CAMERA.position.set(20, 10, 20);
|
||||
this.DEFAULT_CAMERA.lookAt(new THREE.Vector3());
|
||||
|
||||
this.camera = this.DEFAULT_CAMERA.clone();
|
||||
|
||||
// 渲染器
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: true
|
||||
});
|
||||
this.renderer.gammaInput = false;
|
||||
this.renderer.gammaOutput = false;
|
||||
this.renderer.shadowMap.enabled = true;
|
||||
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
this.renderer.autoClear = false;
|
||||
this.renderer.autoUpdateScene = false;
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||
|
||||
this.app.viewport.container.dom.appendChild(this.renderer.domElement);
|
||||
this.renderer.setSize(this.app.viewport.container.dom.offsetWidth, this.app.viewport.container.dom.offsetHeight);
|
||||
|
||||
// 音频监听器
|
||||
this.audioListener = new THREE.AudioListener();
|
||||
this.audioListener.name = '音频监听器';
|
||||
|
||||
// 物体
|
||||
this.object = {};
|
||||
|
||||
// 物体
|
||||
this.objects = [];
|
||||
|
||||
// 脚本 格式:{ uuid: { id: 'mongoDB id', name: 'Script Name', type: 'Script Type', source: 'Source Code', uuid: 'uuid' }}
|
||||
// 其中,uuid是创建脚本时自动生成,不可改变,关联时使用,id是mongo数据库ID字段;name:随便填写;type:javascript,vertexShader, fragmentShader, json;source:源码。
|
||||
this.scripts = {};
|
||||
this.animation = new AnimationManager(this.app);
|
||||
|
||||
// 帮助器
|
||||
this.helpers = {};
|
||||
|
||||
// 当前选中物体
|
||||
this.selected = null;
|
||||
|
||||
// 网格
|
||||
this.grid = new THREE.GridHelper(30, 30, 0x444444, 0x888888);
|
||||
this.grid.visible = this.app.options.showGrid;
|
||||
this.sceneHelpers.add(this.grid);
|
||||
|
||||
// 平移旋转缩放控件
|
||||
this.transformControls = new THREE.TransformControls(this.camera, this.app.viewport.container.dom);
|
||||
this.sceneHelpers.add(this.transformControls);
|
||||
|
||||
// 编辑器控件
|
||||
this.controls = new THREE.EditorControls(this.camera, this.app.viewport.container.dom);
|
||||
|
||||
// 性能控件
|
||||
this.stats = new Stats();
|
||||
this.stats.dom.style.position = 'absolute';
|
||||
this.stats.dom.style.left = '8px';
|
||||
this.stats.dom.style.top = '8px';
|
||||
this.stats.dom.style.zIndex = 'initial';
|
||||
this.app.viewport.container.dom.appendChild(this.stats.dom);
|
||||
|
||||
// 事件
|
||||
this.app.on(`appStarted.${this.id}`, this.onAppStarted.bind(this));
|
||||
this.app.on(`optionsChanged.${this.id}`, this.onOptionsChanged.bind(this));
|
||||
};
|
||||
|
||||
Editor.prototype.onAppStarted = function () {
|
||||
this.clear();
|
||||
};
|
||||
|
||||
// -------------------- 场景 --------------------------
|
||||
|
||||
Editor.prototype.setScene = function (scene) { // 设置场景
|
||||
// 移除原有物体
|
||||
var objects = this.scene.children;
|
||||
while (objects.length > 0) {
|
||||
this.removeObject(objects[0]);
|
||||
}
|
||||
|
||||
// 添加新物体
|
||||
var children = scene.children.slice();
|
||||
scene.children.length = 0;
|
||||
this.scene = scene;
|
||||
|
||||
children.forEach(n => {
|
||||
this.addObject(n);
|
||||
});
|
||||
|
||||
this.app.call('sceneGraphChanged', this);
|
||||
};
|
||||
|
||||
Editor.prototype.clear = function (addObject = true) { // 清空场景
|
||||
this.history.clear();
|
||||
this.storage.clear();
|
||||
|
||||
this.camera.copy(this.DEFAULT_CAMERA);
|
||||
|
||||
if (this.camera.children.findIndex(o => o instanceof THREE.AudioListener) === -1) {
|
||||
this.camera.add(this.audioListener);
|
||||
}
|
||||
|
||||
if (this.scene.background instanceof THREE.Texture) {
|
||||
this.scene.background = new THREE.Color(0xaaaaaa);
|
||||
} else if (this.scene.background instanceof THREE.Color) {
|
||||
this.scene.background.setHex(0xaaaaaa);
|
||||
}
|
||||
|
||||
this.scene.fog = null;
|
||||
|
||||
var objects = this.scene.children;
|
||||
|
||||
while (objects.length > 0) {
|
||||
this.removeObject(objects[0]);
|
||||
}
|
||||
|
||||
this.textures = {};
|
||||
this.scripts = {};
|
||||
this.animation.clear();
|
||||
|
||||
this.deselect();
|
||||
|
||||
// 添加默认元素
|
||||
if (addObject) {
|
||||
var light1 = new THREE.AmbientLight(0xffffff, 0.24);
|
||||
light1.name = '环境光';
|
||||
this.addObject(light1);
|
||||
|
||||
var light2 = new THREE.DirectionalLight(0xffffff, 0.56);
|
||||
light2.name = '平行光';
|
||||
light2.castShadow = true;
|
||||
light2.position.set(5, 10, 7.5);
|
||||
light2.shadow.mapSize.x = 2048;
|
||||
light2.shadow.mapSize.y = 2048;
|
||||
light2.shadow.camera.left = -100;
|
||||
light2.shadow.camera.right = 100;
|
||||
light2.shadow.camera.top = 100;
|
||||
light2.shadow.camera.bottom = -100;
|
||||
this.addObject(light2);
|
||||
}
|
||||
|
||||
this.app.call('editorCleared', this);
|
||||
this.app.call('scriptChanged', this);
|
||||
this.app.call('animationChanged', this);
|
||||
};
|
||||
|
||||
// ---------------------- 物体 ---------------------------
|
||||
|
||||
Editor.prototype.objectByUuid = function (uuid) { // 根据uuid获取物体
|
||||
return this.scene.getObjectByProperty('uuid', uuid, true);
|
||||
};
|
||||
|
||||
Editor.prototype.addObject = function (object) { // 添加物体
|
||||
this.scene.add(object);
|
||||
|
||||
object.traverse(child => {
|
||||
this.addHelper(child);
|
||||
});
|
||||
|
||||
this.app.call('objectAdded', this, object);
|
||||
this.app.call('sceneGraphChanged', this);
|
||||
};
|
||||
|
||||
Editor.prototype.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.app.call('sceneGraphChanged', this);
|
||||
};
|
||||
|
||||
Editor.prototype.removeObject = function (object) { // 移除物体
|
||||
if (object.parent === null) { // 避免删除相机或场景
|
||||
return;
|
||||
}
|
||||
|
||||
object.traverse(child => {
|
||||
this.removeHelper(child);
|
||||
});
|
||||
|
||||
object.parent.remove(object);
|
||||
|
||||
this.app.call('objectRemoved', this, object);
|
||||
this.app.call('sceneGraphChanged', this);
|
||||
};
|
||||
|
||||
// ------------------------- 帮助 ------------------------------
|
||||
|
||||
Editor.prototype.addHelper = function (object) { // 添加物体帮助器
|
||||
var options = this.app.options;
|
||||
|
||||
var helper = null;
|
||||
|
||||
if (object instanceof THREE.Camera) { // 相机
|
||||
helper = new THREE.CameraHelper(object, 1);
|
||||
helper.visible = options.showCameraHelper;
|
||||
} else if (object instanceof THREE.PointLight) { // 点光源
|
||||
helper = new THREE.PointLightHelper(object, 1);
|
||||
helper.visible = options.showPointLightHelper;
|
||||
} else if (object instanceof THREE.DirectionalLight) { // 平行光
|
||||
helper = new THREE.DirectionalLightHelper(object, 1);
|
||||
helper.visible = options.showDirectionalLightHelper;
|
||||
} else if (object instanceof THREE.SpotLight) { // 聚光灯
|
||||
helper = new THREE.SpotLightHelper(object, 1);
|
||||
helper.visible = options.showSpotLightHelper;
|
||||
} else if (object instanceof THREE.HemisphereLight) { // 半球光
|
||||
helper = new THREE.HemisphereLightHelper(object, 1);
|
||||
helper.visible = options.showHemisphereLightHelper;
|
||||
} else if (object instanceof THREE.RectAreaLight) { // 矩形光
|
||||
helper = new THREE.RectAreaLightHelper(object, 0xffffff);
|
||||
helper.visible = options.showRectAreaLightHelper;
|
||||
} else if (object instanceof THREE.SkinnedMesh) { // 骨骼
|
||||
helper = new THREE.SkeletonHelper(object);
|
||||
helper.visible = options.showSkeletonHelper;
|
||||
} else {
|
||||
// 该类型物体没有帮助器
|
||||
return;
|
||||
}
|
||||
|
||||
var geometry = new THREE.SphereBufferGeometry(2, 4, 2);
|
||||
var material = new THREE.MeshBasicMaterial({
|
||||
color: 0xff0000,
|
||||
visible: false
|
||||
});
|
||||
|
||||
var 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.objects.push(picker);
|
||||
};
|
||||
|
||||
Editor.prototype.removeHelper = function (object) { // 移除物体帮助
|
||||
if (this.helpers[object.id] !== undefined) {
|
||||
|
||||
var helper = this.helpers[object.id];
|
||||
helper.parent.remove(helper);
|
||||
delete this.helpers[object.id];
|
||||
|
||||
var objects = this.objects;
|
||||
objects.splice(objects.indexOf(helper.getObjectByName('picker')), 1);
|
||||
}
|
||||
};
|
||||
|
||||
Editor.prototype.onOptionsChanged = function (options) { // 帮助器改变事件
|
||||
this.grid.visible = options.showGrid === undefined ? true : options.showGrid;
|
||||
Object.keys(this.helpers).forEach(n => {
|
||||
var helper = this.helpers[n];
|
||||
if (helper instanceof THREE.CameraHelper) {
|
||||
helper.visible = options.showCameraHelper;
|
||||
} else if (helper instanceof THREE.PointLightHelper) {
|
||||
helper.visible = options.showPointLightHelper;
|
||||
} else if (helper instanceof THREE.DirectionalLightHelper) {
|
||||
helper.visible = options.showDirectionalLightHelper;
|
||||
} else if (helper instanceof THREE.SpotLightHelper) {
|
||||
helper.visible = options.showSpotLightHelper;
|
||||
} else if (helper instanceof THREE.HemisphereLightHelper) {
|
||||
helper.visible = options.showHemisphereLightHelper;
|
||||
} else if (helper instanceof THREE.RectAreaLightHelper) {
|
||||
helper.visible = options.showRectAreaLightHelper;
|
||||
} else if (helper instanceof THREE.SkeletonHelper) {
|
||||
helper.visible = options.showSkeletonHelper;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ------------------------ 脚本 ----------------------------
|
||||
|
||||
Editor.prototype.addScript = function (object, script) { // 添加脚本
|
||||
if (this.scripts[object.uuid] === undefined) {
|
||||
this.scripts[object.uuid] = [];
|
||||
}
|
||||
|
||||
this.scripts[object.uuid].push(script);
|
||||
|
||||
this.app.call('scriptAdded', this, script);
|
||||
};
|
||||
|
||||
Editor.prototype.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.app.call('scriptRemoved', this);
|
||||
};
|
||||
|
||||
// ------------------------ 选中事件 --------------------------------
|
||||
|
||||
Editor.prototype.select = function (object) { // 选中物体
|
||||
if (this.selected === object) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = object;
|
||||
|
||||
this.app.call('objectSelected', this, object);
|
||||
};
|
||||
|
||||
Editor.prototype.selectById = function (id) { // 根据id选中物体
|
||||
if (id === this.camera.id) {
|
||||
this.select(this.camera);
|
||||
return;
|
||||
}
|
||||
|
||||
this.select(this.scene.getObjectById(id, true));
|
||||
};
|
||||
|
||||
Editor.prototype.selectByUuid = function (uuid) { // 根据uuid选中物体
|
||||
var _this = this;
|
||||
this.scene.traverse(function (child) {
|
||||
if (child.uuid === uuid) {
|
||||
_this.select(child);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Editor.prototype.deselect = function () { // 取消选中物体
|
||||
this.select(null);
|
||||
};
|
||||
|
||||
// ---------------------- 焦点事件 --------------------------
|
||||
|
||||
Editor.prototype.focus = function (object) { // 设置焦点
|
||||
this.app.call('objectFocused', this, object);
|
||||
};
|
||||
|
||||
Editor.prototype.focusById = function (id) { // 根据id设置交点
|
||||
var obj = this.scene.getObjectById(id, true);
|
||||
if (obj) {
|
||||
this.focus(obj);
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------- 命令事件 --------------------------
|
||||
|
||||
Editor.prototype.execute = function (cmd, optionalName) { // 执行事件
|
||||
this.history.execute(cmd, optionalName);
|
||||
};
|
||||
|
||||
Editor.prototype.undo = function () { // 撤销事件
|
||||
this.history.undo();
|
||||
};
|
||||
|
||||
Editor.prototype.redo = function () { // 重做事件
|
||||
this.history.redo();
|
||||
};
|
||||
|
||||
export default Editor;
|
||||
171
ShadowEditor.Core/src/editor/Physics.js
Normal file
171
ShadowEditor.Core/src/editor/Physics.js
Normal file
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* 物理
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function Physics(options) {
|
||||
this.app = options.app;
|
||||
this.app.physics = this;
|
||||
|
||||
// 各种参数
|
||||
var gravityConstant = -9.8;
|
||||
this.margin = 0.05;
|
||||
this.transformAux1 = new Ammo.btTransform();
|
||||
|
||||
this.time = 0;
|
||||
this.armMovement = 0;
|
||||
|
||||
// 物理环境配置
|
||||
this.collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration(); // 软体刚体碰撞配置
|
||||
this.dispatcher = new Ammo.btCollisionDispatcher(this.collisionConfiguration); // 碰撞调度器
|
||||
this.broadphase = new Ammo.btDbvtBroadphase(); // dbvt粗测
|
||||
this.solver = new Ammo.btSequentialImpulseConstraintSolver(); // 顺序脉冲约束求解器
|
||||
this.softBodySolver = new Ammo.btDefaultSoftBodySolver(); // 默认软体求解器
|
||||
|
||||
this.world = new Ammo.btSoftRigidDynamicsWorld(this.dispatcher, this.broadphase, this.solver, this.collisionConfiguration, this.softBodySolver);
|
||||
|
||||
var gravity = new Ammo.btVector3(0, gravityConstant, 0);
|
||||
this.world.setGravity(gravity);
|
||||
this.world.getWorldInfo().set_m_gravity(gravity);
|
||||
|
||||
this.rigidBodies = [];
|
||||
|
||||
// 扔球
|
||||
this.enableThrowBall = false;
|
||||
}
|
||||
|
||||
Physics.prototype.start = function () {
|
||||
this.app.on(`animate.Physics`, this.update.bind(this));
|
||||
this.app.on(`mThrowBall.Physics`, this.onEnableThrowBall.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建刚体
|
||||
* @param {*} threeObject three.js中Object3D对象
|
||||
* @param {*} physicsShape 物理形状
|
||||
* @param {*} mass 重力
|
||||
* @param {*} pos 位置
|
||||
* @param {*} quat 旋转
|
||||
*/
|
||||
Physics.prototype.createRigidBody = function (threeObject, physicsShape, mass, pos, quat) {
|
||||
threeObject.position.copy(pos);
|
||||
threeObject.quaternion.copy(quat);
|
||||
|
||||
var transform = new Ammo.btTransform();
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
|
||||
transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));
|
||||
var motionState = new Ammo.btDefaultMotionState(transform);
|
||||
|
||||
// 惯性
|
||||
var localInertia = new Ammo.btVector3(0, 0, 0);
|
||||
physicsShape.calculateLocalInertia(mass, localInertia);
|
||||
|
||||
var rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, physicsShape, localInertia);
|
||||
var body = new Ammo.btRigidBody(rbInfo);
|
||||
|
||||
threeObject.userData.physicsBody = body;
|
||||
|
||||
// 重力大于0才响应物理事件
|
||||
if (mass > 0) {
|
||||
this.rigidBodies.push(threeObject);
|
||||
body.setActivationState(4);
|
||||
}
|
||||
|
||||
this.world.addRigidBody(body);
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建一个平板
|
||||
* @param {*} sx 长度
|
||||
* @param {*} sy 厚度
|
||||
* @param {*} sz 宽度
|
||||
* @param {*} mass 重力
|
||||
* @param {*} pos 位置
|
||||
* @param {*} quat 旋转
|
||||
* @param {*} material 材质
|
||||
*/
|
||||
Physics.prototype.createParalellepiped = function (sx, sy, sz, mass, pos, quat, material) {
|
||||
var threeObject = new THREE.Mesh(new THREE.BoxBufferGeometry(sx, sy, sz, 1, 1, 1), material);
|
||||
var shape = new Ammo.btBoxShape(new Ammo.btVector3(sx * 0.5, sy * 0.5, sz * 0.5));
|
||||
shape.setMargin(this.margin);
|
||||
|
||||
this.createRigidBody(threeObject, shape, mass, pos, quat);
|
||||
|
||||
return threeObject;
|
||||
};
|
||||
|
||||
Physics.prototype.onThrowBall = function (event) {
|
||||
var mouse = new THREE.Vector2();
|
||||
var raycaster = new THREE.Raycaster();
|
||||
var camera = this.app.editor.camera;
|
||||
|
||||
var width = UI.get('viewport').dom.clientWidth;
|
||||
var height = UI.get('viewport').dom.clientHeight;
|
||||
|
||||
mouse.set((event.offsetX / width) * 2 - 1, -(event.offsetY / height) * 2 + 1);
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
// Creates a ball and throws it
|
||||
var ballMass = 35;
|
||||
var ballRadius = 0.4;
|
||||
var ballMaterial = new THREE.MeshPhongMaterial({
|
||||
color: 0x202020
|
||||
});
|
||||
|
||||
var ball = new THREE.Mesh(new THREE.SphereBufferGeometry(ballRadius, 14, 10), ballMaterial);
|
||||
ball.castShadow = true;
|
||||
ball.receiveShadow = true;
|
||||
this.app.editor.scene.add(ball);
|
||||
|
||||
var ballShape = new Ammo.btSphereShape(ballRadius);
|
||||
ballShape.setMargin(this.margin);
|
||||
|
||||
var pos = new THREE.Vector3();
|
||||
pos.copy(raycaster.ray.direction);
|
||||
pos.add(raycaster.ray.origin);
|
||||
|
||||
var quat = new THREE.Quaternion();
|
||||
quat.set(0, 0, 0, 1);
|
||||
|
||||
var ballBody = this.createRigidBody(ball, ballShape, ballMass, pos, quat);
|
||||
|
||||
pos.copy(raycaster.ray.direction);
|
||||
pos.multiplyScalar(24);
|
||||
|
||||
ballBody.setLinearVelocity(new Ammo.btVector3(pos.x, pos.y, pos.z));
|
||||
};
|
||||
|
||||
Physics.prototype.update = function (clock, deltaTime) {
|
||||
this.world.stepSimulation(deltaTime, 10);
|
||||
|
||||
// Update rigid bodies
|
||||
for (var i = 0, il = this.rigidBodies.length; i < il; i++) {
|
||||
var objThree = this.rigidBodies[i];
|
||||
var objPhys = objThree.userData.physicsBody;
|
||||
var ms = objPhys.getMotionState();
|
||||
if (ms) {
|
||||
ms.getWorldTransform(this.transformAux1);
|
||||
var p = this.transformAux1.getOrigin();
|
||||
var q = this.transformAux1.getRotation();
|
||||
objThree.position.set(p.x(), p.y(), p.z());
|
||||
objThree.quaternion.set(q.x(), q.y(), q.z(), q.w());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Physics.prototype.onEnableThrowBall = function () {
|
||||
if (this.enableThrowBall) {
|
||||
this.enableThrowBall = false;
|
||||
UI.get('mThrowBall').dom.innerHTML = '开启探测小球';
|
||||
this.app.on(`click.Physics`, null);
|
||||
} else {
|
||||
this.enableThrowBall = true;
|
||||
UI.get('mThrowBall').dom.innerHTML = '关闭探测小球';
|
||||
this.app.on(`click.Physics`, this.onThrowBall.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
export default Physics;
|
||||
100
ShadowEditor.Core/src/editor/StatusBar.js
Normal file
100
ShadowEditor.Core/src/editor/StatusBar.js
Normal file
@ -0,0 +1,100 @@
|
||||
import { UI } from '../third_party';
|
||||
|
||||
/**
|
||||
* 状态栏
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function StatusBar(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
StatusBar.prototype = Object.create(UI.Control.prototype);
|
||||
StatusBar.prototype.constructor = StatusBar;
|
||||
|
||||
StatusBar.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'statusBar',
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '物体'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'objectsText',
|
||||
scope: this.id,
|
||||
text: '0' // 物体数
|
||||
}, {
|
||||
xtype: 'label',
|
||||
text: '顶点'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'verticesText',
|
||||
scope: this.id,
|
||||
text: '0' // 顶点数
|
||||
}, {
|
||||
xtype: 'label',
|
||||
text: '三角形'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'trianglesText',
|
||||
scope: this.id,
|
||||
text: '0' // 三角形数
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on('objectAdded.' + this.id, this.onUpdateInfo.bind(this));
|
||||
this.app.on('objectRemoved.' + this.id, this.onUpdateInfo.bind(this));
|
||||
this.app.on('geometryChanged.' + this.id, this.onUpdateInfo.bind(this));
|
||||
};
|
||||
|
||||
StatusBar.prototype.onUpdateInfo = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var scene = editor.scene;
|
||||
|
||||
var objects = 0, vertices = 0, triangles = 0;
|
||||
|
||||
for (var i = 0, l = scene.children.length; i < l; i++) {
|
||||
var object = scene.children[i];
|
||||
|
||||
object.traverseVisible(function (object) {
|
||||
objects++;
|
||||
|
||||
if (object instanceof THREE.Mesh) {
|
||||
var geometry = object.geometry;
|
||||
|
||||
if (geometry instanceof THREE.Geometry) {
|
||||
vertices += geometry.vertices.length;
|
||||
triangles += geometry.faces.length;
|
||||
} else if (geometry instanceof THREE.BufferGeometry) {
|
||||
if (geometry.index !== null) {
|
||||
vertices += geometry.index.count * 3;
|
||||
triangles += geometry.index.count;
|
||||
} else {
|
||||
vertices += geometry.attributes.position.count;
|
||||
triangles += geometry.attributes.position.count / 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var objectsText = UI.get('objectsText', this.id);
|
||||
var verticesText = UI.get('verticesText', this.id);
|
||||
var trianglesText = UI.get('trianglesText', this.id);
|
||||
|
||||
objectsText.setValue(objects.format());
|
||||
verticesText.setValue(vertices.format());
|
||||
trianglesText.setValue(triangles.format());
|
||||
};
|
||||
|
||||
export default StatusBar;
|
||||
134
ShadowEditor.Core/src/editor/Toolbar.js
Normal file
134
ShadowEditor.Core/src/editor/Toolbar.js
Normal file
@ -0,0 +1,134 @@
|
||||
import { UI } from '../third_party';
|
||||
import ModelWindow from './window/ModelWindow';
|
||||
|
||||
/**
|
||||
* 工具栏
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function Toolbar(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
Toolbar.prototype = Object.create(UI.Control.prototype);
|
||||
Toolbar.prototype.constructor = Toolbar;
|
||||
|
||||
Toolbar.prototype.render = function () {
|
||||
var _this = this;
|
||||
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'toolbar',
|
||||
children: [{
|
||||
xtype: 'iconbutton',
|
||||
id: 'selectBtn',
|
||||
scope: this.id,
|
||||
icon: 'icon-select',
|
||||
title: '选择',
|
||||
onClick: this.enterSelectMode.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
id: 'translateBtn',
|
||||
scope: this.id,
|
||||
icon: 'icon-translate',
|
||||
cls: 'Button IconButton selected',
|
||||
title: '平移(W)',
|
||||
onClick: this.enterTranslateMode.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
id: 'rotateBtn',
|
||||
scope: this.id,
|
||||
icon: 'icon-rotate',
|
||||
title: '旋转(E)',
|
||||
onClick: this.enterRotateMode.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
id: 'scaleBtn',
|
||||
scope: this.id,
|
||||
icon: 'icon-scale',
|
||||
title: '缩放(R)',
|
||||
onClick: this.enterScaleMode.bind(this)
|
||||
}, {
|
||||
xtype: 'hr'
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
icon: 'icon-model-view',
|
||||
title: '模型',
|
||||
onClick: this.showModelWindow.bind(this)
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`changeMode.${this.id}`, this.onChangeMode.bind(this));
|
||||
};
|
||||
|
||||
// --------------------------------- 选择模式 -------------------------------------
|
||||
|
||||
Toolbar.prototype.enterSelectMode = function () {
|
||||
this.app.call('changeMode', this, 'select');
|
||||
};
|
||||
|
||||
// -------------------------------- 平移模式 --------------------------------------
|
||||
|
||||
Toolbar.prototype.enterTranslateMode = function () {
|
||||
this.app.call('changeMode', this, 'translate');
|
||||
};
|
||||
|
||||
// -------------------------------- 旋转模式 ---------------------------------------
|
||||
|
||||
Toolbar.prototype.enterRotateMode = function () {
|
||||
this.app.call('changeMode', this, 'rotate');
|
||||
};
|
||||
|
||||
// -------------------------------- 缩放模式 ---------------------------------------
|
||||
|
||||
Toolbar.prototype.enterScaleMode = function () {
|
||||
this.app.call('changeMode', this, 'scale');
|
||||
};
|
||||
|
||||
// ------------------------------ 模式改变事件 -------------------------------------
|
||||
|
||||
Toolbar.prototype.onChangeMode = function (mode) {
|
||||
var selectBtn = UI.get('selectBtn', this.id);
|
||||
var translateBtn = UI.get('translateBtn', this.id);
|
||||
var rotateBtn = UI.get('rotateBtn', this.id);
|
||||
var scaleBtn = UI.get('scaleBtn', this.id);
|
||||
|
||||
selectBtn.unselect();
|
||||
translateBtn.unselect();
|
||||
rotateBtn.unselect();
|
||||
scaleBtn.unselect();
|
||||
|
||||
switch (mode) {
|
||||
case 'select':
|
||||
selectBtn.select();
|
||||
break;
|
||||
case 'translate':
|
||||
translateBtn.select();
|
||||
break;
|
||||
case 'rotate':
|
||||
rotateBtn.select();
|
||||
break;
|
||||
case 'scale':
|
||||
scaleBtn.select();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// -------------------------------- 模型窗口 ---------------------------------------
|
||||
|
||||
Toolbar.prototype.showModelWindow = function () {
|
||||
if (this.modelWindow == null) {
|
||||
this.modelWindow = new ModelWindow({
|
||||
parent: this.app.container,
|
||||
app: this.app
|
||||
});
|
||||
this.modelWindow.render();
|
||||
}
|
||||
this.modelWindow.show();
|
||||
};
|
||||
|
||||
export default Toolbar;
|
||||
26
ShadowEditor.Core/src/editor/Viewport.js
Normal file
26
ShadowEditor.Core/src/editor/Viewport.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { UI } from '../third_party';
|
||||
|
||||
/**
|
||||
* 场景编辑区
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function Viewport(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
Viewport.prototype = Object.create(UI.Control.prototype);
|
||||
Viewport.prototype.constructor = Viewport;
|
||||
|
||||
Viewport.prototype.render = function () {
|
||||
this.container = UI.create({
|
||||
xtype: 'div',
|
||||
id: 'viewport',
|
||||
parent: this.parent,
|
||||
cls: 'viewport'
|
||||
});
|
||||
this.container.render();
|
||||
};
|
||||
|
||||
export default Viewport;
|
||||
432
ShadowEditor.Core/src/editor/bottom/AnimationPanel.js
Normal file
432
ShadowEditor.Core/src/editor/bottom/AnimationPanel.js
Normal file
@ -0,0 +1,432 @@
|
||||
import { UI } from '../../third_party';
|
||||
import AnimationGroup from '../../animation/AnimationGroup';
|
||||
import Animation from '../../animation/Animation';
|
||||
|
||||
const STOP = 0;
|
||||
const PLAY = 1;
|
||||
const PAUSE = 2;
|
||||
|
||||
/**
|
||||
* 动画面板
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function AnimationPanel(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
|
||||
this.status = STOP;
|
||||
this.sliderLeft = 0;
|
||||
this.speed = 4;
|
||||
|
||||
this.isDragging = false;
|
||||
};
|
||||
|
||||
AnimationPanel.prototype = Object.create(UI.Control.prototype);
|
||||
AnimationPanel.prototype.constructor = AnimationPanel;
|
||||
|
||||
AnimationPanel.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'animation-panel',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'controls',
|
||||
children: [{
|
||||
xtype: 'iconbutton',
|
||||
icon: 'icon-add',
|
||||
onClick: this.onAddGroup.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
icon: 'icon-delete',
|
||||
onClick: this.onRemoveGroup.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
style: {
|
||||
width: '2px',
|
||||
height: '20px',
|
||||
borderLeft: '1px solid #aaa',
|
||||
borderRight: '1px solid #aaa',
|
||||
boxSizing: 'border-box',
|
||||
margin: '5px 8px'
|
||||
}
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
icon: 'icon-backward',
|
||||
onClick: this.onBackward.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
id: 'btnPlay',
|
||||
scope: this.id,
|
||||
icon: 'icon-play',
|
||||
onClick: this.onPlay.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
id: 'btnPause',
|
||||
scope: this.id,
|
||||
icon: 'icon-pause',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
onClick: this.onPause.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
icon: 'icon-forward',
|
||||
onClick: this.onForward.bind(this)
|
||||
}, {
|
||||
xtype: 'iconbutton',
|
||||
icon: 'icon-stop',
|
||||
onClick: this.onStop.bind(this)
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'time',
|
||||
scope: this.id,
|
||||
style: {
|
||||
marginLeft: '8px',
|
||||
color: '#555',
|
||||
fontSize: '12px'
|
||||
},
|
||||
text: '00:00'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'speed',
|
||||
scope: this.id,
|
||||
style: {
|
||||
marginLeft: '8px',
|
||||
color: '#aaa',
|
||||
fontSize: '12px'
|
||||
},
|
||||
text: 'X 1'
|
||||
}, {
|
||||
xtype: 'toolbarfiller'
|
||||
}, {
|
||||
xtype: 'text',
|
||||
scope: this.id,
|
||||
style: {
|
||||
marginLeft: '8px',
|
||||
color: '#aaa',
|
||||
fontSize: '12px'
|
||||
},
|
||||
text: '说明:双击时间轴下方添加动画。'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'box',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'left-area',
|
||||
id: 'groupInfo',
|
||||
scope: this.id
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'right-area',
|
||||
children: [{
|
||||
xtype: 'timeline',
|
||||
id: 'timeline',
|
||||
cls: 'timeline',
|
||||
scope: this.id
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'groups',
|
||||
id: 'groups',
|
||||
scope: this.id,
|
||||
children: []
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'slider',
|
||||
id: 'slider',
|
||||
scope: this.id
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`appStarted.${this.id}`, this.onAppStarted.bind(this));
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onAppStarted = function () {
|
||||
var timeline = UI.get('timeline', this.id);
|
||||
var groups = UI.get('groups', this.id);
|
||||
|
||||
timeline.updateUI();
|
||||
groups.dom.style.width = timeline.dom.clientWidth + 'px';
|
||||
|
||||
groups.dom.addEventListener(`click`, this.onClick.bind(this));
|
||||
groups.dom.addEventListener(`dblclick`, this.onDblClick.bind(this));
|
||||
groups.dom.addEventListener(`mousedown`, this.onMouseDown.bind(this));
|
||||
groups.dom.addEventListener(`mousemove`, this.onMouseMove.bind(this));
|
||||
document.body.addEventListener(`mouseup`, this.onMouseUp.bind(this));
|
||||
|
||||
this.app.on(`animationChanged.${this.id}`, this.updateUI.bind(this));
|
||||
|
||||
this.app.on(`resetAnimation.${this.id}`, this.onResetAnimation.bind(this));
|
||||
this.app.on(`startAnimation.${this.id}`, this.onPlay.bind(this));
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.updateUI = function () {
|
||||
var animations = this.app.editor.animation.getAnimationGroups();
|
||||
|
||||
var groupInfo = UI.get('groupInfo', this.id);
|
||||
var timeline = UI.get('timeline', this.id);
|
||||
var groups = UI.get('groups', this.id);
|
||||
|
||||
while (groupInfo.dom.children.length) {
|
||||
var child = groupInfo.dom.children[0];
|
||||
groupInfo.dom.removeChild(child);
|
||||
}
|
||||
|
||||
while (groups.dom.children.length) {
|
||||
var child = groups.dom.children[0];
|
||||
child.data = null;
|
||||
groups.dom.removeChild(child);
|
||||
}
|
||||
|
||||
animations.forEach(n => {
|
||||
// 动画组信息区
|
||||
var groupName = document.createElement('div');
|
||||
groupName.className = 'group-info';
|
||||
groupName.innerHTML = `<input type="checkbox" data-uuid="${n.uuid}" />${n.name}`;
|
||||
groupInfo.dom.appendChild(groupName);
|
||||
|
||||
// 动画区
|
||||
var group = document.createElement('div');
|
||||
group.className = 'group';
|
||||
group.setAttribute('droppable', true);
|
||||
group.data = n;
|
||||
group.addEventListener('dragenter', this.onDragEnterGroup.bind(this));
|
||||
group.addEventListener('dragover', this.onDragOverGroup.bind(this));
|
||||
group.addEventListener('dragleave', this.onDragLeaveGroup.bind(this));
|
||||
group.addEventListener('drop', this.onDropGroup.bind(this));
|
||||
groups.dom.appendChild(group);
|
||||
|
||||
n.animations.forEach(m => {
|
||||
var item = document.createElement('div');
|
||||
item.data = m;
|
||||
item.className = 'item';
|
||||
item.setAttribute('draggable', true);
|
||||
item.setAttribute('droppable', false);
|
||||
item.style.left = m.beginTime * timeline.scale + 'px';
|
||||
item.style.width = (m.endTime - m.beginTime) * timeline.scale + 'px';
|
||||
item.innerHTML = m.name;
|
||||
item.addEventListener('dragstart', this.onDragStartAnimation.bind(this));
|
||||
item.addEventListener('dragend', this.onDragEndAnimation.bind(this));
|
||||
group.appendChild(item);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.updateSlider = function () {
|
||||
var timeline = UI.get('timeline', this.id);
|
||||
var slider = UI.get('slider', this.id);
|
||||
var time = UI.get('time', this.id);
|
||||
var speed = UI.get('speed', this.id);
|
||||
|
||||
slider.dom.style.left = this.sliderLeft + 'px';
|
||||
|
||||
var animationTime = this.sliderLeft / timeline.scale;
|
||||
|
||||
var minute = ('0' + Math.floor(animationTime / 60)).slice(-2);
|
||||
var second = ('0' + Math.floor(animationTime % 60)).slice(-2);
|
||||
|
||||
time.setValue(`${minute}:${second}`);
|
||||
|
||||
if (this.speed >= 4) {
|
||||
speed.dom.innerHTML = `X ${this.speed / 4}`;
|
||||
} else {
|
||||
speed.dom.innerHTML = `X 1/${4 / this.speed}`;
|
||||
}
|
||||
|
||||
this.app.call('animationTime', this, animationTime);
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onAnimate = function () {
|
||||
var timeline = UI.get('timeline', this.id);
|
||||
this.sliderLeft += this.speed / 4;
|
||||
|
||||
if (this.sliderLeft >= timeline.dom.clientWidth) {
|
||||
this.sliderLeft = 0;
|
||||
}
|
||||
|
||||
this.updateSlider();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onAddGroup = function () {
|
||||
var group = new AnimationGroup();
|
||||
this.app.editor.animation.add(group);
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onRemoveGroup = function () {
|
||||
var inputs = document.querySelectorAll('.animation-panel .left-area input:checked');
|
||||
|
||||
var uuids = [];
|
||||
inputs.forEach(n => {
|
||||
uuids.push(n.getAttribute('data-uuid'));
|
||||
});
|
||||
|
||||
if (uuids.length === 0) {
|
||||
UI.msg('请勾选需要删除的组!');
|
||||
return;
|
||||
}
|
||||
|
||||
UI.confirm('询问', '删除组会删除组上的所有动画。是否删除?', (event, btn) => {
|
||||
if (btn === 'ok') {
|
||||
uuids.forEach(n => {
|
||||
this.app.editor.animation.removeByUUID(n);
|
||||
});
|
||||
this.updateUI();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ----------------------------------- 播放器事件 -------------------------------------------
|
||||
|
||||
AnimationPanel.prototype.onPlay = function () {
|
||||
if (this.status === PLAY) {
|
||||
return;
|
||||
}
|
||||
this.status = PLAY;
|
||||
|
||||
UI.get('btnPlay', this.id).dom.style.display = 'none';
|
||||
UI.get('btnPause', this.id).dom.style.display = '';
|
||||
|
||||
this.app.on(`animate.${this.id}`, this.onAnimate.bind(this));
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onPause = function () {
|
||||
if (this.status === PAUSE) {
|
||||
return;
|
||||
}
|
||||
this.status = PAUSE;
|
||||
|
||||
UI.get('btnPlay', this.id).dom.style.display = '';
|
||||
UI.get('btnPause', this.id).dom.style.display = 'none';
|
||||
|
||||
this.app.on(`animate.${this.id}`, null);
|
||||
this.updateSlider();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onForward = function () {
|
||||
if (this.speed >= 16) {
|
||||
return;
|
||||
}
|
||||
this.speed *= 2;
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onBackward = function () {
|
||||
if (this.speed <= 1) {
|
||||
return;
|
||||
}
|
||||
this.speed /= 2;
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onStop = function () {
|
||||
if (this.status === STOP) {
|
||||
return;
|
||||
}
|
||||
this.status = STOP;
|
||||
|
||||
UI.get('btnPlay', this.id).dom.style.display = '';
|
||||
UI.get('btnPause', this.id).dom.style.display = 'none';
|
||||
|
||||
this.app.on(`animate.${this.id}`, null);
|
||||
this.sliderLeft = 0;
|
||||
this.updateSlider();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onResetAnimation = function () {
|
||||
this.onStop();
|
||||
this.speed = 4;
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onClick = function (event) {
|
||||
if (event.target.data.type === 'AnimationGroup') {
|
||||
return;
|
||||
}
|
||||
this.app.call('tabSelected', this, 'animation');
|
||||
this.app.call('animationSelected', this, event.target.data);
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onDblClick = function (event) {
|
||||
var timeline = UI.get('timeline', this.id);
|
||||
|
||||
if (event.target.data && event.target.data.type === 'AnimationGroup') {
|
||||
event.stopPropagation();
|
||||
|
||||
var animation = new Animation({
|
||||
beginTime: event.offsetX / timeline.scale,
|
||||
endTime: (event.offsetX + 80) / timeline.scale
|
||||
});
|
||||
|
||||
event.target.data.add(animation);
|
||||
|
||||
this.app.call('animationChanged', this);
|
||||
}
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onMouseDown = function () {
|
||||
if (this.isDragging) {
|
||||
return;
|
||||
}
|
||||
this.isDragging = true;
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onMouseMove = function () {
|
||||
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onMouseUp = function () {
|
||||
this.isDragging = false;
|
||||
};
|
||||
|
||||
// ----------------------- 拖动动画事件 ---------------------------------------------
|
||||
|
||||
AnimationPanel.prototype.onDragStartAnimation = function (event) {
|
||||
event.dataTransfer.setData('uuid', event.target.data.uuid);
|
||||
event.dataTransfer.setData('offsetX', event.offsetX);
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onDragEndAnimation = function (event) {
|
||||
event.dataTransfer.clearData();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onDragEnterGroup = function (event) {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onDragOverGroup = function (event) {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onDragLeaveGroup = function (event) {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
AnimationPanel.prototype.onDropGroup = function (event) {
|
||||
event.preventDefault();
|
||||
var uuid = event.dataTransfer.getData('uuid');
|
||||
var offsetX = event.dataTransfer.getData('offsetX');
|
||||
|
||||
var groups = this.app.editor.animation.getAnimationGroups();
|
||||
var group = groups.filter(n => n.animations.findIndex(m => m.uuid === uuid) > -1)[0];
|
||||
var animation = group.animations.filter(n => n.uuid === uuid)[0];
|
||||
group.remove(animation);
|
||||
|
||||
var timeline = UI.get('timeline', this.id);
|
||||
var length = animation.endTime - animation.beginTime;
|
||||
animation.beginTime = (event.offsetX - offsetX) / timeline.scale;
|
||||
animation.endTime = animation.beginTime + length;
|
||||
|
||||
if (event.target.data instanceof Animation) { // 拖动到其他动画上
|
||||
event.target.parentElement.data.add(animation);
|
||||
} else { // 拖动到动画组上
|
||||
event.target.data.add(animation);
|
||||
}
|
||||
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
export default AnimationPanel;
|
||||
30
ShadowEditor.Core/src/editor/bottom/AudioPanel.js
Normal file
30
ShadowEditor.Core/src/editor/bottom/AudioPanel.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { UI } from '../../third_party';
|
||||
|
||||
/**
|
||||
* 纹理面板
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function AudioPanel(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
AudioPanel.prototype = Object.create(UI.Control.prototype);
|
||||
AudioPanel.prototype.constructor = AudioPanel;
|
||||
|
||||
AudioPanel.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
position: 'relative'
|
||||
},
|
||||
children: []
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
};
|
||||
|
||||
export default AudioPanel;
|
||||
197
ShadowEditor.Core/src/editor/bottom/BottomPanel.js
Normal file
197
ShadowEditor.Core/src/editor/bottom/BottomPanel.js
Normal file
@ -0,0 +1,197 @@
|
||||
import { Control } from '../../third_party';
|
||||
import AnimationPanel from './AnimationPanel';
|
||||
import ModelPanel from './ModelPanel';
|
||||
import TexturePanel from './TexturePanel';
|
||||
import AudioPanel from './AudioPanel';
|
||||
import ParticlePanel from './ParticlePanel';
|
||||
import LogPanel from './LogPanel';
|
||||
|
||||
/**
|
||||
* 底部面板
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function BottomPanel(options) {
|
||||
Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
BottomPanel.prototype = Object.create(Control.prototype);
|
||||
BottomPanel.prototype.constructor = BottomPanel;
|
||||
|
||||
BottomPanel.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
cls: 'sidebar bottomPanel',
|
||||
parent: this.parent,
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'tabs',
|
||||
style: {
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 20
|
||||
},
|
||||
children: [{
|
||||
xtype: 'text',
|
||||
id: 'animationTab',
|
||||
text: '动画',
|
||||
onClick: () => {
|
||||
this.selectTab('animation');
|
||||
}
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'modelTab',
|
||||
text: '模型',
|
||||
onClick: () => {
|
||||
this.selectTab('model');
|
||||
}
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'textureTab',
|
||||
text: '纹理',
|
||||
onClick: () => {
|
||||
this.selectTab('texture');
|
||||
}
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'audioTab',
|
||||
text: '音频',
|
||||
onClick: () => {
|
||||
this.selectTab('audio');
|
||||
}
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'particleTab',
|
||||
text: '粒子',
|
||||
onClick: () => {
|
||||
this.selectTab('particle');
|
||||
}
|
||||
}, {
|
||||
xtype: 'text',
|
||||
id: 'logTab',
|
||||
text: '日志',
|
||||
onClick: () => {
|
||||
this.selectTab('log');
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'animationPanel',
|
||||
style: {
|
||||
flex: 1
|
||||
},
|
||||
children: [
|
||||
new AnimationPanel({ app: this.app })
|
||||
]
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'modelPanel',
|
||||
style: {
|
||||
flex: 1
|
||||
},
|
||||
children: [
|
||||
new ModelPanel({ app: this.app })
|
||||
]
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'texturePanel',
|
||||
style: {
|
||||
flex: 1
|
||||
},
|
||||
children: [
|
||||
new TexturePanel({ app: this.app })
|
||||
]
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'audioPanel',
|
||||
style: {
|
||||
flex: 1
|
||||
},
|
||||
children: [
|
||||
new AudioPanel({ app: this.app })
|
||||
]
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'particlePanel',
|
||||
style: {
|
||||
flex: 1
|
||||
},
|
||||
children: [
|
||||
new ParticlePanel({ app: this.app })
|
||||
]
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'logPanel',
|
||||
children: [
|
||||
new LogPanel({ app: this.app })
|
||||
]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`appStarted.${this.id}`, () => {
|
||||
this.selectTab('animation');
|
||||
});
|
||||
};
|
||||
|
||||
BottomPanel.prototype.selectTab = function (tabName) {
|
||||
var animationTab = UI.get('animationTab');
|
||||
var modelTab = UI.get('modelTab');
|
||||
var textureTab = UI.get('textureTab');
|
||||
var audioTab = UI.get('audioTab');
|
||||
var particleTab = UI.get('particleTab');
|
||||
var logTab = UI.get('logTab');
|
||||
|
||||
var animationPanel = UI.get('animationPanel');
|
||||
var modelPanel = UI.get('modelPanel');
|
||||
var texturePanel = UI.get('texturePanel');
|
||||
var audioPanel = UI.get('audioPanel');
|
||||
var particlePanel = UI.get('particlePanel');
|
||||
var logPanel = UI.get('logPanel');
|
||||
|
||||
animationTab.dom.className = '';
|
||||
modelTab.dom.className = '';
|
||||
textureTab.dom.className = '';
|
||||
audioTab.dom.className = '';
|
||||
particleTab.dom.className = '';
|
||||
logTab.dom.className = '';
|
||||
|
||||
animationPanel.dom.style.display = 'none';
|
||||
modelPanel.dom.style.display = 'none';
|
||||
texturePanel.dom.style.display = 'none';
|
||||
audioPanel.dom.style.display = 'none';
|
||||
particlePanel.dom.style.display = 'none';
|
||||
logPanel.dom.style.display = 'none';
|
||||
|
||||
switch (tabName) {
|
||||
case 'animation':
|
||||
animationTab.dom.className = 'selected';
|
||||
animationPanel.dom.style.display = '';
|
||||
break;
|
||||
case 'model':
|
||||
modelTab.dom.className = 'selected';
|
||||
modelPanel.dom.style.display = '';
|
||||
break;
|
||||
case 'texture':
|
||||
textureTab.dom.className = 'selected';
|
||||
texturePanel.dom.style.display = '';
|
||||
break;
|
||||
case 'audio':
|
||||
audioTab.dom.className = 'selected';
|
||||
audioPanel.dom.style.display = '';
|
||||
break;
|
||||
case 'particle':
|
||||
particleTab.dom.className = 'selected';
|
||||
particlePanel.dom.style.display = '';
|
||||
break;
|
||||
case 'log':
|
||||
logTab.dom.className = 'selected';
|
||||
logPanel.dom.style.display = '';
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export default BottomPanel;
|
||||
85
ShadowEditor.Core/src/editor/bottom/LogPanel.js
Normal file
85
ShadowEditor.Core/src/editor/bottom/LogPanel.js
Normal file
@ -0,0 +1,85 @@
|
||||
import { UI } from '../../third_party';
|
||||
|
||||
/**
|
||||
* 日志面板
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function LogPanel(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
LogPanel.prototype = Object.create(UI.Control.prototype);
|
||||
LogPanel.prototype.constructor = LogPanel;
|
||||
|
||||
LogPanel.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
position: 'relative'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'button',
|
||||
text: '清空',
|
||||
onClick: this.onClearLog.bind(this)
|
||||
}, {
|
||||
xtype: 'br'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
style: {
|
||||
height: '140px',
|
||||
marginTop: '8px',
|
||||
backgroundColor: '#fff',
|
||||
overflowY: 'auto'
|
||||
},
|
||||
id: 'logContent'
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`log.${this.id}`, this.onLog.bind(this));
|
||||
};
|
||||
|
||||
LogPanel.prototype.onLog = function (content, type) {
|
||||
var dom = UI.get('logContent').dom;
|
||||
|
||||
var date = new Date();
|
||||
var hour = date.getHours();
|
||||
var minute = date.getMinutes();
|
||||
var second = date.getSeconds();
|
||||
|
||||
hour = hour < 10 ? '0' + hour : hour;
|
||||
minute = minute < 10 ? '0' + minute : minute;
|
||||
second = second < 10 ? '0' + second : second;
|
||||
|
||||
content = `<span style="font-weight: bold; margin-right: 8px">${hour}:${minute}:${second}</span>${content}`;
|
||||
|
||||
var box = document.createElement('div');
|
||||
box.innerHTML = content;
|
||||
|
||||
if (dom.children.length === 0) {
|
||||
dom.appendChild(box);
|
||||
} else {
|
||||
dom.insertBefore(box, dom.children[0]);
|
||||
}
|
||||
|
||||
if (type === 'warn') {
|
||||
box.style.backgroundColor = '#fffbe5';
|
||||
box.style.color = '#5c3c00';
|
||||
} else if (type === 'error') {
|
||||
box.style.backgroundColor = '#fff0f0';
|
||||
box.style.color = '#ff0000';
|
||||
}
|
||||
};
|
||||
|
||||
LogPanel.prototype.onClearLog = function () {
|
||||
var dom = UI.get('logContent').dom;
|
||||
dom.innerHTML = '';
|
||||
this.onLog('清空日志');
|
||||
};
|
||||
|
||||
export default LogPanel;
|
||||
30
ShadowEditor.Core/src/editor/bottom/ModelPanel.js
Normal file
30
ShadowEditor.Core/src/editor/bottom/ModelPanel.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { UI } from '../../third_party';
|
||||
|
||||
/**
|
||||
* 模型面板
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function ModelPanel(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
ModelPanel.prototype = Object.create(UI.Control.prototype);
|
||||
ModelPanel.prototype.constructor = ModelPanel;
|
||||
|
||||
ModelPanel.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
position: 'relative'
|
||||
},
|
||||
children: []
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
};
|
||||
|
||||
export default ModelPanel;
|
||||
30
ShadowEditor.Core/src/editor/bottom/ParticlePanel.js
Normal file
30
ShadowEditor.Core/src/editor/bottom/ParticlePanel.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { UI } from '../../third_party';
|
||||
|
||||
/**
|
||||
* 粒子面板
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function ParticlePanel(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
ParticlePanel.prototype = Object.create(UI.Control.prototype);
|
||||
ParticlePanel.prototype.constructor = ParticlePanel;
|
||||
|
||||
ParticlePanel.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
position: 'relative'
|
||||
},
|
||||
children: []
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
};
|
||||
|
||||
export default ParticlePanel;
|
||||
30
ShadowEditor.Core/src/editor/bottom/TexturePanel.js
Normal file
30
ShadowEditor.Core/src/editor/bottom/TexturePanel.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { UI } from '../../third_party';
|
||||
|
||||
/**
|
||||
* 纹理面板
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function TexturePanel(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
TexturePanel.prototype = Object.create(UI.Control.prototype);
|
||||
TexturePanel.prototype.constructor = TexturePanel;
|
||||
|
||||
TexturePanel.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
position: 'relative'
|
||||
},
|
||||
children: []
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
};
|
||||
|
||||
export default TexturePanel;
|
||||
119
ShadowEditor.Core/src/editor/control/TextureSelectControl.js
Normal file
119
ShadowEditor.Core/src/editor/control/TextureSelectControl.js
Normal file
@ -0,0 +1,119 @@
|
||||
import { Control } from '../../third_party';
|
||||
import TextureWindow from '../window/TextureWindow';
|
||||
|
||||
/**
|
||||
* 纹理选择控件
|
||||
* @param {*} options
|
||||
*/
|
||||
function TextureSelectControl(options) {
|
||||
Control.call(this, options);
|
||||
|
||||
this.app = options.app;
|
||||
|
||||
this.texture = null;
|
||||
|
||||
this.onChange = options.onChange || null;
|
||||
}
|
||||
|
||||
TextureSelectControl.prototype = Object.create(Control.prototype);
|
||||
TextureSelectControl.prototype.constructor = TextureSelectControl;
|
||||
|
||||
TextureSelectControl.prototype.render = function () {
|
||||
this.dom = document.createElement('div');
|
||||
this.dom.className = 'Texture';
|
||||
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.width = 32;
|
||||
this.canvas.height = 16;
|
||||
this.dom.appendChild(this.canvas);
|
||||
|
||||
this.canvas.addEventListener('click', this.onClick.bind(this));
|
||||
|
||||
this.name = document.createElement('input');
|
||||
this.name.disabled = true;
|
||||
this.dom.appendChild(this.name);
|
||||
|
||||
this.parent.appendChild(this.dom);
|
||||
};
|
||||
|
||||
TextureSelectControl.prototype.updateUI = function () {
|
||||
var canvas = this.dom.children[0];
|
||||
var name = this.dom.children[1];
|
||||
var context = canvas.getContext('2d');
|
||||
|
||||
var texture = this.texture;
|
||||
|
||||
if (texture !== undefined && texture !== null) {
|
||||
var image = texture.image;
|
||||
|
||||
if (image !== undefined && image.width > 0) {
|
||||
name.value = texture.name;
|
||||
|
||||
var scale = canvas.width / image.width;
|
||||
context.drawImage(image, 0, 0, image.width * scale, image.height * scale);
|
||||
} else {
|
||||
name.value = '无图片';
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
} else {
|
||||
name.value = '';
|
||||
|
||||
if (context !== null) {
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TextureSelectControl.prototype.getValue = function () {
|
||||
return this.texture;
|
||||
};
|
||||
|
||||
TextureSelectControl.prototype.setValue = function (texture) {
|
||||
this.texture = texture;
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
TextureSelectControl.prototype.onClick = function () {
|
||||
if (this.window === undefined) {
|
||||
this.window = new TextureWindow({
|
||||
app: this.app,
|
||||
onSelect: this.onSelect.bind(this)
|
||||
});
|
||||
this.window.render();
|
||||
}
|
||||
this.window.show();
|
||||
};
|
||||
|
||||
TextureSelectControl.prototype.onSelect = function (data) {
|
||||
var urls = data.Url.split(';'); // 立体贴图data.Url多于一张,只取第一个。
|
||||
|
||||
if (data.Type === 'video') { // 视频贴图
|
||||
var video = document.createElement('video');
|
||||
video.src = `${this.app.options.server}${urls[0]}`;
|
||||
video.loop = 'loop';
|
||||
video.autoplay = 'autoplay';
|
||||
video.onplay = () => {
|
||||
var texture = new THREE.VideoTexture(video);
|
||||
texture.minFilter = THREE.LinearFilter;
|
||||
texture.magFilter = THREE.LinearFilter;
|
||||
texture.format = THREE.RGBFormat;
|
||||
this.texture = texture;
|
||||
this.texture.name = data.Name;
|
||||
this.window.hide();
|
||||
this.updateUI();
|
||||
this.onChange();
|
||||
}
|
||||
} else { // 其他
|
||||
var loader = new THREE.TextureLoader();
|
||||
loader.load(`${this.app.options.server}${urls[0]}`, texture => {
|
||||
this.texture = texture;
|
||||
this.texture.name = data.Name;
|
||||
this.window.hide();
|
||||
this.updateUI();
|
||||
this.onChange();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default TextureSelectControl;
|
||||
282
ShadowEditor.Core/src/editor/menubar/AssetMenu.js
Normal file
282
ShadowEditor.Core/src/editor/menubar/AssetMenu.js
Normal file
@ -0,0 +1,282 @@
|
||||
import { UI } from '../../third_party';
|
||||
import ModelWindow from '../window/ModelWindow';
|
||||
import TextureWindow from '../window/TextureWindow';
|
||||
import AudioWindow from '../window/AudioWindow';
|
||||
import MMDWindow from '../window/MMDWindow';
|
||||
import StringUtils from '../../utils/StringUtils';
|
||||
|
||||
/**
|
||||
* 资源菜单
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function AssetMenu(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
}
|
||||
|
||||
AssetMenu.prototype = Object.create(UI.Control.prototype);
|
||||
AssetMenu.prototype.constructor = AssetMenu;
|
||||
|
||||
AssetMenu.prototype.render = function () {
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'menu',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'title',
|
||||
html: '资源'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'options',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
html: '模型管理',
|
||||
cls: 'option',
|
||||
onClick: this.onManageModel.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '纹理管理',
|
||||
cls: 'option',
|
||||
onClick: this.onManageTexture.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '音频管理',
|
||||
cls: 'option',
|
||||
onClick: this.onManageAudio.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: 'MMD资源管理',
|
||||
cls: 'option',
|
||||
onClick: this.onManageMMD.bind(this)
|
||||
}, {
|
||||
xtype: 'hr'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '导出几何体',
|
||||
cls: 'option',
|
||||
onClick: this.onExportGeometry.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '导出物体',
|
||||
cls: 'option',
|
||||
onClick: this.onExportObject.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '导出场景',
|
||||
cls: 'option',
|
||||
onClick: this.onExportScene.bind(this)
|
||||
}, {
|
||||
xtype: 'hr'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '导出gltf文件',
|
||||
cls: 'option',
|
||||
onClick: this.onExportGLTF.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '导出obj文件',
|
||||
cls: 'option',
|
||||
onClick: this.onExportOBJ.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '导出ply文件',
|
||||
cls: 'option',
|
||||
onClick: this.onExportPLY.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'mExportSTLB',
|
||||
html: '导出stl二进制文件',
|
||||
cls: 'option',
|
||||
onClick: this.onExportSTLB.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'mExportSTL',
|
||||
html: '导出stl文件',
|
||||
cls: 'option',
|
||||
onClick: this.onExportSTL.bind(this)
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
container.render();
|
||||
}
|
||||
|
||||
// --------------------------------- 模型管理 --------------------------------------
|
||||
|
||||
AssetMenu.prototype.onManageModel = function () {
|
||||
if (this.modelWindow == null) {
|
||||
this.modelWindow = new ModelWindow({ parent: this.app.container, app: this.app });
|
||||
this.modelWindow.render();
|
||||
}
|
||||
this.modelWindow.show();
|
||||
};
|
||||
|
||||
// --------------------------------- 纹理管理 --------------------------------------
|
||||
|
||||
AssetMenu.prototype.onManageTexture = function () {
|
||||
if (this.textureWindow == null) {
|
||||
this.textureWindow = new TextureWindow({ parent: this.app.container, app: this.app });
|
||||
this.textureWindow.render();
|
||||
}
|
||||
this.textureWindow.show();
|
||||
};
|
||||
|
||||
// --------------------------------- 音频管理 --------------------------------------
|
||||
|
||||
AssetMenu.prototype.onManageAudio = function () {
|
||||
if (this.audioWindow == null) {
|
||||
this.audioWindow = new AudioWindow({ parent: this.app.container, app: this.app });
|
||||
this.audioWindow.render();
|
||||
}
|
||||
this.audioWindow.show();
|
||||
};
|
||||
|
||||
// ------------------------------- MMD资源管理 --------------------------------------
|
||||
|
||||
AssetMenu.prototype.onManageMMD = function () {
|
||||
if (this.mmdWindow == null) {
|
||||
this.mmdWindow = new MMDWindow({ parent: this.app.container, app: this.app });
|
||||
this.mmdWindow.render();
|
||||
}
|
||||
this.mmdWindow.show();
|
||||
};
|
||||
|
||||
// ------------------------------- 导出几何体 ----------------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportGeometry = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var object = editor.selected;
|
||||
|
||||
if (object === null) {
|
||||
UI.msg('请选择物体');
|
||||
return;
|
||||
}
|
||||
|
||||
var geometry = object.geometry;
|
||||
|
||||
if (geometry === undefined) {
|
||||
UI.msg('选中的对象不具有Geometry属性。');
|
||||
return;
|
||||
}
|
||||
|
||||
var output = geometry.toJSON();
|
||||
|
||||
try {
|
||||
output = JSON.stringify(output, parseNumber, '\t');
|
||||
output = output.replace(/[\n\t]+([\d\.e\-\[\]]+)/g, '$1');
|
||||
} catch (e) {
|
||||
output = JSON.stringify(output);
|
||||
}
|
||||
|
||||
StringUtils.saveString(output, 'geometry.json');
|
||||
};
|
||||
|
||||
// ------------------------------- 导出物体 ------------------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportObject = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var object = editor.selected;
|
||||
|
||||
if (object === null) {
|
||||
UI.msg('请选择对象');
|
||||
return;
|
||||
}
|
||||
|
||||
var output = object.toJSON();
|
||||
|
||||
try {
|
||||
output = JSON.stringify(output, parseNumber, '\t');
|
||||
output = output.replace(/[\n\t]+([\d\.e\-\[\]]+)/g, '$1');
|
||||
} catch (e) {
|
||||
output = JSON.stringify(output);
|
||||
}
|
||||
|
||||
StringUtils.saveString(output, 'model.json');
|
||||
};
|
||||
|
||||
// ------------------------------- 导出场景 ------------------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportScene = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var output = editor.scene.toJSON();
|
||||
|
||||
try {
|
||||
output = JSON.stringify(output, parseNumber, '\t');
|
||||
output = output.replace(/[\n\t]+([\d\.e\-\[\]]+)/g, '$1');
|
||||
} catch (e) {
|
||||
output = JSON.stringify(output);
|
||||
}
|
||||
|
||||
StringUtils.saveString(output, 'scene.json');
|
||||
};
|
||||
|
||||
// ------------------------------ 导出gltf文件 ----------------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportGLTF = function () {
|
||||
var exporter = new THREE.GLTFExporter();
|
||||
|
||||
exporter.parse(app.editor.scene, function (result) {
|
||||
StringUtils.saveString(JSON.stringify(result), 'model.gltf');
|
||||
});
|
||||
};
|
||||
|
||||
// ------------------------------ 导出obj文件 -----------------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportOBJ = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var object = editor.selected;
|
||||
|
||||
if (object === null) {
|
||||
UI.msg('请选择对象');
|
||||
return;
|
||||
}
|
||||
|
||||
var exporter = new THREE.OBJExporter();
|
||||
StringUtils.saveString(exporter.parse(object), 'model.obj');
|
||||
};
|
||||
|
||||
// ------------------------------- 导出ply文件 ----------------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportPLY = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var object = editor.selected;
|
||||
|
||||
if (object === null) {
|
||||
UI.msg('请选择对象');
|
||||
return;
|
||||
}
|
||||
|
||||
var exporter = new THREE.PLYExporter();
|
||||
StringUtils.saveString(exporter.parse(object, {
|
||||
excludeAttributes: ['normal', 'uv', 'color', 'index']
|
||||
}), 'model.ply');
|
||||
};
|
||||
|
||||
// ------------------------------- 导出stl二进制文件 -----------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportSTLB = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var exporter = new THREE.STLBinaryExporter();
|
||||
|
||||
StringUtils.saveString(exporter.parse(editor.scene), 'model.stl');
|
||||
};
|
||||
|
||||
// ------------------------------- 导出stl文件 -----------------------------------------
|
||||
|
||||
AssetMenu.prototype.onExportSTL = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var exporter = new THREE.STLExporter();
|
||||
|
||||
StringUtils.saveString(exporter.parse(editor.scene), 'model.stl');
|
||||
};
|
||||
|
||||
export default AssetMenu;
|
||||
205
ShadowEditor.Core/src/editor/menubar/ComponentMenu.js
Normal file
205
ShadowEditor.Core/src/editor/menubar/ComponentMenu.js
Normal file
@ -0,0 +1,205 @@
|
||||
import { UI } from '../../third_party';
|
||||
import AddObjectCommand from '../../command/AddObjectCommand';
|
||||
import Sky from '../../object/component/Sky';
|
||||
import Fire from '../../object/component/Fire';
|
||||
import Water from '../../object/water/Water';
|
||||
import Smoke from '../../object/component/Smoke';
|
||||
import ParticleEmitter from '../../object/component/ParticleEmitter';
|
||||
import PlysicsUtils from '../../physics/PlysicsUtils';
|
||||
|
||||
/**
|
||||
* 组件菜单
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function ComponentMenu(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
}
|
||||
|
||||
ComponentMenu.prototype = Object.create(UI.Control.prototype);
|
||||
ComponentMenu.prototype.constructor = ComponentMenu;
|
||||
|
||||
ComponentMenu.prototype.render = function () {
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'menu',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'title',
|
||||
html: '组件'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'options',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
html: '背景音乐',
|
||||
cls: 'option',
|
||||
onClick: this.onAddBackgroundMusic.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '粒子发射器',
|
||||
cls: 'option',
|
||||
onClick: this.ParticleEmitter.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '天空',
|
||||
cls: 'option',
|
||||
onClick: this.onAddSky.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '火焰',
|
||||
cls: 'option',
|
||||
onClick: this.onAddFire.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '液体',
|
||||
cls: 'option',
|
||||
onClick: this.onAddWater.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '烟',
|
||||
cls: 'option',
|
||||
onClick: this.onAddSmoke.bind(this)
|
||||
}, {
|
||||
xtype: 'hr'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '刚体',
|
||||
cls: 'option',
|
||||
onClick: this.addRigidBody.bind(this)
|
||||
}, {
|
||||
xtype: 'hr'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '曲线编辑器',
|
||||
cls: 'option',
|
||||
onClick: this.curveEditor.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '发型编辑器',
|
||||
cls: 'option',
|
||||
onClick: this.hairEditor.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '服装编辑器',
|
||||
cls: 'option',
|
||||
onClick: this.clothingEditor.bind(this)
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
container.render();
|
||||
}
|
||||
|
||||
// ---------------------------- 添加背景音乐 ----------------------------------
|
||||
|
||||
ComponentMenu.prototype.onAddBackgroundMusic = function () {
|
||||
var editor = this.app.editor;
|
||||
var listener = editor.audioListener;
|
||||
|
||||
var audio = new THREE.Audio(listener);
|
||||
audio.name = `背景音乐`;
|
||||
audio.autoplay = false;
|
||||
audio.setLoop(true);
|
||||
audio.setVolume(1.0);
|
||||
|
||||
audio.userData.autoplay = true;
|
||||
|
||||
this.app.editor.execute(new AddObjectCommand(audio));
|
||||
};
|
||||
|
||||
// ---------------------------- 添加粒子发射器 --------------------------------------------
|
||||
|
||||
ComponentMenu.prototype.ParticleEmitter = function () {
|
||||
var emitter = new ParticleEmitter();
|
||||
this.app.editor.execute(new AddObjectCommand(emitter));
|
||||
emitter.userData.group.tick(0);
|
||||
};
|
||||
|
||||
// ---------------------------- 天空 ----------------------------------------
|
||||
|
||||
ComponentMenu.prototype.onAddSky = function () {
|
||||
var obj = new Sky();
|
||||
obj.name = '天空';
|
||||
obj.userData.type = 'Sky';
|
||||
this.app.editor.execute(new AddObjectCommand(obj));
|
||||
};
|
||||
|
||||
// ---------------------------- 添加火焰 -------------------------------------
|
||||
|
||||
ComponentMenu.prototype.onAddFire = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var fire = new Fire(editor.camera);
|
||||
|
||||
editor.execute(new AddObjectCommand(fire));
|
||||
|
||||
fire.userData.fire.update(0);
|
||||
};
|
||||
|
||||
// -------------------------- 添加水 ---------------------------------------
|
||||
|
||||
ComponentMenu.prototype.onAddWater = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var water = new Water(editor.renderer);
|
||||
|
||||
editor.execute(new AddObjectCommand(water));
|
||||
|
||||
this.app.on(`animate.${this.id}`, () => {
|
||||
water.update();
|
||||
});
|
||||
};
|
||||
|
||||
// ------------------------------ 添加烟 ------------------------------------
|
||||
|
||||
ComponentMenu.prototype.onAddSmoke = function () {
|
||||
var editor = this.app.editor;
|
||||
var camera = editor.camera;
|
||||
var renderer = editor.renderer;
|
||||
|
||||
var smoke = new Smoke(camera, renderer);
|
||||
|
||||
smoke.position.y = 3;
|
||||
|
||||
editor.execute(new AddObjectCommand(smoke));
|
||||
|
||||
smoke.update(0);
|
||||
};
|
||||
|
||||
// --------------------------- 添加刚体 ------------------------------------
|
||||
|
||||
ComponentMenu.prototype.addRigidBody = function () {
|
||||
var selected = this.app.editor.selected;
|
||||
if (!selected) {
|
||||
UI.msg('请选择几何体!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlysicsUtils.addRigidBodyData(selected) !== false) {
|
||||
this.app.call('objectChanged', this, selected);
|
||||
UI.msg('添加刚体组件成功!');
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------- 曲线编辑器 -------------------------------------
|
||||
|
||||
ComponentMenu.prototype.curveEditor = function () {
|
||||
UI.msg('待开发');
|
||||
};
|
||||
|
||||
// --------------------------- 发型编辑器 --------------------------------------
|
||||
|
||||
ComponentMenu.prototype.hairEditor = function () {
|
||||
UI.msg('待开发');
|
||||
};
|
||||
|
||||
// --------------------------- 服装编辑器 --------------------------------------
|
||||
|
||||
ComponentMenu.prototype.clothingEditor = function () {
|
||||
UI.msg('待开发');
|
||||
};
|
||||
|
||||
export default ComponentMenu;
|
||||
192
ShadowEditor.Core/src/editor/menubar/EditMenu.js
Normal file
192
ShadowEditor.Core/src/editor/menubar/EditMenu.js
Normal file
@ -0,0 +1,192 @@
|
||||
import { UI } from '../../third_party';
|
||||
import AddObjectCommand from '../../command/AddObjectCommand';
|
||||
import RemoveObjectCommand from '../../command/RemoveObjectCommand';
|
||||
|
||||
/**
|
||||
* 编辑菜单
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function EditMenu(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
}
|
||||
|
||||
EditMenu.prototype = Object.create(UI.Control.prototype);
|
||||
EditMenu.prototype.constructor = EditMenu;
|
||||
|
||||
EditMenu.prototype.render = function () {
|
||||
var _this = this;
|
||||
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'menu',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'title',
|
||||
html: '编辑'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'options',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
id: 'undo',
|
||||
scope: this.id,
|
||||
html: '撤销(Ctrl+Z)',
|
||||
cls: 'option inactive',
|
||||
onClick: this.undo.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'redo',
|
||||
scope: this.id,
|
||||
html: '重做(Ctrl+Shift+Z)',
|
||||
cls: 'option inactive',
|
||||
onClick: this.redo.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'clearHistory',
|
||||
scope: this.id,
|
||||
html: '清空历史记录',
|
||||
cls: 'option inactive',
|
||||
onClick: this.clearHistory.bind(this)
|
||||
}, {
|
||||
xtype: 'hr'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'clone',
|
||||
scope: this.id,
|
||||
html: '复制',
|
||||
cls: 'option inactive',
|
||||
onClick: this.clone.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'delete',
|
||||
scope: this.id,
|
||||
html: '删除(Del)',
|
||||
cls: 'option inactive',
|
||||
onClick: this.delete.bind(this)
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
container.render();
|
||||
|
||||
this.app.on(`historyChanged.${this.id}`, this.onHistoryChanged.bind(this));
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
}
|
||||
|
||||
// --------------------- 撤销 --------------------------
|
||||
|
||||
EditMenu.prototype.undo = function () {
|
||||
var history = this.app.editor.history;
|
||||
if (history.undos.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.app.editor.undo();
|
||||
};
|
||||
|
||||
// --------------------- 重做 -----------------------------
|
||||
|
||||
EditMenu.prototype.redo = function () {
|
||||
var history = this.app.editor.history;
|
||||
if (history.redos.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.app.editor.redo();
|
||||
};
|
||||
|
||||
// -------------------- 清空历史记录 --------------------------------
|
||||
|
||||
EditMenu.prototype.clearHistory = function () {
|
||||
var editor = this.app.editor;
|
||||
var history = editor.history;
|
||||
|
||||
if (history.undos.length === 0 && history.redos.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
UI.confirm('询问', '撤销/重做历史记录将被清空。确定吗?', function (event, btn) {
|
||||
if (btn === 'ok') {
|
||||
editor.history.clear();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// -------------------------- 复制 -----------------------------------
|
||||
|
||||
EditMenu.prototype.clone = function () {
|
||||
var editor = this.app.editor;
|
||||
var object = editor.selected;
|
||||
|
||||
if (object == null || object.parent == null) { // 避免复制场景或相机
|
||||
return;
|
||||
}
|
||||
|
||||
object = object.clone();
|
||||
editor.execute(new AddObjectCommand(object));
|
||||
};
|
||||
|
||||
// ----------------------- 删除 -----------------------------------
|
||||
|
||||
EditMenu.prototype.delete = function () {
|
||||
var editor = this.app.editor;
|
||||
var object = editor.selected;
|
||||
|
||||
if (object == null || object.parent == null) { // 避免删除场景或相机
|
||||
return;
|
||||
}
|
||||
|
||||
UI.confirm('询问', '删除 ' + object.name + '?', function (event, btn) {
|
||||
if (btn === 'ok') {
|
||||
editor.execute(new RemoveObjectCommand(object));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ---------------------- 事件 -----------------------
|
||||
|
||||
EditMenu.prototype.onHistoryChanged = function () {
|
||||
var history = this.app.editor.history;
|
||||
|
||||
var undo = UI.get('undo', this.id);
|
||||
var redo = UI.get('redo', this.id);
|
||||
var clearHistory = UI.get('clearHistory', this.id);
|
||||
|
||||
if (history.undos.length === 0) {
|
||||
undo.dom.classList.add('inactive');
|
||||
} else {
|
||||
undo.dom.classList.remove('inactive');
|
||||
}
|
||||
|
||||
if (history.redos.length === 0) {
|
||||
redo.dom.classList.add('inactive');
|
||||
} else {
|
||||
redo.dom.classList.remove('inactive');
|
||||
}
|
||||
|
||||
if (history.undos.length === 0 && history.redos.length === 0) {
|
||||
clearHistory.dom.classList.add('inactive');
|
||||
} else {
|
||||
clearHistory.dom.classList.remove('inactive');
|
||||
}
|
||||
};
|
||||
|
||||
EditMenu.prototype.onObjectSelected = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var clone = UI.get('clone', this.id);
|
||||
var deleteBtn = UI.get('delete', this.id);
|
||||
|
||||
if (editor.selected && editor.selected.parent != null) {
|
||||
clone.dom.classList.remove('inactive');
|
||||
deleteBtn.dom.classList.remove('inactive');
|
||||
} else {
|
||||
clone.dom.classList.add('inactive');
|
||||
deleteBtn.dom.classList.add('inactive');
|
||||
}
|
||||
};
|
||||
|
||||
export default EditMenu;
|
||||
198
ShadowEditor.Core/src/editor/menubar/GeometryMenu.js
Normal file
198
ShadowEditor.Core/src/editor/menubar/GeometryMenu.js
Normal file
@ -0,0 +1,198 @@
|
||||
import { UI } from '../../third_party';
|
||||
import AddObjectCommand from '../../command/AddObjectCommand';
|
||||
|
||||
import Group from '../../object/geometry/Group';
|
||||
import Plane from '../../object/geometry/Plane';
|
||||
import Box from '../../object/geometry/Box';
|
||||
import Circle from '../../object/geometry/Circle';
|
||||
import Cylinder from '../../object/geometry/Cylinder';
|
||||
import Sphere from '../../object/geometry/Sphere';
|
||||
import Icosahedron from '../../object/geometry/Icosahedron';
|
||||
import Torus from '../../object/geometry/Torus';
|
||||
import TorusKnot from '../../object/geometry/TorusKnot';
|
||||
import Teapot from '../../object/geometry/Teapot';
|
||||
import Lathe from '../../object/geometry/Lathe';
|
||||
import Sprite from '../../object/geometry/Sprite';
|
||||
import Text from '../../object/geometry/Text';
|
||||
|
||||
/**
|
||||
* 几何体菜单
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function GeometryMenu(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
}
|
||||
|
||||
GeometryMenu.prototype = Object.create(UI.Control.prototype);
|
||||
GeometryMenu.prototype.constructor = GeometryMenu;
|
||||
|
||||
GeometryMenu.prototype.render = function () {
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'menu',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'title',
|
||||
html: '几何体'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'options',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
html: '组',
|
||||
cls: 'option',
|
||||
onClick: this.addGroup.bind(this)
|
||||
}, {
|
||||
xtype: 'hr'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '平板',
|
||||
cls: 'option',
|
||||
onClick: this.addPlane.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '正方体',
|
||||
cls: 'option',
|
||||
onClick: this.addBox.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '圆',
|
||||
cls: 'option',
|
||||
onClick: this.addCircle.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '圆柱体',
|
||||
cls: 'option',
|
||||
onClick: this.addCylinder.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '球体',
|
||||
cls: 'option',
|
||||
onClick: this.addSphere.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '二十面体',
|
||||
cls: 'option',
|
||||
onClick: this.addIcosahedron.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '轮胎',
|
||||
cls: 'option',
|
||||
onClick: this.addTorus.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '扭结',
|
||||
cls: 'option',
|
||||
onClick: this.addTorusKnot.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '茶壶',
|
||||
cls: 'option',
|
||||
onClick: this.addTeaport.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '酒杯',
|
||||
cls: 'option',
|
||||
onClick: this.addLathe.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
id: 'mAddSprite',
|
||||
html: '精灵',
|
||||
cls: 'option',
|
||||
onClick: this.addSprite.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '文本',
|
||||
cls: 'option',
|
||||
onClick: this.addText.bind(this)
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
container.render();
|
||||
}
|
||||
|
||||
// ------------------------- 组 ---------------------------------
|
||||
|
||||
GeometryMenu.prototype.addGroup = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Group()));
|
||||
};
|
||||
|
||||
// ------------------------- 平板 -------------------------------
|
||||
|
||||
GeometryMenu.prototype.addPlane = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Plane()));
|
||||
};
|
||||
|
||||
// ------------------------ 正方体 -----------------------------
|
||||
|
||||
GeometryMenu.prototype.addBox = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Box()));
|
||||
};
|
||||
|
||||
// ------------------------ 圆 ----------------------------------
|
||||
|
||||
GeometryMenu.prototype.addCircle = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Circle()));
|
||||
};
|
||||
|
||||
// ------------------------圆柱体 -------------------------------
|
||||
|
||||
GeometryMenu.prototype.addCylinder = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Cylinder()));
|
||||
};
|
||||
|
||||
// ------------------------ 球体 -------------------------------
|
||||
|
||||
GeometryMenu.prototype.addSphere = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Sphere()));
|
||||
};
|
||||
|
||||
// ----------------------- 二十面体 -----------------------------
|
||||
|
||||
GeometryMenu.prototype.addIcosahedron = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Icosahedron()));
|
||||
};
|
||||
|
||||
// ----------------------- 轮胎 ---------------------------------
|
||||
|
||||
GeometryMenu.prototype.addTorus = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Torus()));
|
||||
};
|
||||
|
||||
// ----------------------- 纽结 ---------------------------------
|
||||
|
||||
GeometryMenu.prototype.addTorusKnot = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new TorusKnot()));
|
||||
};
|
||||
|
||||
// ---------------------- 茶壶 ----------------------------------
|
||||
|
||||
GeometryMenu.prototype.addTeaport = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Teapot()));
|
||||
};
|
||||
|
||||
// ---------------------- 酒杯 ----------------------------------
|
||||
|
||||
GeometryMenu.prototype.addLathe = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Lathe()));
|
||||
};
|
||||
|
||||
// ---------------------- 精灵 -----------------------------------
|
||||
|
||||
GeometryMenu.prototype.addSprite = function () {
|
||||
this.app.editor.execute(new AddObjectCommand(new Sprite()));
|
||||
};
|
||||
|
||||
// ---------------------- 文本 ----------------------------------
|
||||
|
||||
GeometryMenu.prototype.addText = function () {
|
||||
UI.prompt('请输入', null, '一些文字', (event, value) => {
|
||||
this.app.editor.execute(new AddObjectCommand(new Text(value)));
|
||||
});
|
||||
};
|
||||
|
||||
export default GeometryMenu;
|
||||
71
ShadowEditor.Core/src/editor/menubar/HelpMenu.js
Normal file
71
ShadowEditor.Core/src/editor/menubar/HelpMenu.js
Normal file
@ -0,0 +1,71 @@
|
||||
import { UI } from '../../third_party';
|
||||
|
||||
/**
|
||||
* 帮助菜单
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function HelpMenu(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
}
|
||||
|
||||
HelpMenu.prototype = Object.create(UI.Control.prototype);
|
||||
HelpMenu.prototype.constructor = HelpMenu;
|
||||
|
||||
HelpMenu.prototype.render = function () {
|
||||
var _this = this;
|
||||
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'menu',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'title',
|
||||
html: '帮助'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'options',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'option',
|
||||
html: '源码',
|
||||
onClick: () => {
|
||||
window.open('https://github.com/tengge1/ShadowEditor', '_blank');
|
||||
}
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'option',
|
||||
html: '示例',
|
||||
onClick: () => {
|
||||
window.open('https://github.com/tengge1/ShadowEditor-examples', '_blank');
|
||||
}
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'option',
|
||||
html: '文档',
|
||||
onClick: () => {
|
||||
window.open('https://tengge1.github.io/ShadowEditor/', '_blank');
|
||||
}
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'option',
|
||||
html: '关于',
|
||||
onClick: () => {
|
||||
UI.alert(
|
||||
`About`,
|
||||
`Name: ShadowEditor<br />
|
||||
Author: tengge<br />
|
||||
License: MIT<br />
|
||||
Thanks to three.js and everyone who helped us.`
|
||||
);
|
||||
}
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
container.render();
|
||||
};
|
||||
|
||||
export default HelpMenu;
|
||||
177
ShadowEditor.Core/src/editor/menubar/LightMenu.js
Normal file
177
ShadowEditor.Core/src/editor/menubar/LightMenu.js
Normal file
@ -0,0 +1,177 @@
|
||||
import { UI } from '../../third_party';
|
||||
import AddObjectCommand from '../../command/AddObjectCommand';
|
||||
|
||||
import PointLight from '../../object/light/PointLight';
|
||||
import HemisphereLight from '../../object/light/HemisphereLight';
|
||||
import RectAreaLight from '../../object/light/RectAreaLight';
|
||||
|
||||
/**
|
||||
* 光源菜单
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function LightMenu(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
}
|
||||
|
||||
LightMenu.prototype = Object.create(UI.Control.prototype);
|
||||
LightMenu.prototype.constructor = LightMenu;
|
||||
|
||||
LightMenu.prototype.render = function () {
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'menu',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
cls: 'title',
|
||||
html: '光源'
|
||||
}, {
|
||||
xtype: 'div',
|
||||
cls: 'options',
|
||||
children: [{
|
||||
xtype: 'div',
|
||||
html: '环境光',
|
||||
cls: 'option',
|
||||
onClick: this.addAmbientLight.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '平行光',
|
||||
cls: 'option',
|
||||
onClick: this.addDirectionalLight.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '点光源',
|
||||
cls: 'option',
|
||||
onClick: this.addPointLight.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '聚光灯',
|
||||
cls: 'option',
|
||||
onClick: this.addSpotLight.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '半球光',
|
||||
cls: 'option',
|
||||
onClick: this.addHemisphereLight.bind(this)
|
||||
}, {
|
||||
xtype: 'div',
|
||||
html: '矩形光',
|
||||
cls: 'option',
|
||||
onClick: this.addRectAreaLight.bind(this)
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
container.render();
|
||||
}
|
||||
|
||||
// ------------------------- 环境光 ------------------------------
|
||||
|
||||
LightMenu.prototype.addAmbientLight = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var color = 0xaaaaaa;
|
||||
|
||||
var light = new THREE.AmbientLight(color);
|
||||
light.name = '环境光';
|
||||
|
||||
editor.execute(new AddObjectCommand(light));
|
||||
};
|
||||
|
||||
// ------------------------- 平行光 ------------------------------
|
||||
|
||||
LightMenu.prototype.addDirectionalLight = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var color = 0xffffff;
|
||||
var intensity = 1;
|
||||
|
||||
var light = new THREE.DirectionalLight(color, intensity);
|
||||
light.name = '平行光';
|
||||
light.castShadow = true;
|
||||
light.shadow.mapSize.x = 2048;
|
||||
light.shadow.mapSize.y = 2048;
|
||||
light.shadow.camera.left = -100;
|
||||
light.shadow.camera.right = 100;
|
||||
light.shadow.camera.top = 100;
|
||||
light.shadow.camera.bottom = -100;
|
||||
light.position.set(5, 10, 7.5);
|
||||
|
||||
editor.execute(new AddObjectCommand(light));
|
||||
};
|
||||
|
||||
// ------------------------- 点光源 ------------------------------
|
||||
|
||||
LightMenu.prototype.addPointLight = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var color = 0xffffff;
|
||||
var intensity = 1;
|
||||
var distance = 0;
|
||||
|
||||
var light = new PointLight(color, intensity, distance);
|
||||
light.name = '点光源';
|
||||
light.position.y = 5;
|
||||
light.castShadow = true;
|
||||
|
||||
editor.execute(new AddObjectCommand(light));
|
||||
};
|
||||
|
||||
// ------------------------- 聚光灯 ------------------------------
|
||||
|
||||
LightMenu.prototype.addSpotLight = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var color = 0xffffff;
|
||||
var intensity = 1;
|
||||
var distance = 0;
|
||||
var angle = Math.PI * 0.1;
|
||||
var penumbra = 0;
|
||||
|
||||
var light = new THREE.SpotLight(color, intensity, distance, angle, penumbra);
|
||||
|
||||
light.name = '聚光灯';
|
||||
light.castShadow = true;
|
||||
|
||||
light.position.set(5, 10, 7.5);
|
||||
|
||||
editor.execute(new AddObjectCommand(light));
|
||||
};
|
||||
|
||||
// ------------------------- 半球光 ------------------------------
|
||||
|
||||
LightMenu.prototype.addHemisphereLight = function () {
|
||||
var editor = this.app.editor;
|
||||
var skyColor = 0x00aaff;
|
||||
var groundColor = 0xffaa00;
|
||||
var intensity = 1;
|
||||
|
||||
var light = new HemisphereLight(skyColor, groundColor, intensity);
|
||||
light.name = '半球光';
|
||||
|
||||
light.position.set(0, 10, 0);
|
||||
|
||||
editor.execute(new AddObjectCommand(light));
|
||||
};
|
||||
|
||||
// ------------------------- 矩形光 ------------------------------
|
||||
|
||||
LightMenu.prototype.addRectAreaLight = function () {
|
||||
var editor = this.app.editor;
|
||||
|
||||
var color = 0xffffff;
|
||||
var intensity = 1;
|
||||
var width = 20;
|
||||
var height = 10;
|
||||
|
||||
var light = new RectAreaLight(color, intensity, width, height);
|
||||
light.name = '矩形光';
|
||||
|
||||
light.position.set(0, 6, 0);
|
||||
|
||||
editor.execute(new AddObjectCommand(light));
|
||||
};
|
||||
|
||||
export default LightMenu;
|
||||
29
ShadowEditor.Core/src/editor/menubar/Logo.js
Normal file
29
ShadowEditor.Core/src/editor/menubar/Logo.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { UI } from '../../third_party';
|
||||
|
||||
/**
|
||||
* Logo标志
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function Logo(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
}
|
||||
|
||||
Logo.prototype = Object.create(UI.Control.prototype);
|
||||
Logo.prototype.constructor = Logo;
|
||||
|
||||
Logo.prototype.render = function () {
|
||||
var _this = this;
|
||||
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
parent: this.parent,
|
||||
cls: 'logo',
|
||||
html: '<i class="iconfont icon-shadow"></i>'
|
||||
});
|
||||
|
||||
container.render();
|
||||
}
|
||||
|
||||
export default Logo;
|
||||
62
ShadowEditor.Core/src/editor/menubar/Menubar.js
Normal file
62
ShadowEditor.Core/src/editor/menubar/Menubar.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { UI } from '../../third_party';
|
||||
import Logo from './Logo';
|
||||
import SceneMenu from './SceneMenu';
|
||||
import EditMenu from './EditMenu';
|
||||
import GeometryMenu from './GeometryMenu';
|
||||
import LightMenu from './LightMenu';
|
||||
import AssetMenu from './AssetMenu';
|
||||
import TerrainMenu from './TerrainMenu';
|
||||
import PhysicsMenu from './PhysicsMenu';
|
||||
import ComponentMenu from './ComponentMenu';
|
||||
import PlayMenu from './PlayMenu';
|
||||
import OptionsMenu from './OptionsMenu';
|
||||
import HelpMenu from './HelpMenu';
|
||||
import StatusMenu from './StatusMenu';
|
||||
|
||||
/**
|
||||
* 菜单栏
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author tengge / https://github.com/tengge1
|
||||
*/
|
||||
function Menubar(options) {
|
||||
UI.Control.call(this, options);
|
||||
this.app = options.app;
|
||||
};
|
||||
|
||||
Menubar.prototype = Object.create(UI.Control.prototype);
|
||||
Menubar.prototype.constructor = Menubar;
|
||||
|
||||
Menubar.prototype.render = function () {
|
||||
var params = { app: this.app };
|
||||
|
||||
var container = UI.create({
|
||||
xtype: 'div',
|
||||
id: 'menubar',
|
||||
cls: 'menubar',
|
||||
parent: this.parent,
|
||||
children: [
|
||||
// Logo
|
||||
new Logo(params),
|
||||
|
||||
// 左侧
|
||||
new SceneMenu(params),
|
||||
new EditMenu(params),
|
||||
new GeometryMenu(params),
|
||||
new LightMenu(params),
|
||||
new AssetMenu(params),
|
||||
new TerrainMenu(params),
|
||||
new PhysicsMenu(params),
|
||||
new ComponentMenu(params),
|
||||
new PlayMenu(params),
|
||||
new OptionsMenu(params),
|
||||
new HelpMenu(params),
|
||||
|
||||
// 右侧
|
||||
new StatusMenu(params)
|
||||
]
|
||||
});
|
||||
|
||||
container.render();
|
||||
};
|
||||
|
||||
export default Menubar;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user