UNPKG

elation-engine

Version:
1,220 lines (1,139 loc) 48.2 kB
elation.require([ "engine.external.three.FlyControls", "engine.external.three.OrbitControls", "engine.external.three.TransformControls", //"engine.external.highlight", "engine.things.manipulator", "engine.things.camera_admin", "ui.accordion", "ui.buttonbar", "ui.slider", "ui.combobox", "ui.select", "ui.tabs", //"ui.list", "ui.grid", "ui.panel", "ui.treeview", "ui.window", "ui.indicator", "ui.contextmenu", "ui.toolbox", "ui.label", ], function() { elation.requireCSS('engine.systems.admin'); elation.extend("engine.systems.admin", function(args) { elation.implement(this, elation.engine.systems.system); this.cameraactive = true; elation.template.add('engine.systems.admin.scenetree.thing', '<span class="engine_thing">{name}</span> ({type})'); elation.template.add('engine.systems.admin.inspector.property', '{?children}<span>{name}</span>{:else}<label for="engine_admin_inspector_properties_{fullname}">{name}</label><input id="engine_admin_inspector_properties_{fullname}" name="{fullname}" value="{value}">{/children}'); elation.template.add('engine.systems.admin.inspector.object', '<span class="engine_thing_object engine_thing_object_{type}">{object.name} ({type})</span>'); elation.template.add('engine.systems.admin.inspector.function', '<span class="engine_thing_function{?function.own} engine_thing_function_own{/function.own}" title="this.{function.name} = {function.content}">{function.name}</span>'); elation.template.add('engine.systems.admin.addthing', 'Add new <select name="type" data-elation-component="ui.select" data-elation-args.items="{thingtypes}"></select> named <input name="name"> <input type="submit" value="add">'); elation.template.add('engine.systems.admin.definething', '<input name="thingtype" placeholder="type name"> <textarea name="thingdef">function() {}</textarea> <input type="submit">'); elation.template.add('engine.systems.admin.assets.material', '<div data-elation-component="engine.systems.admin.assets.material" data-elation-args.materialname="{name}"><data class="elation-args"></data>{name}</div>'); elation.template.add('engine.systems.admin.assets.geometry', '<div data-elation-component="engine.systems.admin.assets.geometry" data-elation-args.geometryname="{name}"><data class="elation-args"></data>{name}</div>'); this.system_attach = function(ev) { console.log('INIT: admin'); this.world = this.engine.systems.world; this.hidden = true; //this.inspector = elation.engine.systems.admin.inspector('admin', elation.html.create({append: document.body}), {engine: this.engine}); /* this.engine.systems.controls.addCommands('admin', { //'add_thing': elation.bind(this, function(ev) { if (ev.value == 1) { this.scenetree.addItem(); }}), 'toggle_admin': elation.bind(this, function(ev) { if (ev.value == 1) { this.toggleAdmin(); } }), //'toggle_snap': elation.bind(this, function(ev) { this.toggleSnap(ev.value); }), }); this.engine.systems.controls.addBindings('admin', { //'keyboard_ctrl_a': 'add_thing', 'keyboard_f1': 'toggle_admin', //'keyboard_shift': 'toggle_snap', }); this.engine.systems.controls.activateContext('admin'); */ } this.createUI = function() { this.scenetree = elation.engine.systems.admin.scenetree(null, elation.html.create({append: document.body}), {world: this.world, admin: this}); this.assets = elation.engine.systems.admin.assets(null, elation.html.create({append: document.body}), {world: this.world, admin: this}); //this.worldcontrol = elation.engine.systems.admin.worldcontrol(null, elation.html.create({append: document.body}), {engine: this.engine}); } this.engine_frame = function(ev) { if (!this.hidden) { if (this.manipulator) { //this.manipulator.update(); } } } this.createCamera = function() { var render = this.engine.systems.render; if (render.views['main']) { var view = render.views['main']; this.clickpos = [0,0]; // Prevent object selection when dragging elation.events.add(window, 'mousedown', elation.bind(this, function(ev) { if (!this.hidden) { this.clicking = true; this.clickpos = [ev.clientX, ev.clientY]; this.cancelClick = false; } })); elation.events.add(window, 'mousemove', elation.bind(this, function(ev) { if (!this.hidden) { var diff = [this.clickpos[0] - ev.clientX, this.clickpos[1] - ev.clientY]; if (this.clicking && (diff[0] || diff[1])) { this.cancelClick = true; } } })); //elation.events.add(window, 'mouseup', elation.bind(this, function() { this.cancelClick = false; })); this.manipulator = new THREE.TransformControls(view.actualcamera, view.container); elation.events.add(this.manipulator, 'mouseDown', elation.bind(this, function() { if (this.manipulator.object && this.manipulator.object.userData.thing) { elation.events.fire({type: 'admin_edit_start', element: this, data: this.manipulator.object.userData.thing}); this.admincamera.admincontrols.enabled = false; }})); elation.events.add(this.manipulator, 'mouseUp', elation.bind(this, function() { if (this.manipulator.object && this.manipulator.object.userData.thing) { elation.events.fire({type: 'admin_edit_end', element: this, data: this.manipulator.object.userData.thing}); this.admincamera.admincontrols.enabled = true; }})); elation.events.add(this.manipulator, 'change', elation.bind(this, function(ev) { if (this.manipulator.object && this.manipulator.object.userData.thing) { elation.events.fire({type: 'admin_edit_change', element: this, data: this.manipulator.object.userData.thing}); this.manipulator.object.userData.thing.refresh(); render.setdirty(); } })); /* this.orbitcontrols = new THREE.OrbitControls(view.camera, view.container); this.orbitcontrols.rotateUp(-Math.PI/4); this.orbitcontrols.rotateLeft(-Math.PI/4); this.orbitcontrols.dollyOut(25); this.orbitcontrols.userPanSpeed = 10; this.orbitcontrols.keyPanSpeed = 100; this.orbitcontrols.noKeys = true; elation.events.add(this.orbitcontrols, 'change', elation.bind(this, this.controls_change)); this.flycontrols = new THREE.FlyControls(view.camera, view.container); this.flycontrols.movementSpeed = 10; this.flycontrols.rollSpeed = Math.PI/4; this.flycontrols.dragToLook = true; elation.events.add(this.flycontrols, 'change', elation.bind(this, this.controls_change)); this.toggleControls(); this.admincontrols.update(0); this.cameraactive = true; */ this.cameraactive = true; this.admincontrols = true; this.admincamera = this.world.spawn('camera_admin', 'admincam', { position: [0, 1, 1], view: this.engine.systems.render.views['main'], persist: false}); elation.events.add(render.views['main'].container, 'dragenter,dragover,drop', this); elation.events.add(null, 'engine_control_capture,engine_control_release', this); } } this.setCameraActive = function(active) { this.cameraactive = active; if (active) this.admincontrols.enabled = true; else this.admincontrols.enabled = false; } this.toggleAdmin = function(forceshow) { var render = this.engine.systems.render, view = render.views['main']; if (forceshow !== true && !this.hidden) { this.hidden = true; this.scenetree.hide(); if (this.worldcontrol) this.worldcontrol.hide(); //this.inspector.hide(); //view.toggleStats(false); if (this.lastactivething) { this.engine.client.setActiveThing(this.lastactivething); this.lastactivething.enable(); } if (this.manipulator && this.manipulator.parent) { this.manipulator.parent.remove(this.manipulator); } this.lastactivething = false; this.admincamera.disable(); } else { if (!this.created) { this.createUI(); this.created = true; this.createCamera(); } this.hidden = false; this.scenetree.show(); if (this.worldcontrol) this.worldcontrol.show(); //this.inspector.show(); //view.toggleStats(true); if (view.activething) { this.lastactivething = view.activething; //this.lastactivething = this.engine.client.player; if (this.lastactivething) { this.lastactivething.disable(); } } if (this.manipulator) { this.engine.systems.world.scene['world-3d'].add(this.manipulator); } setTimeout(elation.bind(this, function() { let thing = this.lastactivething; this.engine.client.setActiveThing(this.admincamera); this.admincamera.enable(); this.admincamera.properties.position.copy(thing.properties.position).add(new THREE.Vector3(0, 5, -5)); }), 0); } } this.toggleSnap = function(state) { this.manipulator.setSnap((state === 0 ? null : state)); } this.dragenter = function(ev) { this.dragkeystate = { altKey: ev.altKey, ctrlKey: ev.ctrlKey, shiftKey: ev.shiftKey, metaKey: ev.metaKey, }; } this.dragover = function(ev) { //ev.stopPropagation(); ev.preventDefault(); for (var k in this.dragkeystate) { this.dragkeystate[k] = ev[k]; } } this.drop = function(ev) { ev.stopPropagation(); ev.preventDefault(); // Handle drag/drop for textures if (ev.dataTransfer.files.length > 0) { var f = ev.dataTransfer.files[0]; if (f.type.indexOf('image') == 0) { var reader = new FileReader(); reader.onload = elation.bind(this, function(ev) { var tex = elation.engine.materials.getTexture(ev.target.result); if (this.scenetree.hoverthing) { if (this.dragkeystate.ctrlKey) { this.scenetree.hoverthing.value.set('textures.normalMap', tex, true); } else { this.scenetree.hoverthing.value.set('textures.map', tex, true); } } }); reader.readAsDataURL(f); } } else { var materialname = ev.dataTransfer.getData('-elation-material'); if (materialname) { var material = elation.engine.materials.get(materialname); if (material) { //this.scenetree.hoverthing.value.set('textures.map', tex, true); } } } } this.controls_change = function(ev) { this.engine.systems.render.setdirty(); } this.engine_control_capture = function(ev) { this.admincontrols.enabled = false; } this.engine_control_release = function(ev) { this.admincontrols.enabled = true; } }); elation.component.add("engine.systems.admin.scenetree", function() { this.init = function() { this.args.controls = true; this.args.width = '20em'; this.args.movable = true; this.args.minimize = false; this.args.maximize = false; this.args.close = false; this.args.resizable = true; elation.engine.systems.admin.scenetree.extendclass.init.call(this); this.world = this.args.world; this.admin = this.args.admin; this.engine = this.world.engine; elation.events.add(this.world, 'engine_thing_create,world_thing_add,world_thing_remove', this); this.attachEvents(this.world); this.create(); //this.cameratoggle = elation.ui.button(null, elation.html.create({tag: 'button', append: this.window.titlebar}), {label: '🔁'}); //elation.events.add(this.cameratoggle, "ui_button_click", elation.bind(this, function() { this.admin.toggleControls(); })); } this.attachEvents = function(thing) { elation.events.add(thing, 'thing_add', elation.bind(this, this.world_thing_add)); elation.events.add(thing, 'thing_remove', elation.bind(this, this.world_thing_remove)); for (var k in thing.children) { this.attachEvents(thing.children[k]); } } this.create = function() { this.addclass('engine_admin_scenetree'); var title = elation.html.create({tag: 'h2'}); var panel = elation.ui.panel({append: title, orientation: 'horizontal'}); var overrides = this.world.listLocalOverrides(); /* title.innerHTML = "Scene: "; var select = elation.ui.select(null, elation.html.create({tag: 'span', append: title}), {items: overrides, selected: this.world.rootname}, {ui_select_change: elation.bind(this, this.changeScene)}); */ var label = elation.ui.label({label: 'Scene:', append: panel}); /* this.sceneselect = elation.ui.select({ append: panel, items: overrides, selected: this.world.rootname, events: { ui_select_change: elation.bind(this, this.changeScene) } }); */ var createbutton = elation.ui.button({ append: panel, label: 'New', events: { click: elation.bind(this, this.createScene) } }); var translatebutton = elation.ui.button({ append: panel, label: '⬌', events: { click: elation.bind(this, function() { this.admin.manipulator.setMode('translate'); }) } }); var rotatebutton = elation.ui.button({ append: panel, label: '↺', events: { click: elation.bind(this, function() { this.admin.manipulator.setMode('rotate'); }) } }); var scalebutton = elation.ui.button({ append: panel, label: '⛞', events: { click: elation.bind(this, function() { this.admin.manipulator.setMode('scale'); }) } }); //this.window = elation.ui.window(null, elation.html.create({tag: 'div', classname: 'style_box engine_admin_scenetree', append: document.body}), {title: title, controls: false}); //this.window.setcontent(this.container); //this.setcontent(this.container); //elation.html.addclass(this.container, 'engine_admin_scenetree style_box'); this.settitle(title); this.treeview = elation.ui.treeview(null, elation.html.create({tag: 'div', classname: 'engine_admin_scenetree_list', append: this.content}), { items: this.world.children, attrs: { children: 'children', label: 'id', visible: 'properties.pickable', itemtemplate: 'engine.systems.admin.scenetree.thing' } }); this.toolbar = elation.ui.buttonbar(null, elation.html.create({tag: 'div', classname: 'engine_admin_scenetree_toolbar'}), { buttons: [ { label: '⊙', events: { click: elation.bind(this, this.centerItem) } }, { label: '+', events: { click: elation.bind(this, this.addItem) } }, { label: 'x', events: { click: elation.bind(this, this.removeItem) } } ] }); elation.events.add(this.treeview, 'ui_treeview_select,ui_treeview_hover', this); // TODO - object hover/selection should be made available when a specific selection mode is enabled /* elation.events.add(this, 'mouseover', elation.bind(this, function(ev) { if (ev.data && mouseevents.data.material) { var materials = (ev.data.material instanceof THREE.MeshFaceMaterial ? ev.data.material.materials : [ev.data.material]); for (var i = 0; i < materials.length; i++) { if (materials[i].emissive) { materials[i].emissive.setHex(0x333300); } } } })); elation.events.add(this, 'mouseout', elation.bind(this, function(ev) { if (ev.data && ev.data.material) { var materials = (ev.data.material instanceof THREE.MeshFaceMaterial ? ev.data.material.materials : [ev.data.material]); for (var i = 0; i < materials.length; i++) { if (materials[i].emissive) { materials[i].emissive.setHex(0x000000); } } } })); */ this.inspector = elation.engine.systems.admin.inspector('admin', elation.html.create({append: this.content, window: false}), {engine: this.engine}); } this.updateTreeview = function() { this.treeview.setItems(this.world.children); this.treeview.sort(); } this.ui_treeview_hover = function(ev) { this.hoverthing = ev.data; var li = ev.data.container; this.toolbar.reparent(li); } this.ui_treeview_select = function(ev) { var thing = ev.data.value; if (thing.properties.pickable && !this.hidden && !this.admin.cancelClick) { this.selectedthing = ev.data; elation.engine.systems.admin.inspector('admin').setThing(this.selectedthing); } } this.world_thing_add = function(ev) { this.attachEvents(ev.data.thing); // refresh tree view when new items are added if (ev.data.thing.properties.pickable) { this.treeview.setItems(this.world.children); } //elation.events.add(ev.data.thing, 'engine_thing_create,world_thing_add,world_thing_remove', this); //ev.target.persist(); } this.world_thing_remove = function(ev) { // refresh tree view if destroyed item was pickable if (ev.data.thing.properties.pickable) { this.treeview.setItems(this.world.children); } //ev.target.persist(); } this.centerItem = function() { var cdiff = this.admin.admincontrols.object.position.clone().sub(this.admin.admincontrols.center); this.admin.admincontrols.center.copy(this.hoverthing.value.properties.position); this.admin.admincontrols.object.position.copy(this.hoverthing.value.properties.position).add(cdiff); } this.addItem = function() { var addthing = elation.engine.systems.admin.addthing(null, elation.html.create(), {title: 'fuh'}); addthing.setParent(this.hoverthing.value); } this.defineThing = function(type) { var definething = elation.engine.systems.admin.definething(null, elation.html.create(), {title: 'fuh', type: type}); } this.removeItem = function() { var thing = this.hoverthing.value; thing.die(); this.treeview.setItems(this.world.children); } this.changeScene = function(ev) { if (ev.data) { this.world.loadLocal(ev.data); } ev.target.select.blur(); } this.createScene = function() { //var createscene = elation.engine.systems.admin.createscene(null, elation.html.create(), {title: 'fuh'}); var scenename = prompt('Enter scene name'); //this.world.createNew(); this.world.loadLocal(scenename); this.sceneselect.setSelected(scenename); } this.show = function() { elation.engine.systems.admin.scenetree.extendclass.show.call(this); elation.engine.systems.admin.inspector('admin').show(); this.treeview.enable(); this.inspector.show(); } this.hide = function() { elation.engine.systems.admin.scenetree.extendclass.hide.call(this); elation.engine.systems.admin.inspector('admin').hide(); this.treeview.disable(); this.inspector.hide(); } }, elation.ui.window); elation.component.add("engine.systems.admin.addthing", function() { this.init = function() { this.window = elation.ui.window(null, elation.html.create({classname: 'engine_admin_addthing style_box', append: document.body}), {title: 'Add Thing'}); this.window.setcontent(this.container); this.create(); } this.create = function() { var tplvars = { }; var thingtypes = []; for (var k in elation.engine.things) { thingtypes.push(k); } thingtypes.sort(); //thingtypes.push('_other_'); tplvars.thingtypes = thingtypes.join(';'); //var newhtml = elation.template.get('engine.systems.admin.addthing', tplvars); this.form = elation.html.create('form'); elation.events.add(this.form, 'submit', this); //this.form.innerHTML = newhtml; this.container.appendChild(this.form); var panel = elation.ui.panel({ append: this.form, orientation: 'horizontal' }); var thingcollection = elation.collection.custom({ items: function() { return Object.keys(elation.engine.things).sort(); } }); var thingtype = elation.ui.combobox({ append: panel, label: 'Add new', inputname: 'type', collection: thingcollection, events: { 'ui_input_accept': elation.bind(this, this.submit) } }); var thingname = elation.ui.input({ append: panel, label: 'named', inputname: 'name', events: { 'ui_input_accept': elation.bind(this, this.submit) } }); var submit = elation.ui.button({ append: panel, label: 'add', inputname: 'submit', }); elation.component.init(this.container); this.form.type.focus(); this.window.center(); elation.events.add(this.form.type, 'change', this); } this.setParent = function(newparent) { this.parentthing = newparent; } this.submit = function(ev) { ev.preventDefault(); var type = this.form.type.value; var name = this.form.name.value; if (type != '' && name != '' && this.parentthing) { var newthing = this.parentthing.spawn(type, name, {persist: true}); this.window.close(); // FIXME - should set the newly spawned item as active, since the next logical step is to start manipulating it... //elation.engine.systems.admin.inspector('admin').setThing(this.selectedthing); } } this.change = function(ev) { if (ev.target == this.form.type) { if (ev.target.value == '_other_') { var input = elation.html.create('input'); input.name = ev.target.name; input.value = ''; ev.target.parentNode.replaceChild(input, ev.target); input.focus(); } } } }); elation.component.add("engine.systems.admin.definething", function() { this.init = function() { this.type = this.args.type || 'light'; this.highlight = true; this.window = elation.ui.window(null, elation.html.create({classname: 'engine_admin_definething style_box', append: document.body}), {title: 'Define Thing'}); this.window.setcontent(this.container); this.create(); } this.create = function() { var tplvars = {}; /* var newhtml = elation.template.get('engine.systems.admin.definething', tplvars); this.form = elation.html.create('form'); elation.events.add(this.form, 'submit', this); this.form.innerHTML = newhtml; this.container.appendChild(this.form); elation.component.init(this.container); this.form.thingtype.focus(); */ this.accordions = {}; this.buttonbar = elation.ui.buttonbar(null, elation.html.create({append: this.container}), { buttons: [ { label: 'Save', events: { click: elation.bind(this, this.save) } }, { label: 'Revert', events: { click: elation.bind(this, this.revert) } }, ] }); this.accordions['own'] = elation.ui.accordion(null, elation.html.create({append: this.container, classname: 'engine_admin_definething_own'}), {}); this.accordions['inherit'] = elation.ui.accordion(null, elation.html.create({append: this.container, classname: 'engine_admin_definething_inherited'}), {}); var obj = new elation.engine.things[this.type].classdef(); var parent = new elation.engine.things[this.type].extendclass(); this.updateAccordion('own', obj); this.updateAccordion('inherit', parent); this.window.center(); elation.events.add(this.form, 'submit', this); } this.updateAccordion = function(name, obj) { var objtree = {}; for (var k in obj) { if (typeof obj[k] == 'function') { objtree[k] = { title: k, content: '<pre><code contenteditable=true spellcheck=false data-name="' + k + '">' + obj[k].toString() + '</code></pre>'}; } } this.accordions[name].setItems(objtree); if (this.highlight) { var codes = elation.find('code', this.accordions[name].container); for (var i = 0; i < codes.length; i++) { hljs.highlightBlock(codes[i]); //elation.events.add(codes[i], 'keydown,keyup', this); } } } this.keydown = function(ev) { if (this.updatetimer) { clearTimeout(this.updatetimer); } } this.keyup = function(ev) { if (ev.target.innerText != this.lasttext){ var target = ev.target; this.updatetimer = setTimeout(elation.bind(this, function() { this.lasttext = ev.target.innerText; var caret = this.getCaretPosition(ev.target); console.log('caretpos:', caret, target); var highlight = hljs.highlight('javascript', target.innerText); target.innerHTML = highlight.value; sel = document.getSelection(); var range = document.createRange(); range.setStart(target, caret); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); }), 250); } } this.getCaretPosition = function(element) { var range = window.getSelection().getRangeAt(0); var preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); var caretOffset = preCaretRange.toString().length; return caretOffset; } this.setParent = function(newparent) { this.parentthing = newparent; } this.submit = function(ev) { ev.preventDefault(); var type = this.form.thingtype.value; var content = this.form.thingdef.value; this.save(); if (this.parentthing) { this.window.close(); } } this.assemble = function() { var content = "function() {\n"; var codes = elation.find('code', this.accordions['own'].container); for (var i = 0; i < codes.length; i++) { content += " this." + codes[i].dataset.name + " = " + codes[i].innerText + "\n";; } content += "}"; return content; } this.save = function() { var oldthing = elation.engine.things[this.type]; elation.engine.things[this.type] = undefined; var cmd = "elation.component.add('engine.things." + this.type + "', " + this.assemble() + ", elation.engine.things.generic);"; console.log(cmd); eval(cmd); for (var k in oldthing.obj) { console.log('stupid thing', k, oldthing.obj[k]); var oldparent = oldthing.obj[k].parent; oldthing.obj[k].die(); var newthing = elation.engine.things[this.type](k, oldthing.obj[k].container, oldthing.obj[k].args); oldparent.add(newthing); } } }); elation.component.add("engine.systems.admin.inspector", function() { this.defaultcontainer = { tag: 'div', classname: 'engine_admin_inspector' }; this.init = function() { this.engine = this.args.engine; if (this.args.window) { this.window = elation.ui.window(null, elation.html.create({tag: 'div', classname: 'style_box engine_admin_inspector', append: document.body}), {title: 'Thing', controls: false}); this.window.setcontent(this.container); } else { this.label = elation.ui.labeldivider({append: this.container}); } //elation.html.addclass(this.container, 'engine_admin_inspector style_box'); elation.events.add(this.container, "mousewheel", function(ev) { ev.stopPropagation(); }); // FIXME - hack for mousewheel //this.label = elation.html.create({tag: 'h2', append: this.container}); this.tabcontents = { properties: elation.engine.systems.admin.inspector.properties({}), objects: elation.engine.systems.admin.inspector.objects({}), functions: elation.engine.systems.admin.inspector.functions({}) }; //this.manipulator = elation.engine.things.manipulator('manipulator', elation.html.create(), {properties: {persist: false, pickable: false, physical: false}, name: 'manipulator', type: 'manipulator', engine: this.engine}); /* setTimeout(elation.bind(this, function() { this.manipulator = this.engine.systems.world.spawn('manipulator', 'manipulator', {persist: false, pickable: false, physical: false}); }), 100); */ } this.setThing = function(thingwrapper) { if (this.thingwrapper) { elation.events.remove(this.thingwrapper.value, 'thing_change', this); elation.events.remove(this.thingwrapper.value, 'thing_recreate', this); } this.thingwrapper = thingwrapper; var thing = thingwrapper.value; elation.events.add(this.thingwrapper.value, 'thing_change', this); elation.events.add(this.thingwrapper.value, 'thing_recreate', this); if (!this.tabs) { this.createTabs(); } if (this.window) { this.window.settitle(thing.id + ' (' + thing.type + ')'); } else { this.label.setlabel(thing.id + ' (' + thing.type + ')'); } this.tabs.setActiveTab(this.activetab || "properties"); //this.engine.systems.physics.debug(thing); //this.selectedthing.value.spawn('manipulator', null, {persist: false}); //this.manipulator.reparent(thing); if (!this.manipulator) { this.manipulator = this.engine.systems.admin.manipulator; this.engine.systems.world.scene['world-3d'].add(this.manipulator); } this.manipulator.attach(thing.objects['3d']); thing.refresh(); /* console.log(this.engine.systems.render); if (!this.transformer && this.world.engine.systems.render.camera) { this.transformer = new THREE.TransformControls(this.world.engine.systems.render.camera); } this.transformer.attach(thing.objects['3d']); */ //this.properties.setThing(thingwrapper); //this.objects.setThing(thingwrapper); //this.manipulator.refresh(); } this.createTabs = function() { this.tabs = elation.ui.tabs(null, elation.html.create({append: this.container}), { items: [ { label: "Properties", name: "properties", }, { label: "Objects", name: "objects", }, { label: "Functions", name: "functions", }, ]}); this.contentarea = elation.html.create({tag: 'div', classname: 'engine_admin_inspector_contents', append: this.container}); elation.events.add(this.tabs, 'ui_tabs_change', this); } this.show = function() { if (this.thingwrapper) { var thing = this.thingwrapper.value; //this.manipulator.setThing(thing); } } this.hide = function() { //this.manipulator.setThing(null); } this.ui_tabs_change = function(ev) { var newtab = ev.data; if (this.tabcontents[newtab.name]) { this.activetab = newtab.name; this.contentarea.innerHTML = ''; this.tabcontents[newtab.name].reparent(this.contentarea); this.tabcontents[newtab.name].setThing(this.thingwrapper); } } this.thing_change = function(ev) { this.tabcontents.properties.updateThing(this.thingwrapper) } this.thing_recreate = function(ev) { this.tabcontents.properties.setThing(this.thingwrapper) if (this.manipulator) { this.manipulator.attach(this.thingwrapper.value.objects['3d']); } } }, elation.ui.base); elation.component.add("engine.systems.admin.inspector.properties", function() { this.defaultcontainer = { tag: 'div' }; this.init = function() { elation.html.addclass(this.container, 'engine_admin_inspector_properties ui_treeview'); this.propdiv = elation.html.create({tag: 'div', append: this.container}); } this.setThing = function(thingwrapper) { this.thingwrapper = thingwrapper; var thing = thingwrapper.value; this.propdiv.innerHTML = ''; var proptree = this.buildPropertyTree(thing.properties); // FIXME - should reuse the same treeview rather than creating a new one each time this.treeview = elation.ui.treeview(null, this.propdiv, { items: proptree, attrs: { children: 'children', itemtemplate: 'engine.systems.admin.inspector.property' } }); this.treeview.sort(); var propinputs = elation.find('input', this.propdiv); elation.events.add(propinputs, 'change', this); } this.updateThing = function() { var thing = this.thingwrapper.value; var proptree = this.buildPropertyTree(thing.properties); } this.buildPropertyTree = function(properties, prefix) { var root = {}; if (!prefix) prefix = ''; for (var k in properties) { root[k] = {name: k, fullname: prefix + k}; if (properties[k] instanceof THREE.Vector2) { root[k]['value'] = properties[k].x.toFixed(4) + ' ' + properties[k].y.toFixed(4); } else if (properties[k] instanceof THREE.Vector3 || properties[k] instanceof THREE.Euler) { root[k]['value'] = (properties[k].x * THREE.MathUtils.RAD2DEG).toFixed(2) + ' ' + (properties[k].y * THREE.MathUtils.RAD2DEG).toFixed(2) + ' ' + (properties[k].z * THREE.MathUtils.RAD2DEG).toFixed(2); } else if (properties[k] instanceof THREE.Vector4 || properties[k] instanceof THREE.Quaternion) { root[k]['value'] = properties[k].x.toFixed(4) + ' ' + properties[k].y.toFixed(4) + ' ' + properties[k].z.toFixed(4) + ' ' + properties[k].w.toFixed(4); } else if (properties[k] instanceof THREE.Texture) { root[k]['value'] = properties[k].sourceFile; } else if (properties[k] instanceof THREE.Mesh) { root[k]['value'] = '[mesh]'; } else if (properties[k] instanceof Object && !elation.utils.isArray(properties[k])) { //root[k]['children'] = this.buildPropertyTree(properties[k], prefix + k + "."); } else if (elation.utils.isArray(properties[k]) || elation.utils.isObject(properties[k])) { root[k]['value'] = JSON.stringify(properties[k]); } else { root[k]['value'] = properties[k]; } } return root; } this.change = function(ev) { var propname = ev.target.name; var thing = this.thingwrapper.value; thing.set(propname, ev.target.value, true); } }); elation.component.add("engine.systems.admin.inspector.objects", function() { this.defaultcontainer = { tag: 'div' }; this.types = ['Mesh', 'PointLight', 'SpotLight', 'AmbientLight', 'DirectionalLight', 'Light', 'ParticleSystem', 'PerspectiveCamera', 'OrthographicCamera', 'Camera', 'TextGeometry', 'BoxGeometry', 'SphereGeometry', 'PlaneGeometry', 'TorusGeometry', 'Geometry', 'BufferGeometry', 'MeshPhongMaterial', 'MeshBasicMaterial', 'MeshLambertMaterial', 'MeshFaceMaterial', 'ShaderMaterial', 'Material', 'Bone', 'Object3D']; this.init = function() { elation.html.addclass(this.container, 'engine_admin_inspector_objects ui_treeview'); } this.setThing = function(thingwrapper) { this.thing = thingwrapper.value; this.container.innerHTML = ''; var objtree = this.buildObjectTree(this.thing.objects); // FIXME - should reuse the same treeview rather than creating a new one each time this.treeview = elation.ui.treeview(null, this.container, { items: objtree, attrs: { children: 'children', itemtemplate: 'engine.systems.admin.inspector.object' } }); elation.events.add(this.treeview, 'ui_treeview_select', this); } this.buildObjectTree = function(objects, prefix) { var root = {}; if (!prefix) prefix = ''; for (var k in objects) { if (!elation.utils.isNull(objects[k])) { if (objects[k] instanceof elation.physics.rigidbody) { // TODO - show physics object to allow editing } else if (objects[k].userData && objects[k].userData.thing && objects[k].userData.thing != this.thing) { // TODO - This is a child thing - should we show them here? } else if (!(objects[k].userData && objects[k].userData.thing) || objects[k].userData.thing == this.thing) { root[k] = { name: k, fullname: prefix + k, type: this.getObjectType(objects[k]) }; root[k].object = objects[k]; root[k].children = {}; if (objects[k].children && objects[k].children.length > 0) { elation.utils.merge(this.buildObjectTree(objects[k].children, prefix + k + "_"), root[k].children); } switch (root[k].type) { case 'Mesh': var subobjects = { 'geometry': objects[k].geometry, 'material': objects[k].material }; elation.utils.merge(this.buildObjectTree(subobjects), root[k].children); break; case 'MeshFaceMaterial': var subobjects = objects[k].materials; elation.utils.merge(this.buildObjectTree(subobjects), root[k].children); break; } } } } return root; } this.getObjectType = function(obj) { var type = 'Object3D'; //console.log('object ' + objects[k].name + ': ', objects[k]); for (var i = 0; i < this.types.length; i++) { if (obj instanceof THREE[this.types[i]]) { type = this.types[i]; break; } } return type; } this.ui_treeview_select = function(ev) { var selected = ev.data.value; switch (selected.type) { case 'ShaderMaterial': elation.engine.materials.displayall(null, selected.object); break; } } }); elation.component.add("engine.systems.admin.inspector.functions", function() { this.defaultcontainer = { tag: 'div' }; this.init = function() { elation.html.addclass(this.container, 'engine_admin_inspector_functions'); } this.setThing = function(thingwrapper) { this.thing = thingwrapper.value; this.container.innerHTML = ''; //var objtree = this.buildObjectTree(this.thing.objects); //var generic = elation.engine.things.generic(null, elation.html.create()); var parent = elation.engine.things[this.thing.type].extendclass; var objtree = {}; for (var k in this.thing) { if (typeof this.thing[k] == 'function') { objtree[k] = { 'function': { name: k, content: this.thing[k].toString() } }; objtree[k].function.own = (typeof parent[k] != 'function' || parent[k].toString() != this.thing[k].toString()); console.log(" - " + k, objtree[k].own, parent[k], this.thing[k]); } } // FIXME - should reuse the same treeview rather than creating a new one each time this.treeview = elation.ui.treeview(null, this.container, { items: objtree, attrs: { children: 'children', itemtemplate: 'engine.systems.admin.inspector.function' } }); elation.events.add(this.treeview, 'ui_treeview_select', this); } }); elation.component.add("engine.systems.admin.worldcontrol", function() { this.init = function() { this.args.center = true; this.args.bottom = 20; this.args.controls = false; elation.engine.systems.admin.worldcontrol.extendclass.init.call(this); this.timescale = 1; this.playing = true; this.engine = this.args.engine; elation.html.addclass(this.container, 'engine_admin_worldcontrol'); this.playcontrols = elation.ui.buttonbar(null, elation.html.create({append: this.content}), { buttons: { 'reload': { label: '⟲', events: { click: elation.bind(this, this.reload) }, autoblur: true }, 'stepback': { label: '⇤', events: { click: elation.bind(this, this.stepback) }, autoblur: true }, 'play': { label: '||', events: { click: elation.bind(this, this.toggle) }, autoblur: true }, 'stepforward': { label: '⇥', events: { click: elation.bind(this, this.stepforward) }, autoblur: true }, 'save': { label: 'save', events: { click: elation.bind(this, this.save) }, autoblur: true }, } }); this.speedslider = elation.ui.slider({ append: this.content, value: this.timescale * 100, min: -500, max: 500, labelprefix: 'Speed: ', labelsuffix: '%', snap: 2.5, handle: { name: 'handle_one', value: this.timescale * 100, //bindvar: [this, this.timescale], } }); elation.events.add(this.speedslider, 'ui_slider_change', this); if (this.engine.systems.physics.timescale == 0) { this.pause(); } /* this.window = elation.ui.window(null, elation.html.create({tag: 'div', classname: 'style_box engine_admin_worldcontrol', append: document.body}), { title: 'World Controls', controls: false, bottom: 20, center: true, content: this.container }); */ this.settitle('World Controls'); this.refresh(); } this.save = function() { this.engine.systems.world.saveLocal(); } this.reload = function() { this.pause(); this.engine.systems.world.reload(); } this.play = function() { this.playcontrols.buttons.play.setLabel('||'); this.setTimescale(this.speedslider.value / 100, true); this.playing = true; } this.pause = function() { this.playcontrols.buttons.play.setLabel('▶'); this.setTimescale(0); this.playing = false; } this.toggle = function() { if (this.playing) { this.pause(); } else { this.play(); } } this.stepback = function() { this.setTimescale(this.speedslider.value/100, true); this.engine.systems.physics.step(-1/60); this.pause(); } this.stepforward = function() { this.setTimescale(this.speedslider.value/100, true); this.engine.systems.physics.step(1/60); this.pause(); } this.setTimescale = function(ts, force) { this.timescale = ts; if (this.playing || force || ts == 0) { this.engine.systems.physics.timescale = ts; } } this.ui_slider_change = function(ev) { if (ev.target == this.speedslider) { //if (!this.playing) this.play(); this.setTimescale(ev.data / 100); } } }, elation.ui.window); elation.component.add("engine.systems.admin.assets", function() { this.init = function() { this.world = this.args.world; this.admin = this.args.admin; this.engine = this.world.engine; this.tabcontents = {}; this.create(); } this.create = function() { this.tabs = elation.ui.tabs({ append: this.container, items: [ { label: "Materials", name: "materials", }, { label: "Geometries", name: "geometries", }, { label: "Behaviors", name: "behaviors", }, ]}); this.contentarea = elation.html.create({tag: 'div', classname: 'engine_admin_assets_contents', append: this.container}); elation.events.add(this.tabs, 'ui_tabs_change', this); this.tabs.setActiveTab("materials"); this.createMaterialList(); this.createGeometryList(); this.createBehaviorList(); this.setTabContent("materials"); this.window = elation.ui.window({ classname: 'style_box engine_admin_worldcontrol', append: document.body, title: 'Assets', controls: false, right: true, top: true, content: this.container }); // FIXME - hidden by default for now... this.window.hide(); } this.createMaterialList = function() { this.tabcontents['materials'] = elation.ui.grid({ classname: 'engine_admin_assets_materials', sortbydefault: 'name', attrs: { itemtemplate: 'engine.systems.admin.assets.material' } }); this.updateMaterialList(); elation.events.add(elation.engine.materials, 'engine_material_add', elation.bind(this, this.updateMaterialList)); } this.updateMaterialList = function() { var materials = []; var matlib = elation.engine.materials.materiallibrary; for (var k in matlib) { materials.push({name: k, material: matlib[k]}) } materials.sort(function(a, b) { return (a.name < b.name ? -1 : 1); }); this.tabcontents['materials'].clear(); this.tabcontents['materials'].setItems(materials); //this.tabcontents['materials'].sort('name'); } this.createGeometryList = function() { this.tabcontents['geometries'] = elation.ui.list({ classname: 'engine_admin_assets_geometries', sortbydefault: 'name', attrs: { itemtemplate: 'engine.systems.admin.assets.geometry' } }); this.updateGeometryList(); elation.events.add(elation.engine.geometries, 'engine_geometry_add', elation.bind(this, this.updateGeometryList)); } this.updateGeometryList = function() { var geometries = []; var geomlib = elation.engine.geometries.generators; for (var k in geomlib) { geometries.push({name: k, geometry: geomlib[k]}) } this.tabcontents['geometries'].clear(); this.tabcontents['geometries'].setItems(geometries); this.tabcontents['geometries'].sort('name'); } this.createBehaviorList = function() { this.tabcontents['behaviors'] = elation.ui.label({label: 'TODO - fill in behaviors'}); } this.setTabContent = function(name) { if (this.tabcontents[name]) { this.activetab = name; this.contentarea.innerHTML = ''; this.tabcontents[name].reparent(this.contentarea); } } this.ui_tabs_change = function(ev) { var newtab = ev.data; this.setTabContent(newtab.name); } }); elation.component.add("engine.systems.admin.assets.material", function() { this.init = function() { this.materialname = this.args.materialname; this.material = elation.engine.materials.get(this.materialname); if (this.material.map) { this.preview = this.material.map.image; } else if (this.material.color) { this.preview = elation.html.create(); this.preview.style.background = "#" + this.material.color.getHexString(); } if (this.preview) { if (this.material.opacity) { this.preview.style.opacity = this.material.opacity; } elation.html.addclass(this.preview, 'engine_admin_assets_material_preview'); this.container.appendChild(this.preview); } this.container.draggable = true; elation.events.add(this.container, 'mousedown,dragstart', this); } this.mousedown = function(ev) { console.log('dn', this.materialname); } this.dragstart = function(ev) { ev.dataTransfer.setData("-elation-material", this.materialname); return true; } }); });